[Libreoffice-commits] core.git: 2 commits - filter/source include/oox oox/Library_oox.mk oox/source sfx2/source

Tomaž Vajngerl quikee at gmail.com
Tue Aug 13 15:04:16 PDT 2013


 filter/source/config/fragments/filters/calc_MS_Excel_2007_XML.xcu |    2 
 include/oox/core/DocumentCrypt.hxx                                |   53 +
 include/oox/core/filterbase.hxx                                   |    5 
 include/oox/core/xmlfilterbase.hxx                                |    7 
 oox/Library_oox.mk                                                |    1 
 oox/source/core/DocumentCrypt.cxx                                 |  507 ++++++++++
 oox/source/core/filterbase.cxx                                    |   12 
 oox/source/core/xmlfilterbase.cxx                                 |   69 +
 sfx2/source/dialog/filedlghelper.cxx                              |   28 
 9 files changed, 672 insertions(+), 12 deletions(-)

New commits:
commit 651b1d08a19c2770ad6f9ed4a6889faeec73e6ba
Author: Tomaž Vajngerl <quikee at gmail.com>
Date:   Tue Aug 13 23:59:07 2013 +0200

    fdo#59524 Also write sheet length so MS Office will open it.
    
    Change-Id: I44407a15ca338bc7a24b5de8f8a60ad4e4f2b03d

diff --git a/oox/source/core/DocumentCrypt.cxx b/oox/source/core/DocumentCrypt.cxx
index 5e29ee8..b9056ab 100644
--- a/oox/source/core/DocumentCrypt.cxx
+++ b/oox/source/core/DocumentCrypt.cxx
@@ -27,6 +27,8 @@
 #include <osl/time.h>
 #include <rtl/random.h>
 
