diff options
-rw-r--r-- | filter/source/config/fragments/filters/calc_MS_Excel_2007_XML.xcu | 2 | ||||
-rw-r--r-- | include/oox/core/DocumentCrypt.hxx | 53 | ||||
-rw-r--r-- | include/oox/core/filterbase.hxx | 5 | ||||
-rw-r--r-- | include/oox/core/xmlfilterbase.hxx | 7 | ||||
-rw-r--r-- | oox/Library_oox.mk | 1 | ||||
-rw-r--r-- | oox/source/core/DocumentCrypt.cxx | 498 | ||||
-rw-r--r-- | oox/source/core/filterbase.cxx | 12 | ||||
-rw-r--r-- | oox/source/core/xmlfilterbase.cxx | 69 | ||||
-rw-r--r-- | sfx2/source/dialog/filedlghelper.cxx | 28 |
9 files changed, 663 insertions, 12 deletions
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 7f1fddd8fcbf..555ccb1bb6fb 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 000000000000..30102365f59d --- /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 cfd9354499f7..da8600659071 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 5bddea46e594..6d61eb1d7e36 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 45f4b9cc494a..f8477369319b 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 000000000000..5e29ee8a3096 --- /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 6a6f73b1e386..f30362f3e493 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 97b6dc2e54c6..d878ad98937c 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 e0ce50bd16c9..b9132ebef151 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 { |