+#include <com/sun/star/io/XSeekable.hpp>
+
 namespace oox {
 namespace core {
 
@@ -408,6 +410,13 @@ AesEncoder::AesEncoder(Reference< XStream > xDocumentStream, oox::ole::OleStorag
 bool AesEncoder::encode()
 {
     Reference< XInputStream > xInputStream ( mxDocumentStream->getInputStream(), UNO_SET_THROW );
+    Reference< XSeekable > xSeekable( xInputStream, UNO_QUERY );
+
+    if (!xSeekable.is())
+        return false;
+
+    sal_uInt32 aLength = xSeekable->getLength();
+    printf("%d\n", aLength);
 
     if (!mrOleStorage.isStorage())
         return false;
@@ -461,7 +470,7 @@ bool AesEncoder::encode()
     sal_Int32 inLength;
     int outLength;
 
-    aEncryptedPackageStream.writeValue<sal_uInt32>( 0 ); // size
+    aEncryptedPackageStream.writeValue<sal_uInt32>( aLength ); // size
     aEncryptedPackageStream.writeValue<sal_uInt32>( 0 ); // size
 
     do
commit 3423db6f81dfcd3993178a70bde7c69a0c02c302
Author: Tomaž Vajngerl <quikee at gmail.com>
Date:   Tue Aug 13 23:25:24 2013 +0200

    fdo#59524 Encryption for MS Office 2007 Spreadsheet documents
    
    This enables saving of MS 2007 spreadsheet documents with a password.
    The encryption used is the same as used in Office 2007 (however
    different than in Office 2010 and 2013 which use "agile" encryption).
    
    Change-Id: I3539e811d95b6f9178246ab269d13bb385a48bd2

diff --git a/filter/source/config/fragments/filters/calc_MS_Excel_2007_XML.xcu b/filter/source/config/fragments/filters/calc_MS_Excel_2007_XML.xcu
index 7f1fddd..555ccb1 100644
--- a/filter/source/config/fragments/filters/calc_MS_Excel_2007_XML.xcu
+++ b/filter/source/config/fragments/filters/calc_MS_Excel_2007_XML.xcu
@@ -16,7 +16,7 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 -->
 <node oor:name="Calc MS Excel 2007 XML" oor:op="replace">
-    <prop oor:name="Flags"><value>IMPORT EXPORT ALIEN 3RDPARTYFILTER PREFERRED</value></prop>
+    <prop oor:name="Flags"><value>IMPORT EXPORT ALIEN 3RDPARTYFILTER PREFERRED ENCRYPTION PASSWORDTOMODIFY</value></prop>
     <prop oor:name="UIComponent"/>
     <prop oor:name="FilterService"><value>com.sun.star.comp.oox.xls.ExcelFilter</value></prop>
     <prop oor:name="UserData"/>
diff --git a/include/oox/core/DocumentCrypt.hxx b/include/oox/core/DocumentCrypt.hxx
new file mode 100644
index 0000000..3010236
--- /dev/null
+++ b/include/oox/core/DocumentCrypt.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef DOCUMENTCRYPTO_HXX
+#define DOCUMENTCRYPTO_HXX
+
+#include "oox/dllapi.h"
+
+#include "oox/ole/olestorage.hxx"
+#include <com/sun/star/io/XStream.hpp>
+
+namespace oox {
+namespace core {
+
+class OOX_DLLPUBLIC AesEncoder
+{
+private:
+    com::sun::star::uno::Reference< com::sun::star::io::XStream > mxDocumentStream;
+    oox::ole::OleStorage& mrOleStorage;
+    OUString maPassword;
+
+public:
+    AesEncoder(
+        com::sun::star::uno::Reference< com::sun::star::io::XStream > xDocumentStream,
+        oox::ole::OleStorage& rOleStorage,
+        OUString aPassword);
+
+    bool encode();
+};
+
+
+} // namespace core
+} // namespace oox
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/oox/core/filterbase.hxx b/include/oox/core/filterbase.hxx
index cfd9354..da86006 100644
--- a/include/oox/core/filterbase.hxx
+++ b/include/oox/core/filterbase.hxx
@@ -257,6 +257,11 @@ protected:
     virtual ::com::sun::star::uno::Reference< ::com::sun::star::io::XStream >
                         implGetOutputStream( ::comphelper::MediaDescriptor& rMediaDesc ) const;
 
+    virtual bool        implFinalizeExport( ::comphelper::MediaDescriptor& rMediaDescriptor );
+
+    ::com::sun::star::uno::Reference< ::com::sun::star::io::XStream >
+                        getMainDocumentStream( ) const;
+
 private:
     void                setMediaDescriptor(
                             const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rMediaDescSeq );
diff --git a/include/oox/core/xmlfilterbase.hxx b/include/oox/core/xmlfilterbase.hxx
index 5bddea4..6d61eb1 100644
--- a/include/oox/core/xmlfilterbase.hxx
+++ b/include/oox/core/xmlfilterbase.hxx
@@ -233,7 +233,12 @@ public:
 
 protected:
     virtual ::com::sun::star::uno::Reference< ::com::sun::star::io::XInputStream >
-                        implGetInputStream( ::comphelper::MediaDescriptor& rMediaDesc ) const;
+        implGetInputStream( ::comphelper::MediaDescriptor& rMediaDesc ) const;
+
+    virtual ::com::sun::star::uno::Reference< ::com::sun::star::io::XStream >
+        implGetOutputStream( ::comphelper::MediaDescriptor& rMediaDesc ) const;
+
+    virtual bool implFinalizeExport(  ::comphelper::MediaDescriptor& rMediaDescriptor );
 
 private:
     virtual StorageRef  implCreateStorage(
diff --git a/oox/Library_oox.mk b/oox/Library_oox.mk
index 45f4b9c..f847736 100644
--- a/oox/Library_oox.mk
+++ b/oox/Library_oox.mk
@@ -78,6 +78,7 @@ $(eval $(call gb_Library_add_exception_objects,oox,\
     oox/source/core/binarycodec \
     oox/source/core/contexthandler2 \
     oox/source/core/contexthandler \
+    oox/source/core/DocumentCrypt \
     oox/source/core/fastparser \
     oox/source/core/fasttokenhandler \
     oox/source/core/filterbase \
diff --git a/oox/source/core/DocumentCrypt.cxx b/oox/source/core/DocumentCrypt.cxx
new file mode 100644
index 0000000..5e29ee8
--- /dev/null
+++ b/oox/source/core/DocumentCrypt.cxx
@@ -0,0 +1,498 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include "oox/core/DocumentCrypt.hxx"
+#include <config_oox.h>
+
+#include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/mediadescriptor.hxx>
+#if USE_TLS_OPENSSL
+#include <openssl/evp.h>
+#endif // USE_TLS_OPENSSL
+#if USE_TLS_NSS
+#include <nss.h>
+#include <pk11pub.h>
+#endif // USE_TLS_NSS
+#include <rtl/digest.h>
+#include "oox/helper/binaryinputstream.hxx"
+#include "oox/helper/binaryoutputstream.hxx"
+
+#include <osl/time.h>
+#include <rtl/random.h>
+
+namespace oox {
+namespace core {
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+using ::comphelper::MediaDescriptor;
+using ::comphelper::SequenceAsHashMap;
+
+/* =========================================================================== */
+/*  Kudos to Caolan McNamara who provided the core decryption implementations. */
+/* =========================================================================== */
+
+namespace {
+
+const sal_uInt32 ENCRYPTINFO_CRYPTOAPI      = 0x00000004;
+const sal_uInt32 ENCRYPTINFO_DOCPROPS       = 0x00000008;
+const sal_uInt32 ENCRYPTINFO_EXTERNAL       = 0x00000010;
+const sal_uInt32 ENCRYPTINFO_AES            = 0x00000020;
+
+const sal_uInt32 ENCRYPT_ALGO_AES128        = 0x0000660E;
+const sal_uInt32 ENCRYPT_ALGO_AES192        = 0x0000660F;
+const sal_uInt32 ENCRYPT_ALGO_AES256        = 0x00006610;
+const sal_uInt32 ENCRYPT_ALGO_RC4           = 0x00006801;
+
+const sal_uInt32 ENCRYPT_HASH_SHA1          = 0x00008004;
+
+const sal_uInt32 ENCRYPT_KEY_SIZE_AES_128   = 0x00000080;
+const sal_uInt32 ENCRYPT_KEY_SIZE_AES_192   = 0x000000C0;
+const sal_uInt32 ENCRYPT_KEY_SIZE_AES_256   = 0x00000100;
+
+const sal_uInt32 ENCRYPT_PROVIDER_TYPE_AES  = 0x00000018;
+const sal_uInt32 ENCRYPT_PROVIDER_TYPE_RC4  = 0x00000001;
+
+struct PackageEncryptionInfo
+{
+    sal_uInt8           mpnSalt[ 16 ];
+    sal_uInt8           mpnEncrVerifier[ 16 ];
+    sal_uInt8           mpnEncrVerifierHash[ 32 ];
+    sal_uInt32          mnFlags;
+    sal_uInt32          mnAlgorithmId;
+    sal_uInt32          mnAlgorithmIdHash;
+    sal_uInt32          mnKeySize;
+    sal_uInt32          mnSaltSize;
+    sal_uInt32          mnVerifierHashSize;
+};
+
+void lclRandomGenerateValues( sal_Int32 nLength, sal_uInt8* aArray )
+{
+    TimeValue aTime;
+    osl_getSystemTime( &aTime );
+    rtlRandomPool aRandomPool = rtl_random_createPool ();
+    rtl_random_addBytes ( aRandomPool, &aTime, 8 );
+    rtl_random_getBytes ( aRandomPool, aArray, nLength );
+    rtl_random_destroyPool ( aRandomPool );
+}
+
+bool lclReadEncryptionInfo( PackageEncryptionInfo& rEncrInfo, BinaryInputStream& rStrm )
+{
+    rStrm.skip( 4 );
+    rStrm >> rEncrInfo.mnFlags;
+    if( getFlag( rEncrInfo.mnFlags, ENCRYPTINFO_EXTERNAL ) )
+        return false;
+
+    sal_uInt32 nHeaderSize, nRepeatedFlags;
+    rStrm >> nHeaderSize >> nRepeatedFlags;
+    if( (nHeaderSize < 20) || (nRepeatedFlags != rEncrInfo.mnFlags) )
+        return false;
+
+    rStrm.skip( 4 );
+    rStrm >> rEncrInfo.mnAlgorithmId >> rEncrInfo.mnAlgorithmIdHash >> rEncrInfo.mnKeySize;
+    rStrm.skip( nHeaderSize - 20 );
+    rStrm >> rEncrInfo.mnSaltSize;
+    if( rEncrInfo.mnSaltSize != 16 )
+        return false;
+
+    rStrm.readMemory( rEncrInfo.mpnSalt, 16 );
+    rStrm.readMemory( rEncrInfo.mpnEncrVerifier, 16 );
+    rStrm >> rEncrInfo.mnVerifierHashSize;
+    rStrm.readMemory( rEncrInfo.mpnEncrVerifierHash, 32 );
+    return !rStrm.isEof();
+}
+
+struct EncryptionStandardHeader {
+    sal_uInt32 flags;
+    sal_uInt32 sizeExtra;
+    sal_uInt32 algId;         // if flag AES && CRYPTOAPI this defaults to 128-bit AES
+    sal_uInt32 algIdHash;     // 0 - determined by flags - defaults to SHA-1 if not external
+    sal_uInt32 keySize;       // 0 - determined by flags, 128, 192, 256 for AES
+    sal_uInt32 providedType;
+    sal_uInt32 reserved1;
+    sal_uInt32 reserved2;
+};
+
+struct EncryptionVerifierAES {
+    sal_uInt32 saltSize; // must be 0x00000010
+    sal_uInt8  salt[16]; //
+    sal_uInt8  encryptedVerifier[16];     // randomly generated verifier value
+    sal_uInt32 verifierHashSize;
+    sal_uInt8  encryptedVerifierHash[32];
+};
+
+bool lclWriteEncryptionInfo( PackageEncryptionInfo& rEncrInfo, BinaryOutputStream& rStream )
+{
+    const sal_uInt16 versionInfoMajor = 0x003;
+    const sal_uInt16 versionInfoMinor = 0x002;
+
+    rStream.writeValue(versionInfoMajor);
+    rStream.writeValue(versionInfoMinor);
+
+    const OUString cspName = "Microsoft Enhanced RSA and AES Cryptographic Provider";
+    sal_Int32      cspNameSize = (cspName.getLength() * 2) + 2;
+
+    EncryptionStandardHeader encryptionHeader;
+    sal_Int32 encryptionHeaderSize = static_cast<sal_Int32>(sizeof(EncryptionStandardHeader));
+    memset(&encryptionHeader, 0, encryptionHeaderSize);
+
+    EncryptionVerifierAES encryptionVerifier;
+    sal_Int32 encryptionVerifierSize = static_cast<sal_Int32>(sizeof(EncryptionVerifierAES));
+    memset(&encryptionVerifier, 0, encryptionVerifierSize);
+
+    rStream << rEncrInfo.mnFlags;
+
+    sal_uInt32 headerSize = encryptionHeaderSize + cspNameSize;
+    rStream << headerSize;
+
+    encryptionHeader.flags = rEncrInfo.mnFlags;
+    encryptionHeader.algId = rEncrInfo.mnAlgorithmId;
+    encryptionHeader.algIdHash = rEncrInfo.mnAlgorithmIdHash;
+    encryptionHeader.keySize = rEncrInfo.mnKeySize;
+    encryptionHeader.providedType = ENCRYPT_PROVIDER_TYPE_AES;
+
+    rStream.writeMemory(&encryptionHeader, encryptionHeaderSize);
+    rStream.writeUnicodeArray(cspName);
+    rStream.writeValue<sal_uInt16>(0);
+
+    if (rEncrInfo.mnSaltSize != 16)
+        return false;
+
+    encryptionVerifier.saltSize = rEncrInfo.mnSaltSize;
+
+    memcpy(&encryptionVerifier.salt, rEncrInfo.mpnSalt, 16);
+
+    memcpy(&encryptionVerifier.encryptedVerifier, rEncrInfo.mpnEncrVerifier, 16);
+
+    encryptionVerifier.verifierHashSize = rEncrInfo.mnVerifierHashSize;
+
+    memcpy(encryptionVerifier.encryptedVerifierHash, rEncrInfo.mpnEncrVerifierHash, 32);
+
+    rStream.writeMemory(&encryptionVerifier, encryptionVerifierSize);
+
+    return true;
+}
+
+void lclDeriveKey( const sal_uInt8* pnHash, sal_uInt32 nHashLen, sal_uInt8* pnKeyDerived, sal_uInt32 nRequiredKeyLen )
+{
+    sal_uInt8 pnBuffer[ 64 ];
+    memset( pnBuffer, 0x36, sizeof( pnBuffer ) );
+    for( sal_uInt32 i = 0; i < nHashLen; ++i )
+        pnBuffer[ i ] ^= pnHash[ i ];
+
+    rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+    rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) );
+    sal_uInt8 pnX1[ RTL_DIGEST_LENGTH_SHA1 ];
+    rtl_digest_get( aDigest, pnX1, RTL_DIGEST_LENGTH_SHA1 );
+    rtl_digest_destroy( aDigest );
+
+    memset( pnBuffer, 0x5C, sizeof( pnBuffer ) );
+    for( sal_uInt32 i = 0; i < nHashLen; ++i )
+        pnBuffer[ i ] ^= pnHash[ i ];
+
+    aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+    rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) );
+    sal_uInt8 pnX2[ RTL_DIGEST_LENGTH_SHA1 ];
+    rtl_digest_get( aDigest, pnX2, RTL_DIGEST_LENGTH_SHA1 );
+    rtl_digest_destroy( aDigest );
+
+    if( nRequiredKeyLen > RTL_DIGEST_LENGTH_SHA1 )
+    {
+        memcpy( pnKeyDerived + RTL_DIGEST_LENGTH_SHA1, pnX2, nRequiredKeyLen - RTL_DIGEST_LENGTH_SHA1 );
+        nRequiredKeyLen = RTL_DIGEST_LENGTH_SHA1;
+    }
+    memcpy( pnKeyDerived, pnX1, nRequiredKeyLen );
+}
+
+bool lclGenerateVerifier(PackageEncryptionInfo& rEncryptionInfo, const sal_uInt8* pKey, sal_uInt32 nKeySize)
+{
+    bool bResult = false;
+
+    if (nKeySize != 16)
+        return bResult;
+
+    sal_uInt8 aVerifier[16];
+    sal_Int32 aVerifierSize = sizeof(aVerifier);
+    memset( aVerifier, 0, aVerifierSize );
+    lclRandomGenerateValues(aVerifierSize, aVerifier);
+
+#if USE_TLS_OPENSSL
+    {
+        EVP_CIPHER_CTX aContext;
+        EVP_CIPHER_CTX_init( &aContext );
+        EVP_EncryptInit_ex( &aContext, EVP_aes_128_ecb(), NULL, pKey, 0 );
+        EVP_CIPHER_CTX_set_padding( &aContext, 0 );
+        int aEncryptedVerifierSize = 0;
+        EVP_EncryptUpdate( &aContext, rEncryptionInfo.mpnEncrVerifier, &aEncryptedVerifierSize, aVerifier, aVerifierSize );
+        EVP_CIPHER_CTX_cleanup( &aContext );
+    }
+
+#endif // USE_TLS_OPENSSL
+
+    sal_uInt8 pSha1Hash[ 32 ];
+    memset(pSha1Hash, 0, 32);
+    rEncryptionInfo.mnVerifierHashSize = RTL_DIGEST_LENGTH_SHA1;
+
+    rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+    rtl_digest_update( aDigest, aVerifier, aVerifierSize );
+    rtl_digest_get( aDigest, pSha1Hash, RTL_DIGEST_LENGTH_SHA1 );
+    rtl_digest_destroy( aDigest );
+
+#if USE_TLS_OPENSSL
+    {
+        memset(rEncryptionInfo.mpnEncrVerifierHash, 0, rEncryptionInfo.mnVerifierHashSize);
+        int written = 0;
+
+        EVP_CIPHER_CTX aContext;
+        EVP_CIPHER_CTX_init( &aContext );
+        EVP_EncryptInit_ex( &aContext, EVP_aes_128_ecb(), NULL, pKey, 0 );
+        EVP_CIPHER_CTX_set_padding( &aContext, 0 );
+        EVP_EncryptUpdate( &aContext, rEncryptionInfo.mpnEncrVerifierHash, &written, pSha1Hash, 32 );
+        EVP_CIPHER_CTX_cleanup( &aContext );
+    }
+
+#endif // USE_TLS_OPENSSL
+
+    bResult = true;
+
+    return bResult;
+}
+
+
+bool lclCheckEncryptionData( const sal_uInt8* pnKey, sal_uInt32 nKeySize, const sal_uInt8* pnVerifier, sal_uInt32 nVerifierSize, const sal_uInt8* pnVerifierHash, sal_uInt32 nVerifierHashSize )
+{
+    bool bResult = false;
+
+    // the only currently supported algorithm needs key size 128
+    if ( nKeySize == 16 && nVerifierSize == 16 )
+    {
+        // check password
+#if USE_TLS_OPENSSL
+        EVP_CIPHER_CTX aes_ctx;
+        EVP_CIPHER_CTX_init( &aes_ctx );
+        EVP_DecryptInit_ex( &aes_ctx, EVP_aes_128_ecb(), 0, pnKey, 0 );
+        EVP_CIPHER_CTX_set_padding( &aes_ctx, 0 );
+        int nOutLen = 0;
+        sal_uInt8 pnTmpVerifier[ 16 ];
+        (void) memset( pnTmpVerifier, 0, sizeof(pnTmpVerifier) );
+
+        /*int*/ EVP_DecryptUpdate( &aes_ctx, pnTmpVerifier, &nOutLen, pnVerifier, nVerifierSize );
+        EVP_CIPHER_CTX_cleanup( &aes_ctx );
+
+        EVP_CIPHER_CTX_init( &aes_ctx );
+        EVP_DecryptInit_ex( &aes_ctx, EVP_aes_128_ecb(), 0, pnKey, 0 );
+        EVP_CIPHER_CTX_set_padding( &aes_ctx, 0 );
+        sal_uInt8* pnTmpVerifierHash = new sal_uInt8[nVerifierHashSize];
+        (void) memset( pnTmpVerifierHash, 0, nVerifierHashSize );
+
+        /*int*/ EVP_DecryptUpdate( &aes_ctx, pnTmpVerifierHash, &nOutLen, pnVerifierHash, nVerifierHashSize );
+        EVP_CIPHER_CTX_cleanup( &aes_ctx );
+#endif // USE_TLS_OPENSSL
+
+#if USE_TLS_NSS
+        PK11SlotInfo *aSlot( PK11_GetBestSlot( CKM_AES_ECB, NULL ) );
+        sal_uInt8 *key( new sal_uInt8[ nKeySize ] );
+        (void) memcpy( key, pnKey, nKeySize * sizeof(sal_uInt8) );
+
+        SECItem keyItem;
+        keyItem.type = siBuffer;
+        keyItem.data = key;
+        keyItem.len  = nKeySize;
+
+        PK11SymKey *symKey( PK11_ImportSymKey( aSlot, CKM_AES_ECB, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, NULL ) );
+        SECItem *secParam( PK11_ParamFromIV( CKM_AES_ECB, NULL ) );
+        PK11Context *encContext( PK11_CreateContextBySymKey( CKM_AES_ECB, CKA_DECRYPT, symKey, secParam ) );
+
+        int nOutLen(0);
+        sal_uInt8 pnTmpVerifier[ 16 ];
+        (void) memset( pnTmpVerifier, 0, sizeof(pnTmpVerifier) );
+
+        PK11_CipherOp( encContext, pnTmpVerifier, &nOutLen, sizeof(pnTmpVerifier), const_cast<sal_uInt8*>(pnVerifier), nVerifierSize );
+
+        sal_uInt8* pnTmpVerifierHash = new sal_uInt8[nVerifierHashSize];
+        (void) memset( pnTmpVerifierHash, 0, nVerifierHashSize );
+        PK11_CipherOp( encContext, pnTmpVerifierHash, &nOutLen, nVerifierHashSize, const_cast<sal_uInt8*>(pnVerifierHash), nVerifierHashSize );
+
+        PK11_DestroyContext( encContext, PR_TRUE );
+        PK11_FreeSymKey( symKey );
+        SECITEM_FreeItem( secParam, PR_TRUE );
+        delete[] key;
+#endif // USE_TLS_NSS
+
+        rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+        rtl_digest_update( aDigest, pnTmpVerifier, sizeof( pnTmpVerifier ) );
+        sal_uInt8 pnSha1Hash[ RTL_DIGEST_LENGTH_SHA1 ];
+        rtl_digest_get( aDigest, pnSha1Hash, RTL_DIGEST_LENGTH_SHA1 );
+        rtl_digest_destroy( aDigest );
+
+        bResult = ( memcmp( pnSha1Hash, pnTmpVerifierHash, RTL_DIGEST_LENGTH_SHA1 ) == 0 );
+    }
+
+    return bResult;
+}
+
+// ----------------------------------------------------------------------------
+
+Sequence< NamedValue > lclGenerateEncryptionKey( const PackageEncryptionInfo& rEncrInfo, const OUString& rPassword, sal_uInt8* pnKey, sal_uInt32 nRequiredKeyLen )
+{
+    size_t nBufferSize = rEncrInfo.mnSaltSize + 2 * rPassword.getLength();
+    sal_uInt8* pnBuffer = new sal_uInt8[ nBufferSize ];
+    memcpy( pnBuffer, rEncrInfo.mpnSalt, rEncrInfo.mnSaltSize );
+
+    sal_uInt8* pnPasswordLoc = pnBuffer + rEncrInfo.mnSaltSize;
+    const sal_Unicode* pStr = rPassword.getStr();
+    for( sal_Int32 i = 0, nLen = rPassword.getLength(); i < nLen; ++i, ++pStr, pnPasswordLoc += 2 )
+        ByteOrderConverter::writeLittleEndian( pnPasswordLoc, static_cast< sal_uInt16 >( *pStr ) );
+
+    rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+    rtl_digest_update( aDigest, pnBuffer, nBufferSize );
+    delete[] pnBuffer;
+
+    size_t nHashSize = RTL_DIGEST_LENGTH_SHA1 + 4;
+    sal_uInt8* pnHash = new sal_uInt8[ nHashSize ];
+    rtl_digest_get( aDigest, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 );
+    rtl_digest_destroy( aDigest );
+
+    for( sal_uInt32 i = 0; i < 50000; ++i )
+    {
+        ByteOrderConverter::writeLittleEndian( pnHash, i );
+        aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+        rtl_digest_update( aDigest, pnHash, nHashSize );
+        rtl_digest_get( aDigest, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 );
+        rtl_digest_destroy( aDigest );
+    }
+
+    memmove( pnHash, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 );
+    memset( pnHash + RTL_DIGEST_LENGTH_SHA1, 0, 4 );
+    aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
+    rtl_digest_update( aDigest, pnHash, nHashSize );
+    rtl_digest_get( aDigest, pnHash, RTL_DIGEST_LENGTH_SHA1 );
+    rtl_digest_destroy( aDigest );
+
+    lclDeriveKey( pnHash, RTL_DIGEST_LENGTH_SHA1, pnKey, nRequiredKeyLen );
+    delete[] pnHash;
+
+    Sequence< NamedValue > aResult;
+    if( lclCheckEncryptionData( pnKey, nRequiredKeyLen, rEncrInfo.mpnEncrVerifier, sizeof( rEncrInfo.mpnEncrVerifier ), rEncrInfo.mpnEncrVerifierHash, sizeof( rEncrInfo.mpnEncrVerifierHash ) ) )
+    {
+        SequenceAsHashMap aEncryptionData;
+        aEncryptionData[ "AES128EncryptionKey" ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( pnKey ), nRequiredKeyLen );
+        aEncryptionData[ "AES128EncryptionSalt" ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( rEncrInfo.mpnSalt ), rEncrInfo.mnSaltSize );
+        aEncryptionData[ "AES128EncryptionVerifier" ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( rEncrInfo.mpnEncrVerifier ), sizeof( rEncrInfo.mpnEncrVerifier ) );
+        aEncryptionData[ "AES128EncryptionVerifierHash" ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( rEncrInfo.mpnEncrVerifierHash ), sizeof( rEncrInfo.mpnEncrVerifierHash ) );
+        aResult = aEncryptionData.getAsConstNamedValueList();
+    }
+
+    return aResult;
+}
+
+} // namespace
+
+AesEncoder::AesEncoder(Reference< XStream > xDocumentStream, oox::ole::OleStorage& rOleStorage, OUString aPassword) :
+    mxDocumentStream(xDocumentStream),
+    mrOleStorage(rOleStorage),
+    maPassword(aPassword)
+{
+}
+
+bool AesEncoder::encode()
+{
+    Reference< XInputStream > xInputStream ( mxDocumentStream->getInputStream(), UNO_SET_THROW );
+
+    if (!mrOleStorage.isStorage())
+        return false;
+
+    Reference< XOutputStream > xEncryptionInfo( mrOleStorage.openOutputStream( "EncryptionInfo" ), UNO_SET_THROW );
+
+    PackageEncryptionInfo rEncrInfo;
+    rEncrInfo.mnFlags = ENCRYPTINFO_AES | ENCRYPTINFO_CRYPTOAPI;
+    rEncrInfo.mnAlgorithmId = ENCRYPT_ALGO_AES128;
+    rEncrInfo.mnAlgorithmIdHash = ENCRYPT_HASH_SHA1;
+    rEncrInfo.mnKeySize = ENCRYPT_KEY_SIZE_AES_128;
+
+    rEncrInfo.mnSaltSize = 16;
+
+    lclRandomGenerateValues( rEncrInfo.mnSaltSize, rEncrInfo.mpnSalt );
+
+    sal_Int32 keyLength = rEncrInfo.mnKeySize / 8;
+    sal_uInt8 key[16];
+    memset(key, 0, keyLength);
+
+    lclGenerateEncryptionKey(rEncrInfo, maPassword, key, keyLength);
+
+    lclGenerateVerifier(rEncrInfo, key, keyLength);
+
+    bool aResult = lclCheckEncryptionData(key, keyLength, rEncrInfo.mpnEncrVerifier, 16, rEncrInfo.mpnEncrVerifierHash, 32);
+    if (!aResult)
+        return false;
+
+    BinaryXOutputStream aEncryptionInfoBinaryOutputStream( xEncryptionInfo, false );
+    lclWriteEncryptionInfo( rEncrInfo, aEncryptionInfoBinaryOutputStream );
+    aEncryptionInfoBinaryOutputStream.close();
+
+    xEncryptionInfo->flush();
+    xEncryptionInfo->closeOutput();
+
+    Reference< XOutputStream > xEncryptedPackage( mrOleStorage.openOutputStream( "EncryptedPackage" ), UNO_SET_THROW );
+    BinaryXOutputStream aEncryptedPackageStream( xEncryptedPackage, false );
+
+    BinaryXInputStream aDocumentInputStream( xInputStream, false );
+    aDocumentInputStream.seekToStart();
+
+#if USE_TLS_OPENSSL
+    EVP_CIPHER_CTX aContext;
+    EVP_CIPHER_CTX_init( &aContext );
+    EVP_EncryptInit_ex( &aContext, EVP_aes_128_ecb(), NULL, key, 0 );
+    EVP_CIPHER_CTX_set_padding( &aContext, 0 );
+
+    sal_uInt8 inBuffer[ 1024 ];
+    sal_uInt8 outBuffer[ 1024 ];
+
+    sal_Int32 inLength;
+    int outLength;
+
+    aEncryptedPackageStream.writeValue<sal_uInt32>( 0 ); // size
+    aEncryptedPackageStream.writeValue<sal_uInt32>( 0 ); // size
+
+    do
+    {
+        inLength = aDocumentInputStream.readMemory( inBuffer, sizeof( inBuffer ) );
+        if (inLength > 0)
+        {
+            inLength = inLength % 16 == 0 ? inLength : ((inLength/16)*16)+16;
+            EVP_EncryptUpdate( &aContext, outBuffer, &outLength, inBuffer, inLength );
+            aEncryptedPackageStream.writeMemory( outBuffer, outLength );
+        }
+    }
+    while (inLength > 0);
+
+    EVP_CIPHER_CTX_cleanup( &aContext );
+
+#endif // USE_TLS_OPENSSL
+
+    aEncryptedPackageStream.seekToStart();
+    aEncryptedPackageStream.close();
+
+    aDocumentInputStream.seekToStart();
+    aDocumentInputStream.close();
+
+    xEncryptedPackage->flush();
+    xEncryptedPackage->closeOutput();
+
+    return true;
+}
+
+} // namespace core
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/core/filterbase.cxx b/oox/source/core/filterbase.cxx
index 6a6f73b..f30362f 100644
--- a/oox/source/core/filterbase.cxx
+++ b/oox/source/core/filterbase.cxx
@@ -496,7 +496,7 @@ sal_Bool SAL_CALL FilterBase::filter( const Sequence< PropertyValue >& rMediaDes
                 if( mxImpl->mxOutStream.is() )
                 {
                     mxImpl->mxStorage = implCreateStorage( mxImpl->mxOutStream );
-                    bRet = mxImpl->mxStorage.get() && exportDocument();
+                    bRet = mxImpl->mxStorage.get() && exportDocument() && implFinalizeExport( getMediaDescriptor() );
                 }
             break;
         }
@@ -521,6 +521,16 @@ Reference< XStream > FilterBase::implGetOutputStream( MediaDescriptor& rMediaDes
     return rMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_STREAMFOROUTPUT(), Reference< XStream >() );
 }
 
+bool FilterBase::implFinalizeExport( MediaDescriptor& /*rMediaDescriptor*/ )
+{
+    return true;
+}
+
+Reference< XStream > FilterBase::getMainDocumentStream( ) const
+{
+    return mxImpl->mxOutStream;
+}
+
 // private --------------------------------------------------------------------
 
 void FilterBase::setMediaDescriptor( const Sequence< PropertyValue >& rMediaDescSeq )
diff --git a/oox/source/core/xmlfilterbase.cxx b/oox/source/core/xmlfilterbase.cxx
index 97b6dc2..d878ad9 100644
--- a/oox/source/core/xmlfilterbase.cxx
+++ b/oox/source/core/xmlfilterbase.cxx
@@ -49,6 +49,8 @@
 #include <oox/core/filterdetect.hxx>
 #include <comphelper/storagehelper.hxx>
 
+#include <oox/core/DocumentCrypt.hxx>
+
 using ::com::sun::star::xml::dom::DocumentBuilder;
 using ::com::sun::star::xml::dom::XDocument;
 using ::com::sun::star::xml::dom::XDocumentBuilder;
@@ -645,6 +647,73 @@ Reference< XInputStream > XmlFilterBase::implGetInputStream( MediaDescriptor& rM
     return aDetector.extractUnencryptedPackage( rMediaDesc );
 }
 
+Reference<XStream> XmlFilterBase::implGetOutputStream( MediaDescriptor& rMediaDescriptor ) const
+{
+    Sequence< NamedValue > aMediaEncData;
+    aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault(
+                                        MediaDescriptor::PROP_ENCRYPTIONDATA(),
+                                        Sequence< NamedValue >() );
+
+    OUString aPassword;
+    for (int i=0; i<aMediaEncData.getLength(); i++)
+    {
+        if (aMediaEncData[i].Name == "Password")
+        {
+            Any& any = aMediaEncData[i].Value;
+            any >>= aPassword;
+            break;
+        }
+    }
+    if (aPassword.isEmpty())
+    {
+        return FilterBase::implGetOutputStream( rMediaDescriptor );
+    }
+    else // We need to encrypt the stream so create a memory stream
+    {
+        Reference< XComponentContext > xContext = getComponentContext();
+        return Reference< XStream > (
+                    xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.MemoryStream", xContext),
+                    uno::UNO_QUERY_THROW );
+    }
+    return Reference<XStream>();
+}
+
+bool XmlFilterBase::implFinalizeExport( MediaDescriptor& rMediaDescriptor )
+{
+    bool bRet = true;
+
+    Sequence< NamedValue > aMediaEncData;
+    aMediaEncData = rMediaDescriptor.getUnpackedValueOrDefault(
+                                        MediaDescriptor::PROP_ENCRYPTIONDATA(),
+                                        Sequence< NamedValue >() );
+
+    OUString aPassword;
+
+    for (int i=0; i<aMediaEncData.getLength(); i++)
+    {
+        if (aMediaEncData[i].Name == "Password")
+        {
+            Any& any = aMediaEncData[i].Value;
+            any >>= aPassword;
+            break;
+        }
+    }
+
+    if (!aPassword.isEmpty())
+    {
+        commitStorage();
+
+        Reference< XStream> xDocumentStream (FilterBase::implGetOutputStream(rMediaDescriptor));
+        oox::ole::OleStorage aOleStorage( getComponentContext(), xDocumentStream, true );
+        AesEncoder encoder(getMainDocumentStream(), aOleStorage, aPassword);
+        bRet = encoder.encode();
+        if (bRet)
+            aOleStorage.commit();
+    }
+
+    return bRet;
+}
+
 // private --------------------------------------------------------------------
 
 StorageRef XmlFilterBase::implCreateStorage( const Reference< XInputStream >& rxInStream ) const
diff --git a/sfx2/source/dialog/filedlghelper.cxx b/sfx2/source/dialog/filedlghelper.cxx
index e0ce50b..b9132eb 100644
--- a/sfx2/source/dialog/filedlghelper.cxx
+++ b/sfx2/source/dialog/filedlghelper.cxx
@@ -2719,20 +2719,30 @@ ErrCode RequestPassword(const SfxFilter* pCurrentFilter, OUString& aURL, SfxItem
             // TODO/LATER: The filters should show the password dialog themself in future
             if ( bMSType )
             {
-                // all the current MS-filters use MSCodec_Std97 implementation
-                uno::Sequence< sal_Int8 > aUniqueID = ::comphelper::DocPasswordHelper::GenerateRandomByteSequence( 16 );
-                uno::Sequence< sal_Int8 > aEncryptionKey = ::comphelper::DocPasswordHelper::GenerateStd97Key( pPasswordRequest->getPassword(), aUniqueID );
-
-                if ( aEncryptionKey.getLength() )
+                if ( pCurrentFilter->GetFilterName() == "Calc MS Excel 2007 XML" )
                 {
                     ::comphelper::SequenceAsHashMap aHashData;
-                    aHashData[ OUString( "STD97EncryptionKey"  ) ] <<= aEncryptionKey;
-                    aHashData[ OUString( "STD97UniqueID"  ) ] <<= aUniqueID;
-
+                    aHashData[ OUString( "Password"  ) ] <<= pPasswordRequest->getPassword();
                     pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aHashData.getAsConstNamedValueList() ) ) );
                 }
                 else
-                    return ERRCODE_IO_NOTSUPPORTED;
+                {
+                    uno::Sequence< sal_Int8 > aUniqueID = ::comphelper::DocPasswordHelper::GenerateRandomByteSequence( 16 );
+                    uno::Sequence< sal_Int8 > aEncryptionKey = ::comphelper::DocPasswordHelper::GenerateStd97Key( pPasswordRequest->getPassword(), aUniqueID );
+
+                    if ( aEncryptionKey.getLength() )
+                    {
+                        ::comphelper::SequenceAsHashMap aHashData;
+                        aHashData[ OUString( "STD97EncryptionKey"  ) ] <<= aEncryptionKey;
+                        aHashData[ OUString( "STD97UniqueID"  ) ] <<= aUniqueID;
+
+                        pSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::makeAny( aHashData.getAsConstNamedValueList() ) ) );
+                    }
+                    else
+                    {
+                        return ERRCODE_IO_NOTSUPPORTED;
+                    }
+                }
             }
             else
             {


More information about the Libreoffice-commits mailing list