This is an automated email from the ASF dual-hosted git repository. mseidel pushed a commit to branch AOO42X in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 4e7b0f82e23b35444af97a04a35fe1b376518a4a Author: Damjan Jovanovic <dam...@apache.org> AuthorDate: Sat Mar 2 18:47:05 2024 +0200 Implement the (MS Office 2010+) OOXML "Agile encryption" support, so that we can open such password-protected OOXML files. Adds all the Agile encryption XML tokens and namespaces, and parses the XML from EncryptionInfo stream, gets OpenOffice to recognize the file is encrypted and ask for a password, and successfully decrypts the file if password is correct. Also a number of other fixes and improvements: - Sorted main/oox/source/token/tokens.txt so it's in alphabetical order (wrong order might have broken certain tokens?). - Refactored how OOXML encryption is generally handled. It's now in its own file. - Added logging to the FilterDetect class. It logs to the office-wide default logger. - Added a flush() method to the BinaryXOutputStream class. - Changed FilterDetect to use XMultiComponentFactory and XComponentContext instead of the deprecated XMultiServiceFactory. - Error handling was generally improved. - Exception safety and some memory safety (::std::vector instead of new[]) in all the new code. Memory leaks should not be possible. Much of the code involved in the decryption was ported from the excellent Apache POI project, so it's been credited in our NOTICE file. Patch by: me (cherry picked from commit 506fa58b1970084a0caacb50b3a805e469be4756) --- main/NOTICE | 39 + main/oox/Library_oox.mk | 2 + main/oox/inc/oox/core/encryption.hxx | 66 ++ main/oox/inc/oox/core/filterdetect.hxx | 2 + main/oox/inc/oox/helper/binaryoutputstream.hxx | 3 + main/oox/inc/oox/helper/openssl_wrapper.hxx | 158 ++++ main/oox/source/core/encryption.cxx | 985 +++++++++++++++++++++++++ main/oox/source/core/filterdetect.cxx | 316 ++------ main/oox/source/helper/binaryoutputstream.cxx | 12 + main/oox/source/helper/openssl_wrapper.cxx | 63 ++ main/oox/source/token/namespaces.hxx.tail | 2 + main/oox/source/token/namespaces.txt | 5 + main/oox/source/token/tokens.txt | 29 +- 13 files changed, 1422 insertions(+), 260 deletions(-) diff --git a/main/NOTICE b/main/NOTICE index 3c99fe722e..0b8baa42fa 100644 --- a/main/NOTICE +++ b/main/NOTICE @@ -21,6 +21,7 @@ Apache projects: - Apache Portable Runtime Utility Library - Apache Commons - used by MediaWiki Publisher extension - Apache Jakarta HttpClient - used by MediaWiki Publisher extension +- Apache POI - OOXML encryption code from Apache POI was ported to our main/oox/source/core/encryption.cxx - Apache Tomcat - used by MediaWiki Publisher extension The notices from these projects are following: @@ -107,6 +108,44 @@ This product includes software developed by The Apache Software Foundation (https://www.apache.org/). +Apache POI +Copyright 2003-2024 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (https://www.apache.org/). + +This product contains parts that were originally based on software from BEA. +Copyright (c) 2000-2003, BEA Systems, <http://www.bea.com/> (dead link), +which was acquired by Oracle Corporation in 2008. +<http://www.oracle.com/us/corporate/Acquisitions/bea/index.html> +<https://en.wikipedia.org/wiki/BEA_Systems> +Note: The ASF Secretary has on hand a Software Grant Agreement (SGA) from +BEA Systems, Inc. dated 9 Sep 2003 for XMLBeans signed by their EVP/CFO. + +This product contains W3C XML Schema documents. Copyright 2001-2003 (c) +World Wide Web Consortium (Massachusetts Institute of Technology, European +Research Consortium for Informatics and Mathematics, Keio University) + +This product contains the chunks_parse_cmds.tbl file from the vsdump program. +Copyright (C) 2006-2007 Valek Filippov (f...@df.ru) + +This product contains parts of the eID Applet project +<http://eid-applet.googlecode.com> and <https://github.com/e-Contract/eid-applet>. +Copyright (c) 2009-2018 +FedICT (federal ICT department of Belgium), e-Contract.be BVBA (https://www.e-contract.be), +Bart Hanssens from FedICT + +ExceptionUtils is derived from `scala.util.control.NonFatal` in scala-library +which was released under the Apache 2.0 license. + +Copyright (c) 2002-2023 EPFL +Copyright (c) 2011-2023 Lightbend, Inc. + +Scala includes software developed at +LAMP/EPFL (https://lamp.epfl.ch/) and +Lightbend, Inc. (https://www.lightbend.com/). + + Apache Tomcat Copyright 1999-2012 The Apache Software Foundation diff --git a/main/oox/Library_oox.mk b/main/oox/Library_oox.mk index 7ebe4358ca..24d499c64a 100644 --- a/main/oox/Library_oox.mk +++ b/main/oox/Library_oox.mk @@ -66,6 +66,7 @@ $(eval $(call gb_Library_add_exception_objects,oox,\ oox/source/core/binaryfilterbase \ oox/source/core/contexthandler \ oox/source/core/contexthandler2 \ + oox/source/core/encryption \ oox/source/core/fastparser \ oox/source/core/fasttokenhandler \ oox/source/core/filterbase \ @@ -186,6 +187,7 @@ $(eval $(call gb_Library_add_exception_objects,oox,\ oox/source/helper/datetimehelper \ oox/source/helper/graphichelper \ oox/source/helper/modelobjecthelper \ + oox/source/helper/openssl_wrapper \ oox/source/helper/progressbar \ oox/source/helper/propertymap \ oox/source/helper/propertyset \ diff --git a/main/oox/inc/oox/core/encryption.hxx b/main/oox/inc/oox/core/encryption.hxx new file mode 100644 index 0000000000..ed520eea10 --- /dev/null +++ b/main/oox/inc/oox/core/encryption.hxx @@ -0,0 +1,66 @@ +/************************************************************** + * + * 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 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + *************************************************************/ + + + +#ifndef OOX_CORE_ENCRYPTION_HXX +#define OOX_CORE_ENCRYPTION_HXX + +#include <sal/types.h> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include "com/sun/star/uno/Reference.hxx" +#include <com/sun/star/uno/XComponentContext.hpp> +#include <comphelper/sequenceashashmap.hxx> +#include "oox/helper/binaryinputstream.hxx" +#include "oox/helper/binaryoutputstream.hxx" + + +namespace oox { +namespace core { + +// ============================================================================ + +class EncryptionInfo +{ +public: + // Parses the given stream, and returns a subclass which implements the virtual methods below. + static EncryptionInfo* readEncryptionInfo( + const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext >& context, + ::com::sun::star::uno::Reference< ::com::sun::star::io::XInputStream>& inputStream ) throw ( ::com::sun::star::uno::Exception ); + + virtual ~EncryptionInfo() {} + // Checks whether decryption can work, ie. we support all the algorithms, key sizes, etc. + virtual bool isImplemented() = 0; + // On success, returns a non-empty sequence, and internally stores whatever is needed for a subsequent call to decryptStream() to work. + virtual ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue > verifyPassword( const ::rtl::OUString& rPassword ) throw ( ::com::sun::star::uno::Exception ) = 0; + // On success, returns true, and internally stores whatever is needed for a subsequent call to decryptStream() to work. + virtual bool verifyEncryptionData( const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue >& rEncryptionData ) throw ( ::com::sun::star::uno::Exception ) = 0; + // Decrypts the stream using keys derived from previous calls to verifyPassword() or verifyEncryptionData(). + virtual void decryptStream( BinaryXInputStream &aEncryptedPackage, BinaryXOutputStream &aDecryptedPackage ) throw ( ::com::sun::star::uno::Exception ) = 0; +}; + +// ============================================================================ + +} // namespace core +} // namespace oox + +#endif diff --git a/main/oox/inc/oox/core/filterdetect.hxx b/main/oox/inc/oox/core/filterdetect.hxx index 6227e705d2..5f7758ad8c 100644 --- a/main/oox/inc/oox/core/filterdetect.hxx +++ b/main/oox/inc/oox/core/filterdetect.hxx @@ -30,6 +30,7 @@ #include <com/sun/star/xml/sax/XFastDocumentHandler.hpp> #include <cppuhelper/implbase1.hxx> #include <cppuhelper/implbase2.hxx> +#include <comphelper/logging.hxx> #include "oox/dllapi.h" namespace com { namespace sun { namespace star { @@ -153,6 +154,7 @@ public: private: ::com::sun::star::uno::Reference< ::com::sun::star::uno::XComponentContext > mxContext; + ::comphelper::EventLogger logger; }; // ============================================================================ diff --git a/main/oox/inc/oox/helper/binaryoutputstream.hxx b/main/oox/inc/oox/helper/binaryoutputstream.hxx index ab02db88d7..a7def21076 100644 --- a/main/oox/inc/oox/helper/binaryoutputstream.hxx +++ b/main/oox/inc/oox/helper/binaryoutputstream.hxx @@ -111,6 +111,9 @@ public: virtual ~BinaryXOutputStream(); + /** Flushes the output stream. */ + void flush(); + /** Flushes and closes the output stream. Does also close the wrapped UNO output stream if bAutoClose has been set to true in the constructor. */ void close(); diff --git a/main/oox/inc/oox/helper/openssl_wrapper.hxx b/main/oox/inc/oox/helper/openssl_wrapper.hxx new file mode 100644 index 0000000000..fdb2c690c0 --- /dev/null +++ b/main/oox/inc/oox/helper/openssl_wrapper.hxx @@ -0,0 +1,158 @@ +/************************************************************** + * + * 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 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + *************************************************************/ + + + +#ifndef OOX_HELPER_OPENSSL_WRAPPER_HXX +#define OOX_HELPER_OPENSSL_WRAPPER_HXX + +#include <sal/types.h> +#include "com/sun/star/uno/Exception.hpp" + +#include <openssl/evp.h> + +namespace oox { + +// ============================================================================ + +extern void throwOpenSSLException( const char *prefix ) throw ( ::com::sun::star::uno::Exception ); + + +class OpenSSLDigest +{ +public: + OpenSSLDigest() throw ( ::com::sun::star::uno::Exception ) + { + digest_ctx = EVP_MD_CTX_new(); + if( digest_ctx == NULL ) + throwOpenSSLException( "Failed to create digest context" ); + } + + ~OpenSSLDigest() + { + EVP_MD_CTX_free( digest_ctx ); + } + + void initialize( const EVP_MD* aDigest ) throw ( ::com::sun::star::uno::Exception ) + { + if( 1 != EVP_DigestInit_ex( digest_ctx, aDigest, NULL ) ) + throwOpenSSLException( "Failed to initialize digest context" ); + digest = aDigest; + } + + int digestSize() throw ( ::com::sun::star::uno::Exception ) + { + return digestSize( digest ); + } + + void update( const void *data, unsigned int count ) throw ( ::com::sun::star::uno::Exception ) + { + if( 1 != EVP_DigestUpdate( digest_ctx, data, count ) ) + throwOpenSSLException( "Failed to update the digest context" ); + } + + void final( unsigned char *md, unsigned int *count ) throw ( ::com::sun::star::uno::Exception ) + { + if( 1 != EVP_DigestFinal_ex( digest_ctx, md, count ) ) + throwOpenSSLException( "Failed to finalize digest" ); + } + + static int digestSize( const EVP_MD* digest ) throw ( ::com::sun::star::uno::Exception ) + { + int digest_size = EVP_MD_size( digest ); + if( digest_size < 0 ) + throwOpenSSLException( "Failed to get digest size" ); + return digest_size; + } + +private: + OpenSSLDigest( const OpenSSLDigest& rValue ); + OpenSSLDigest& operator=( const OpenSSLDigest& rValue ); + + const EVP_MD* digest; + EVP_MD_CTX* digest_ctx; +}; + +// ============================================================================ + +class OpenSSLCipher +{ +public: + OpenSSLCipher() throw ( ::com::sun::star::uno::Exception ) + { + cipher_ctx = EVP_CIPHER_CTX_new(); + if( cipher_ctx == NULL ) + throwOpenSSLException( "Failed to create cipher context" ); + } + + ~OpenSSLCipher() + { + EVP_CIPHER_CTX_free( cipher_ctx ); + } + + void initialize( const EVP_CIPHER *aCipher, const unsigned char *key, const unsigned char *iv, int enc ) throw ( ::com::sun::star::uno::Exception ) + { + if( 1 != EVP_CipherInit_ex( cipher_ctx, aCipher, NULL, key, iv, enc ) ) + throwOpenSSLException( "Failed to initialize the cipher context for decryption" ); + cipher = aCipher; + } + + void setPadding( int padding) throw ( ::com::sun::star::uno::Exception ) + { + if( 1 != EVP_CIPHER_CTX_set_padding( cipher_ctx, padding ) ) + throwOpenSSLException( "Failed to set cipher padding" ); + } + + void update( const unsigned char* dataIn, int dataInSize, unsigned char *dataOut, int *dataOutSize ) throw ( ::com::sun::star::uno::Exception ) + { + if( 1 != EVP_CipherUpdate( cipher_ctx, dataOut, dataOutSize, dataIn, dataInSize ) ) + throwOpenSSLException( "EVP_CipherUpdate failed" ); + } + + void final( unsigned char *dataOut, int *dataOutSize ) throw ( ::com::sun::star::uno::Exception ) + { + if( 1 != EVP_CipherFinal( cipher_ctx, dataOut, dataOutSize ) ) + throwOpenSSLException( "EVP_CipherFinal failed" ); + } + + int blockSize() + { + return blockSize( cipher ); + } + + static int blockSize( const EVP_CIPHER *cipherAlgorithm ) + { + return EVP_CIPHER_block_size( cipherAlgorithm ); + } + +private: + OpenSSLCipher( const OpenSSLCipher& rValue ); + OpenSSLCipher& operator=( const OpenSSLCipher& rValue ); + + EVP_CIPHER_CTX* cipher_ctx; + const EVP_CIPHER* cipher; +}; + +// ============================================================================ + +} // namespace oox + +#endif diff --git a/main/oox/source/core/encryption.cxx b/main/oox/source/core/encryption.cxx new file mode 100644 index 0000000000..729a7701bd --- /dev/null +++ b/main/oox/source/core/encryption.cxx @@ -0,0 +1,985 @@ +/************************************************************** + * + * 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 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + *************************************************************/ + + + +#include "oox/core/encryption.hxx" +#include "oox/core/fastparser.hxx" +#include "oox/helper/attributelist.hxx" +#include "oox/helper/helper.hxx" +#include "oox/helper/openssl_wrapper.hxx" + +#include <rtl/digest.h> +#include <cppuhelper/implbase1.hxx> +#include <openssl/evp.h> + +#include <com/sun/star/io/XStream.hpp> + + + +namespace oox { +namespace core { + +// ============================================================================ + +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::xml::sax; + +using ::com::sun::star::io::XInputStream; +using ::comphelper::SequenceAsHashMap; +using ::rtl::OUString; +using ::std::vector; + +// ============================================================================ + + +/* =========================================================================== */ +/* Kudos to Caolan McNamara who provided the core decryption implementation */ +/* of Standard Encryption (MS-OFFCRYPTO section 2.3.4.5). */ +/* =========================================================================== */ + +#define ENCRYPTINFO_CRYPTOAPI 0x00000004U +#define ENCRYPTINFO_DOCPROPS 0x00000008U +#define ENCRYPTINFO_EXTERNAL 0x00000010U +#define ENCRYPTINFO_AES 0x00000020U + +#define ENCRYPT_ALGO_AES128 0x0000660EU +#define ENCRYPT_ALGO_AES192 0x0000660FU +#define ENCRYPT_ALGO_AES256 0x00006610U +#define ENCRYPT_ALGO_RC4 0x00006801U + +#define ENCRYPT_HASH_SHA1 0x00008004U + +class StandardEncryptionInfo : public EncryptionInfo +{ +public: + StandardEncryptionInfo( BinaryInputStream& rStrm ) throw ( Exception ); + ~StandardEncryptionInfo() {} + bool isImplemented(); + Sequence< NamedValue > verifyPassword( const OUString& rPassword ) throw ( Exception ); + bool verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData ) throw ( Exception ); + bool checkEncryptionData( const sal_uInt8* pnKey, sal_uInt32 nKeySize, const sal_uInt8* pnVerifier, sal_uInt32 nVerifierSize, const sal_uInt8* pnVerifierHash, sal_uInt32 nVerifierHashSize ) throw ( Exception ); + void decryptStream( BinaryXInputStream &aEncryptedPackage, BinaryXOutputStream &aDecryptedPackage ) throw ( Exception ); + +private: + 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; + vector< sal_uInt8> encryptionKey; +}; + +StandardEncryptionInfo::StandardEncryptionInfo( BinaryInputStream& rStrm ) throw ( Exception ) +{ + char msg[ 1024 ]; + rStrm >> mnFlags; + if( getFlag( mnFlags, ENCRYPTINFO_EXTERNAL ) ) + throw Exception( OUString::createFromAscii( "EncryptionInfo::readEncryptionInfo() error: \"Extensible encryption\" is not currently supported, please report" ), Reference< XInterface >() ); + + sal_uInt32 nHeaderSize, nRepeatedFlags; + rStrm >> nHeaderSize >> nRepeatedFlags; + if( nHeaderSize < 20 ) + { + snprintf( msg, sizeof( msg ), "EncryptionInfo::readEncryptionInfo() error: header size %u is too short", nHeaderSize ); + throw Exception( OUString::createFromAscii( msg ), Reference< XInterface >() ); + } + if( nRepeatedFlags != mnFlags ) + throw Exception( OUString::createFromAscii( "EncryptionInfo::readEncryptionInfo() error: flags don't match" ), Reference< XInterface>() ); + + rStrm.skip( 4 ); + rStrm >> mnAlgorithmId >> mnAlgorithmIdHash >> mnKeySize; + rStrm.skip( nHeaderSize - 20 ); + rStrm >> mnSaltSize; + if( mnSaltSize != 16 ) + { + snprintf( msg, sizeof( msg ), "EncryptionInfo::readEncryptionInfo() error: salt size is %u instead of 16", mnSaltSize ); + throw Exception( OUString::createFromAscii( msg ), Reference< XInterface >() ); + } + + rStrm.readMemory( mpnSalt, 16 ); + rStrm.readMemory( mpnEncrVerifier, 16 ); + rStrm >> mnVerifierHashSize; + rStrm.readMemory( mpnEncrVerifierHash, 32 ); + if( rStrm.isEof() ) + throw Exception( OUString::createFromAscii( "EncryptionInfo::readEncryptionInfo() error: standard encryption header too short" ), Reference< XInterface >() ); +} + +bool StandardEncryptionInfo::isImplemented() +{ + return getFlag( mnFlags, ENCRYPTINFO_CRYPTOAPI ) && + getFlag( mnFlags, ENCRYPTINFO_AES ) && + // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set + ( ( mnAlgorithmId == 0 ) || ( mnAlgorithmId == ENCRYPT_ALGO_AES128 ) ) && + // hash algorithm ID 0 defaults to SHA-1 too + ( ( mnAlgorithmIdHash == 0 ) || ( mnAlgorithmIdHash == ENCRYPT_HASH_SHA1 ) ) && + ( mnVerifierHashSize == 20 ); +} + +static void deriveKey( 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 ); + rtlDigestError aError = rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) ); + sal_uInt8 pnX1[ RTL_DIGEST_LENGTH_SHA1 ]; + aError = 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 ); + aError = rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) ); + sal_uInt8 pnX2[ RTL_DIGEST_LENGTH_SHA1 ]; + aError = 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 ); +} + +Sequence< NamedValue > StandardEncryptionInfo::verifyPassword( const OUString& rPassword ) throw ( Exception ) +{ + size_t nBufferSize = mnSaltSize + 2 * rPassword.getLength(); + sal_uInt8* pnBuffer = new sal_uInt8[ nBufferSize ]; + memcpy( pnBuffer, mpnSalt, mnSaltSize ); + + sal_uInt8* pnPasswordLoc = pnBuffer + 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 ); + rtlDigestError aError = rtl_digest_update( aDigest, pnBuffer, nBufferSize ); + delete[] pnBuffer; + + size_t nHashSize = RTL_DIGEST_LENGTH_SHA1 + 4; + sal_uInt8* pnHash = new sal_uInt8[ nHashSize ]; + aError = 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 ); + aError = rtl_digest_update( aDigest, pnHash, nHashSize ); + aError = 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 ); + aError = rtl_digest_update( aDigest, pnHash, nHashSize ); + aError = rtl_digest_get( aDigest, pnHash, RTL_DIGEST_LENGTH_SHA1 ); + rtl_digest_destroy( aDigest ); + + vector< sal_uInt8 > key( mnKeySize / 8 ); + deriveKey( pnHash, RTL_DIGEST_LENGTH_SHA1, key.data(), key.size() ); + delete[] pnHash; + + Sequence< NamedValue > aResult; + if( checkEncryptionData( key.data(), key.size(), mpnEncrVerifier, sizeof( mpnEncrVerifier ), mpnEncrVerifierHash, sizeof( mpnEncrVerifierHash ) ) ) + { + SequenceAsHashMap aEncryptionData; + aEncryptionData[ CREATE_OUSTRING( "AES128EncryptionKey" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( key.data() ), key.size() ); + aEncryptionData[ CREATE_OUSTRING( "AES128EncryptionSalt" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( mpnSalt ), mnSaltSize ); + aEncryptionData[ CREATE_OUSTRING( "AES128EncryptionVerifier" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( mpnEncrVerifier ), sizeof( mpnEncrVerifier ) ); + aEncryptionData[ CREATE_OUSTRING( "AES128EncryptionVerifierHash" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( mpnEncrVerifierHash ), sizeof( mpnEncrVerifierHash ) ); + encryptionKey = key; + aResult = aEncryptionData.getAsConstNamedValueList(); + } + + return aResult; +} + +bool StandardEncryptionInfo::verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData ) throw ( Exception ) +{ + SequenceAsHashMap aHashData( rEncryptionData ); + Sequence< sal_Int8 > aKey = aHashData.getUnpackedValueOrDefault( CREATE_OUSTRING( "AES128EncryptionKey" ), Sequence< sal_Int8 >() ); + Sequence< sal_Int8 > aVerifier = aHashData.getUnpackedValueOrDefault( CREATE_OUSTRING( "AES128EncryptionVerifier" ), Sequence< sal_Int8 >() ); + Sequence< sal_Int8 > aVerifierHash = aHashData.getUnpackedValueOrDefault( CREATE_OUSTRING( "AES128EncryptionVerifierHash" ), Sequence< sal_Int8 >() ); + const sal_uInt8 *pnKey = reinterpret_cast< const sal_uInt8* >( aKey.getConstArray() ); + sal_uInt32 nKeySize = aKey.getLength(); + const sal_uInt8 *pnVerifier = reinterpret_cast< const sal_uInt8* >( aVerifier.getConstArray() ); + sal_uInt32 nVerifierSize = aVerifier.getLength(); + const sal_uInt8 *pnVerifierHash = reinterpret_cast< const sal_uInt8* >( aVerifierHash.getConstArray() ); + sal_uInt32 nVerifierHashSize = aVerifierHash.getLength(); + if( checkEncryptionData( pnKey, nKeySize, pnVerifier, nVerifierSize, pnVerifierHash, nVerifierHashSize ) ) + { + encryptionKey = vector< sal_uInt8 >( &pnKey[ 0 ], &pnKey[ nKeySize ] ); + return true; + } + else + return false; +} + +bool StandardEncryptionInfo::checkEncryptionData( const sal_uInt8* pnKey, sal_uInt32 nKeySize, const sal_uInt8* pnVerifier, sal_uInt32 nVerifierSize, const sal_uInt8* pnVerifierHash, sal_uInt32 nVerifierHashSize ) throw ( Exception ) +{ + bool bResult = false; + + // the only currently supported algorithm needs key size 128 + if ( nKeySize == 16 && nVerifierSize == 16 && nVerifierHashSize == 32 ) + { + // check password + EVP_CIPHER_CTX *aes_ctx; + aes_ctx = EVP_CIPHER_CTX_new(); + if ( aes_ctx == NULL ) + return false; + 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_free( aes_ctx ); + + aes_ctx = EVP_CIPHER_CTX_new(); + if ( aes_ctx == NULL ) + return false; + EVP_DecryptInit_ex( aes_ctx, EVP_aes_128_ecb(), 0, pnKey, 0 ); + EVP_CIPHER_CTX_set_padding( aes_ctx, 0 ); + sal_uInt8 pnTmpVerifierHash[ 32 ]; + (void) memset( pnTmpVerifierHash, 0, sizeof(pnTmpVerifierHash) ); + + /*int*/ EVP_DecryptUpdate( aes_ctx, pnTmpVerifierHash, &nOutLen, pnVerifierHash, nVerifierHashSize ); + EVP_CIPHER_CTX_free( aes_ctx ); + + rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 ); + rtlDigestError aError = rtl_digest_update( aDigest, pnTmpVerifier, sizeof( pnTmpVerifier ) ); + sal_uInt8 pnSha1Hash[ RTL_DIGEST_LENGTH_SHA1 ]; + aError = rtl_digest_get( aDigest, pnSha1Hash, RTL_DIGEST_LENGTH_SHA1 ); + rtl_digest_destroy( aDigest ); + + bResult = ( memcmp( pnSha1Hash, pnTmpVerifierHash, RTL_DIGEST_LENGTH_SHA1 ) == 0 ); + } + + return bResult; +} + +void StandardEncryptionInfo::decryptStream( BinaryXInputStream &aEncryptedPackage, BinaryXOutputStream &aDecryptedPackage ) throw ( Exception ) +{ + EVP_CIPHER_CTX *aes_ctx; + aes_ctx = EVP_CIPHER_CTX_new(); + if ( aes_ctx == NULL ) + throw Exception(); + EVP_DecryptInit_ex( aes_ctx, EVP_aes_128_ecb(), 0, encryptionKey.data(), 0 ); + EVP_CIPHER_CTX_set_padding( aes_ctx, 0 ); + + sal_uInt8 pnInBuffer[ 1024 ]; + sal_uInt8 pnOutBuffer[ 1024 ]; + sal_Int32 nInLen; + int nOutLen; + aEncryptedPackage.skip( 8 ); // decrypted size + while( (nInLen = aEncryptedPackage.readMemory( pnInBuffer, sizeof( pnInBuffer ) )) > 0 ) + { + EVP_DecryptUpdate( aes_ctx, pnOutBuffer, &nOutLen, pnInBuffer, nInLen ); + aDecryptedPackage.writeMemory( pnOutBuffer, nOutLen ); + } + EVP_DecryptFinal_ex( aes_ctx, pnOutBuffer, &nOutLen ); + aDecryptedPackage.writeMemory( pnOutBuffer, nOutLen ); + + EVP_CIPHER_CTX_free( aes_ctx ); + aDecryptedPackage.flush(); +} + +// ============================================================================ +// "Agile" encryption, 2.3.4.10 of MS-OFFCRYPTO +// ============================================================================ + +struct AgileKeyData +{ + sal_Int32 saltSize; + sal_Int32 blockSize; + sal_Int32 keyBits; + sal_Int32 hashSize; + OUString cipherAlgorithm; + OUString cipherChaining; + OUString hashAlgorithm; + vector< sal_uInt8 > saltValue; +}; + +struct AgileDataIntegrity +{ + vector< sal_uInt8 > encryptedHmacKey; + vector< sal_uInt8 > encryptedHmacValue; +}; + +struct AgilePasswordKeyEncryptor +{ + sal_Int32 saltSize; + sal_Int32 blockSize; + sal_Int32 keyBits; + sal_Int32 hashSize; + OUString cipherAlgorithm; + OUString cipherChaining; + OUString hashAlgorithm; + vector< sal_uInt8 > saltValue; + sal_Int32 spinCount; + vector< sal_uInt8 > encryptedVerifierHashInput; + vector< sal_uInt8 > encryptedVerifierHashValue; + vector< sal_uInt8 > encryptedKeyValue; +}; + +static bool decodeBase64( OUString& base64, vector< sal_uInt8 >& bytes ) +{ + ::rtl::OString base64Ascii = ::rtl::OUStringToOString( base64, RTL_TEXTENCODING_UTF8 ); + const sal_uInt32 len = base64Ascii.getLength(); + bytes.resize( (len + 3) / 4 * 3 ); + int decodedSize = EVP_DecodeBlock( bytes.data(), reinterpret_cast< sal_uInt8 const * >( base64Ascii.getStr() ), len ); + if ( decodedSize < 0 ) + return false; + if ( len >= 2 && base64Ascii[ len-1 ] == '=' && base64Ascii[ len-2 ] == '=' ) + decodedSize -= 2; + else if ( len >= 1 && base64Ascii[ len-1] == '=' ) + decodedSize--; + bytes.resize( decodedSize ); + return true; +} + +class AgileEncryptionInfo : public EncryptionInfo +{ +public: + AgileEncryptionInfo( const Reference< XComponentContext >& context, Reference< XInputStream >& inputStream ) throw ( Exception ); + ~AgileEncryptionInfo() {} + bool isImplemented() { return true; } // FIXME + Sequence< NamedValue > verifyPassword( const OUString& rPassword ) throw ( Exception ); + bool verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData ) throw ( Exception ); + void decryptStream( BinaryXInputStream &aEncryptedPackage, BinaryXOutputStream &aDecryptedPackage ) throw ( Exception ); + +private: + AgileKeyData keyData; + AgileDataIntegrity dataIntegrity; + AgilePasswordKeyEncryptor passwordKeyEncryptor; + vector< sal_uInt8> encryptionKey; + vector< sal_uInt8> hmacKey; + vector< sal_uInt8> hmacValue; +}; + +// A SAX handler that parses the XML from the "XmlEncryptionDescriptor" in the EncryptionInfo stream. +class AgileEncryptionHandler : public ::cppu::WeakImplHelper1< XFastDocumentHandler > +{ +public: + AgileEncryptionHandler( AgileKeyData &aKeyData, AgileDataIntegrity &aDataIntegrity, AgilePasswordKeyEncryptor &aPasswordKeyEncryptor ) + : keyData( aKeyData ), + dataIntegrity( aDataIntegrity ), + passwordKeyEncryptor( aPasswordKeyEncryptor ) + { + } + + // XFastDocumentHandler + virtual void SAL_CALL startDocument() throw (SAXException, RuntimeException); + virtual void SAL_CALL endDocument() throw (SAXException, RuntimeException); + virtual void SAL_CALL setDocumentLocator( const Reference< XLocator >& xLocator ) throw (SAXException, RuntimeException); + + // XFastContextHandler + virtual void SAL_CALL startFastElement( sal_Int32 nElement, const Reference< XFastAttributeList >& Attribs ) throw (SAXException, RuntimeException); + virtual void SAL_CALL startUnknownElement( const OUString& Namespace, const OUString& Name, const Reference< XFastAttributeList >& Attribs ) throw (SAXException, RuntimeException); + virtual void SAL_CALL endFastElement( sal_Int32 Element ) throw (SAXException, RuntimeException); + virtual void SAL_CALL endUnknownElement( const OUString& Namespace, const OUString& Name ) throw (SAXException, RuntimeException); + virtual Reference< XFastContextHandler > SAL_CALL createFastChildContext( sal_Int32 Element, const Reference< XFastAttributeList >& Attribs ) throw (SAXException, RuntimeException); + virtual Reference< XFastContextHandler > SAL_CALL createUnknownChildContext( const OUString& Namespace, const OUString& Name, const Reference< XFastAttributeList >& Attribs ) throw (SAXException, RuntimeException); + virtual void SAL_CALL characters( const OUString& aChars ) throw (SAXException, RuntimeException); + virtual void SAL_CALL ignorableWhitespace( const OUString& aWhitespaces ) throw (SAXException, RuntimeException); + virtual void SAL_CALL processingInstruction( const OUString& aTarget, const OUString& aData ) throw (SAXException, RuntimeException); + + OUString& getLastError() { return lastError; } + +private: + void parseKeyData( const AttributeList& attribs ) throw (SAXException, RuntimeException); + void parseDataIntegrity( const AttributeList& attribs ) throw (SAXException, RuntimeException); + void parseEncryptedKey( const AttributeList& attribs ) throw (SAXException, RuntimeException); + + vector< sal_Int32 > stack; + OUString lastError; + AgileKeyData &keyData; + AgileDataIntegrity &dataIntegrity; + AgilePasswordKeyEncryptor &passwordKeyEncryptor; +}; + +void AgileEncryptionHandler::startDocument() + throw ( SAXException, RuntimeException ) +{ +} + +void AgileEncryptionHandler::endDocument() + throw ( SAXException, RuntimeException ) +{ +} + +void AgileEncryptionHandler::setDocumentLocator( const Reference< XLocator >& ) + throw ( SAXException, RuntimeException ) +{ +} + +void AgileEncryptionHandler::startFastElement( sal_Int32 nElement, const Reference< XFastAttributeList >& attribs ) + throw( SAXException, RuntimeException ) +{ + switch ( nElement ) + { + case ENCRYPTION_TOKEN( encryption ): + break; + + case ENCRYPTION_TOKEN( keyData ): + if ( stack.size() == 1 && (stack[ 0 ] == ENCRYPTION_TOKEN( encryption )) ) + parseKeyData( AttributeList( attribs ) ); + break; + + case ENCRYPTION_TOKEN( dataIntegrity ): + if ( stack.size() == 1 && (stack[ 0 ] == ENCRYPTION_TOKEN( encryption )) ) + parseDataIntegrity( AttributeList ( attribs ) ); + break; + + case ENCRYPTION_TOKEN( keyEncryptors ): + break; + + case ENCRYPTION_TOKEN( keyEncryptor ): + break; + + case KEY_ENCRYPTOR_PASSWORD_TOKEN( encryptedKey ): + if ( stack.size() == 3 + && (stack[ 0 ] == ENCRYPTION_TOKEN( encryption )) + && (stack[ 1 ] == ENCRYPTION_TOKEN( keyEncryptors )) + && (stack[ 2 ] == ENCRYPTION_TOKEN( keyEncryptor )) ) + parseEncryptedKey( AttributeList ( attribs ) ); + break; + } + stack.push_back( nElement ); +} + +void AgileEncryptionHandler::startUnknownElement( const OUString&, const OUString&, const Reference< XFastAttributeList >& ) + throw( SAXException, RuntimeException ) +{ + stack.push_back( -1 ); +} + +void AgileEncryptionHandler::endFastElement( sal_Int32 nElement ) + throw( SAXException, RuntimeException ) +{ + stack.pop_back(); +} + +void AgileEncryptionHandler::endUnknownElement( const OUString&, const OUString& ) + throw( SAXException, RuntimeException ) +{ + stack.pop_back(); +} + +Reference< XFastContextHandler > AgileEncryptionHandler::createFastChildContext( sal_Int32, const Reference< XFastAttributeList >& ) + throw (SAXException, RuntimeException) +{ + return this; +} + +Reference< XFastContextHandler > AgileEncryptionHandler::createUnknownChildContext( const OUString&, const OUString&, const Reference< XFastAttributeList >& ) + throw (SAXException, RuntimeException) +{ + return this; +} + +void AgileEncryptionHandler::characters( const ::rtl::OUString& rStr ) + throw( SAXException, RuntimeException ) +{ +} + +void AgileEncryptionHandler::ignorableWhitespace( const ::rtl::OUString& str ) + throw( SAXException, RuntimeException ) +{ +} + +void AgileEncryptionHandler::processingInstruction( const ::rtl::OUString& aTarget, const ::rtl::OUString& aData ) + throw( SAXException, RuntimeException ) +{ +} + +void AgileEncryptionHandler::parseKeyData( const AttributeList& attribs ) + throw ( SAXException, RuntimeException ) +{ + keyData.saltSize = attribs.getInteger( XML_saltSize, 0 ); + keyData.blockSize = attribs.getInteger( XML_blockSize, 0 ); + keyData.keyBits = attribs.getInteger( XML_keyBits, 0 ); + keyData.hashSize = attribs.getInteger( XML_hashSize, 0 ); + keyData.cipherAlgorithm = attribs.getString( XML_cipherAlgorithm, OUString() ); + keyData.cipherChaining = attribs.getString( XML_cipherChaining, OUString() ); + keyData.hashAlgorithm = attribs.getString( XML_hashAlgorithm, OUString() ); + + OUString saltValue = attribs.getString( XML_saltValue, OUString() ); + if( !decodeBase64( saltValue, keyData.saltValue ) ) + lastError = OUString::createFromAscii( "Failed to base64 decode the keyData.saltValue " ) + saltValue; +} + +void AgileEncryptionHandler::parseDataIntegrity( const AttributeList& attribs ) + throw ( SAXException, RuntimeException ) +{ + OUString encryptedHmacKey = attribs.getString( XML_encryptedHmacKey, OUString() ); + if( !decodeBase64( encryptedHmacKey, dataIntegrity.encryptedHmacKey ) ) + lastError = OUString::createFromAscii( "Failed to base64 decode the dataIntegrity.encryptedHmacKey " ) + encryptedHmacKey; + OUString encryptedHmacValue = attribs.getString( XML_encryptedHmacValue, OUString() ); + if( !decodeBase64( encryptedHmacValue, dataIntegrity.encryptedHmacValue ) ) + lastError = OUString::createFromAscii( "Failed to base64 decode the dataIntegrity.encryptedHmacValue " ) + encryptedHmacValue; +} + +void AgileEncryptionHandler::parseEncryptedKey( const AttributeList& attribs ) + throw ( SAXException, RuntimeException ) +{ + passwordKeyEncryptor.spinCount = attribs.getInteger( XML_spinCount, 0 ); + passwordKeyEncryptor.saltSize = attribs.getInteger( XML_saltSize, 0 ); + passwordKeyEncryptor.blockSize = attribs.getInteger( XML_blockSize, 0 ); + passwordKeyEncryptor.keyBits = attribs.getInteger( XML_keyBits, 0 ); + passwordKeyEncryptor.hashSize = attribs.getInteger( XML_hashSize, 0 ); + passwordKeyEncryptor.cipherAlgorithm = attribs.getString( XML_cipherAlgorithm, OUString() ); + passwordKeyEncryptor.cipherChaining = attribs.getString( XML_cipherChaining, OUString() ); + passwordKeyEncryptor.hashAlgorithm = attribs.getString( XML_hashAlgorithm, OUString() ); + OUString saltValue = attribs.getString( XML_saltValue, OUString() ); + if( !decodeBase64( saltValue, passwordKeyEncryptor.saltValue ) ) + lastError = OUString::createFromAscii( "Failed to base64 decode the passwordKeyEncryptor.saltValue " ) + saltValue; + OUString encryptedVerifierHashInput = attribs.getString( XML_encryptedVerifierHashInput, OUString() ); + if( !decodeBase64( encryptedVerifierHashInput, passwordKeyEncryptor.encryptedVerifierHashInput ) ) + lastError = OUString::createFromAscii( "Failed to base64 decode the passwordKeyEncryptor.encryptedVerifierHashInput " ) + encryptedVerifierHashInput; + OUString encryptedVerifierHashValue = attribs.getString( XML_encryptedVerifierHashValue, OUString() ); + if( !decodeBase64( encryptedVerifierHashValue, passwordKeyEncryptor.encryptedVerifierHashValue ) ) + lastError = OUString::createFromAscii( "Failed to base64 decode the passwordKeyEncryptor.encryptedVerifierHashValue " ) + encryptedVerifierHashValue; + OUString encryptedKeyValue = attribs.getString( XML_encryptedKeyValue, OUString() ); + if( !decodeBase64( encryptedKeyValue, passwordKeyEncryptor.encryptedKeyValue ) ) + lastError = OUString::createFromAscii( "Failed to base64 decode the passwordKeyEncryptor.encryptedKeyValue " ) + encryptedKeyValue; +} + +static sal_uInt16 readUInt16LE( Reference< XInputStream >& inputStream ) throw ( Exception ) +{ + Sequence< sal_Int8 > bytes( 2 ); + sal_Int32 bytesRead = inputStream->readBytes( bytes, 2 ); + if( bytesRead < 2 ) + throw new Exception( OUString::createFromAscii( "EncryptionInfo::readEncryptionInfo() failed, early end of file" ), Reference< XInterface >() ); + return (sal_uInt16) ( bytes[0] | (bytes[1] << 8) ); +} + +static sal_uInt32 readUInt32LE( Reference< XInputStream >& inputStream ) throw ( Exception ) +{ + Sequence< sal_Int8 > bytes( 4 ); + sal_Int32 bytesRead = inputStream->readBytes( bytes, 4 ); + if( bytesRead < 4 ) + throw new Exception( OUString::createFromAscii( "EncryptionInfo::readEncryptionInfo() failed, early end of file" ), Reference< XInterface >() ); + return (sal_uInt32) ( bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24) ); +} + +AgileEncryptionInfo::AgileEncryptionInfo( const Reference< XComponentContext >& context, Reference< XInputStream >& inputStream ) throw ( Exception ) +{ + sal_uInt32 nReserved = readUInt32LE( inputStream ); + if( nReserved != 0x40 ) + throw new Exception( OUString::createFromAscii( "reserved field isn't 0x40" ), Reference< XInterface >() ); + AgileEncryptionHandler *agileEncryptionHandler = new AgileEncryptionHandler( keyData, dataIntegrity, passwordKeyEncryptor ); + Reference< XFastDocumentHandler > documentHandler( agileEncryptionHandler ); + FastParser fastParser( context ); + fastParser.registerNamespace( NMSP_encryption ); + fastParser.registerNamespace( NMSP_keyEncryptorPassword ); + fastParser.setDocumentHandler( documentHandler ); + fastParser.parseStream( inputStream, OUString::createFromAscii( "EncryptionInfo" ), false ); + if( !agileEncryptionHandler->getLastError().isEmpty() ) + throw new Exception( agileEncryptionHandler->getLastError(), Reference< XInterface >() ); +} + +static const EVP_MD* toOpenSSLDigestAlgorithm( const OUString& hashAlgorithm ) throw ( Exception ) +{ + if( hashAlgorithm.equalsAscii( "SHA-1" ) ) + return EVP_sha1(); + else if( hashAlgorithm.equalsAscii( "SHA1" ) ) // Typical Microsoft. The specification says "SHA-1", but documents use "SHA1". + return EVP_sha1(); + else if( hashAlgorithm.equalsAscii( "SHA256" ) ) + return EVP_sha256(); + else if( hashAlgorithm.equalsAscii( "SHA384" ) ) + return EVP_sha384(); + else if( hashAlgorithm.equalsAscii( "SHA512" ) ) + return EVP_sha512(); + else if( hashAlgorithm.equalsAscii( "MD5" ) ) + return EVP_md5(); + else if( hashAlgorithm.equalsAscii( "MD4" ) ) + return EVP_md4(); +#if !defined(OPENSSL_NO_MD2) + else if( hashAlgorithm.equalsAscii( "MD2" ) ) + return EVP_md2(); +#endif + else if( hashAlgorithm.equalsAscii( "RIPEMD-160" ) ) + return EVP_ripemd160(); + else if( hashAlgorithm.equalsAscii( "WHIRLPOOL" ) ) + return EVP_whirlpool(); + char buffer[ 256 ]; + ::rtl::OString str = ::rtl::OUStringToOString( hashAlgorithm, RTL_TEXTENCODING_UTF8 ); + snprintf( buffer, sizeof( buffer ), "Unsupported digest algorithm %s", str.getStr() ); + throw Exception( OUString::createFromAscii( buffer ), Reference< XInterface >() ); +} + +static const EVP_CIPHER* toOpenSSLCipherAlgorithm( const OUString& cipherName, sal_uInt32 keyBits, const OUString &chainingMode ) throw ( Exception ) +{ + if( cipherName.equalsAscii( "AES" ) && keyBits == 128 && chainingMode.equalsAscii( "ChainingModeCBC" ) ) + return EVP_aes_128_cbc(); + else if( cipherName.equalsAscii( "AES" ) && keyBits == 128 && chainingMode.equalsAscii( "ChainingModeCFB" ) ) + return EVP_aes_128_cfb(); + else if( cipherName.equalsAscii( "AES" ) && keyBits == 192 && chainingMode.equalsAscii( "ChainingModeCBC" ) ) + return EVP_aes_192_cbc(); + else if( cipherName.equalsAscii( "AES" ) && keyBits == 192 && chainingMode.equalsAscii( "ChainingModeCFB" ) ) + return EVP_aes_192_cfb(); + else if( cipherName.equalsAscii( "AES" ) && keyBits == 256 && chainingMode.equalsAscii( "ChainingModeCBC" ) ) + return EVP_aes_256_cbc(); + else if( cipherName.equalsAscii( "AES" ) && keyBits == 256 && chainingMode.equalsAscii( "ChainingModeCFB" ) ) + return EVP_aes_256_cfb(); +#if !defined(OPENSSL_NO_RC2) + else if( cipherName.equalsAscii( "RC2" ) && keyBits == 128 && chainingMode.equalsAscii( "ChainingModeCBC" ) ) + return EVP_rc2_cbc(); + else if( cipherName.equalsAscii( "RC2" ) && keyBits == 128 && chainingMode.equalsAscii( "ChainingModeCFB" ) ) + return EVP_rc2_cfb(); +#endif +#if !defined(OPENSSL_NO_DES) + else if( cipherName.equalsAscii( "DES" ) && keyBits == 56 && chainingMode.equalsAscii( "ChainingModeCBC" ) ) + return EVP_des_cbc(); + else if( cipherName.equalsAscii( "DES" ) && keyBits == 56 && chainingMode.equalsAscii( "ChainingModeCFB" ) ) + return EVP_des_cfb(); + else if( cipherName.equalsAscii( "DESX" ) && keyBits == 128 && chainingMode.equalsAscii( "ChainingModeCBC" ) ) + return EVP_desx_cbc(); + else if( cipherName.equalsAscii( "3DES" ) && keyBits == 168 && chainingMode.equalsAscii( "ChainingModeCBC" ) ) + return EVP_des_ede3_cbc(); + else if( cipherName.equalsAscii( "3DES" ) && keyBits == 168 && chainingMode.equalsAscii( "ChainingModeCFB" ) ) + return EVP_des_ede3_cfb(); + else if( cipherName.equalsAscii( "3DES_112" ) && keyBits == 112 && chainingMode.equalsAscii( "ChainingModeCBC" ) ) + return EVP_des_ede_cbc(); + else if( cipherName.equalsAscii( "3DES_112" ) && keyBits == 112 && chainingMode.equalsAscii( "ChainingModeCFB" ) ) + return EVP_des_ede_cfb(); +#endif + char buffer[ 256 ]; + ::rtl::OString cipherNameUtf8 = ::rtl::OUStringToOString( cipherName, RTL_TEXTENCODING_UTF8 ); + ::rtl::OString chainingModeUtf8 = ::rtl::OUStringToOString( chainingMode, RTL_TEXTENCODING_UTF8 ); + snprintf( buffer, sizeof( buffer ), "Unsupported cipher with name=%s, keyBits=%u, chainingMode=%s", cipherNameUtf8.getStr(), keyBits, chainingModeUtf8.getStr() ); + throw Exception( OUString::createFromAscii( buffer ), Reference< XInterface >() ); +} + +// Ported from Apache POI's org.apache.poi.poifs.crypt.CryptoFunctions.hashPassword(). +static vector< sal_uInt8 > hashPassword( const OUString& password, const EVP_MD *digestAlgorithm, vector< sal_uInt8 >& salt, sal_uInt32 spinCount ) throw ( Exception ) +{ + OpenSSLDigest digest; + digest.initialize( digestAlgorithm ); + size_t digestSize = digest.digestSize(); + + // Convert to little-endian UTF-16 + vector< sal_uInt8 > passwordLE( 2 * password.getLength() ); + for ( int i = 0; i < password.getLength(); i++ ) + ByteOrderConverter::writeLittleEndian( &passwordLE[ 2 * i ], static_cast< sal_uInt16 >( password[ i ] ) ); + + vector< sal_uInt8> digestBuffer( digestSize ); + digest.update( salt.data(), salt.size() ); + digest.update( passwordLE.data(), passwordLE.size() ); + digest.final( digestBuffer.data(), NULL ); + + char iteratorBuffer[ 4 ]; + for (sal_uInt32 i = 0; i < spinCount; i++) + { + digest.initialize( digestAlgorithm ); + ByteOrderConverter::writeLittleEndian( &iteratorBuffer, i ); + digest.update( iteratorBuffer, sizeof( iteratorBuffer ) ); + digest.update( digestBuffer.data(), digestSize ); + digest.final( digestBuffer.data(), NULL ); + } + return digestBuffer; +} + +// Ported from Apache POI's org.apache.poi.poifs.crypt.CryptoFunctions.getBlock36(). +static void toBlock36( vector< sal_uInt8 >& digest, sal_uInt32 size ) +{ + if( digest.size() < size ) + { + sal_uInt32 i = digest.size(); + digest.resize( size ); + for (; i < size; i++) + digest[ i ] = 0x36; + } + else + digest.resize( size ); +} + +// Ported from Apache POI's org.apache.poi.poifs.crypt.CryptoFunctions.getBlock0(). +static void toBlock0( vector< sal_uInt8 >& digest, sal_uInt32 size ) +{ + if( digest.size() < size ) + { + sal_uInt32 i = digest.size(); + digest.resize( size ); + for (; i < size; i++) + digest[ i ] = 0; + } + else + digest.resize( size ); +} + +// Ported from Apache POI's org.apache.poi.poifs.crypt.CryptoFunctions.generateKey(). +static vector< sal_uInt8 > generateKey( const vector< sal_uInt8 >& passwordHash, + const EVP_MD *digestAlgorithm, + const vector< sal_uInt8 >& blockKey, + sal_uInt32 keySize ) + throw ( Exception ) +{ + OpenSSLDigest digest; + digest.initialize( digestAlgorithm ); + digest.update( passwordHash.data(), passwordHash.size() ); + digest.update( blockKey.data(), blockKey.size() ); + vector< sal_uInt8> key( digest.digestSize() ); + digest.final( key.data(), NULL ); + toBlock36( key, keySize ); + return key; +} + +// Ported from Apache POI's org.apache.poi.poifs.crypt.CryptoFunctions.generateIv(). +static vector< sal_uInt8> generateIv( const vector< sal_uInt8 >& salt, + sal_uInt32 blockSize ) + throw ( Exception ) +{ + vector< sal_uInt8> iv( salt ); + toBlock36( iv, blockSize ); + return iv; +} + +// Ported from Apache POI's org.apache.poi.poifs.crypt.CryptoFunctions.generateIv(). +static vector< sal_uInt8> generateIv( const EVP_MD *digestAlgorithm, + const vector< sal_uInt8 >& salt, + const vector< sal_uInt8 >& blockKey, + sal_uInt32 blockSize ) + throw ( Exception ) +{ + OpenSSLDigest digest; + digest.initialize( digestAlgorithm ); + digest.update( salt.data(), salt.size() ); + digest.update( blockKey.data(), blockKey.size() ); + vector< sal_uInt8> iv( digest.digestSize() ); + digest.final( iv.data(), NULL ); + toBlock36( iv, blockSize ); + return iv; +} + +// Ported from Apache POI's org.apache.poi.poifs.crypt.agile.AgileDecryptor.getNextBlockSize(). +static sal_uInt32 getNextBlockSize( sal_uInt32 totalSize, sal_uInt32 blockSize ) +{ + sal_uInt32 numberOfBlocks = ( totalSize + ( blockSize - 1 ) ) / blockSize; + return numberOfBlocks * blockSize; +} + +static vector< sal_uInt8 > decryptAll( const EVP_CIPHER* cipherAlgorithm, + const sal_uInt8* iv, + const sal_uInt8* key, + const sal_uInt8* encryptedData, + sal_uInt32 encryptedDataLength ) + throw ( Exception ) +{ + OpenSSLCipher cipher; + cipher.initialize( cipherAlgorithm, key, iv, 0 ); + cipher.setPadding( 0 ); + const int blockSize = cipher.blockSize(); + vector< sal_uInt8 > decryptedData( encryptedDataLength + 2*blockSize ); + + int decryptedDataLength; + cipher.update( encryptedData, encryptedDataLength, decryptedData.data(), &decryptedDataLength ); + int finalDataLength; + cipher.final( &decryptedData[ decryptedDataLength ], &finalDataLength ); + decryptedDataLength += finalDataLength; + decryptedData.resize( decryptedDataLength ); + return decryptedData; +} + +// Ported from Apache POI's org.apache.poi.poifs.crypt.agile.AgileDecryptor.hashInput(). +static vector< sal_uInt8 > hashInput( const vector< sal_uInt8 >& passwordHash, + const vector< sal_uInt8 >& salt, + const EVP_MD *digestAlgorithm, + const vector< sal_uInt8 >& blockKey, + const vector< sal_uInt8 >& inputKey, + const EVP_CIPHER *decryptionAlgorithm, + sal_uInt32 keySize, + sal_uInt32 blockSize ) + throw ( Exception ) +{ + vector< sal_uInt8 > intermediateKey = generateKey( passwordHash, digestAlgorithm, blockKey, keySize ); + vector< sal_uInt8> iv = generateIv( salt, blockSize ); + vector< sal_uInt8 > zeroedInput( inputKey.size() ); + zeroedInput = inputKey; + toBlock0( zeroedInput, getNextBlockSize( zeroedInput.size(), blockSize ) ); + return decryptAll( decryptionAlgorithm, iv.data(), intermediateKey.data(), zeroedInput.data(), zeroedInput.size() ); +} + +// Ported from Apache POI's org.apache.poi.poifs.crypt.agile.AgileDecryptor.verifyPassword(). +Sequence< NamedValue > AgileEncryptionInfo::verifyPassword( const OUString& password ) + throw ( Exception ) +{ + const EVP_MD *digestAlgorithm = toOpenSSLDigestAlgorithm( passwordKeyEncryptor.hashAlgorithm ); + vector< sal_uInt8 > passwordHash = hashPassword( password, digestAlgorithm, passwordKeyEncryptor.saltValue, passwordKeyEncryptor.spinCount ); + + static const sal_uInt8 verifierInputBlockData[] = { 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79 }; + vector< sal_uInt8 > verifierInputBlock( &verifierInputBlockData[ 0 ], &verifierInputBlockData[ sizeof( verifierInputBlockData ) ] ); + const EVP_CIPHER* cipher = toOpenSSLCipherAlgorithm( passwordKeyEncryptor.cipherAlgorithm, passwordKeyEncryptor.keyBits, passwordKeyEncryptor.cipherChaining ); + vector< sal_uInt8 > encryptedVerifierHash = hashInput( passwordHash, passwordKeyEncryptor.saltValue, digestAlgorithm, verifierInputBlock, + passwordKeyEncryptor.encryptedVerifierHashInput, cipher, passwordKeyEncryptor.keyBits, + passwordKeyEncryptor.blockSize ); + const EVP_MD *verifierDigestAlgorithm = toOpenSSLDigestAlgorithm( keyData.hashAlgorithm ); + OpenSSLDigest verifierDigest; + verifierDigest.initialize( verifierDigestAlgorithm ); + verifierDigest.update( encryptedVerifierHash.data(), encryptedVerifierHash.size() ); + encryptedVerifierHash.resize( verifierDigest.digestSize() ); + verifierDigest.final( encryptedVerifierHash.data(), NULL ); + + static const sal_uInt8 verifierHashBlockData[] = { 0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e }; + vector< sal_uInt8 > verifierHashBlock( &verifierHashBlockData[ 0 ], &verifierHashBlockData[ sizeof( verifierHashBlockData ) ] ); + vector< sal_uInt8 > verifierHashDec = hashInput( passwordHash, passwordKeyEncryptor.saltValue, digestAlgorithm, verifierHashBlock, + passwordKeyEncryptor.encryptedVerifierHashValue, cipher, passwordKeyEncryptor.keyBits, + passwordKeyEncryptor.blockSize ); + toBlock0( verifierHashDec, verifierDigest.digestSize() ); + + if( encryptedVerifierHash != verifierHashDec ) + return Sequence< NamedValue >(); + + // Password is correct. Decrypt and store the encryption key. + static const sal_uInt8 cryptoKeyBlockData[] = { 0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6 }; + vector< sal_uInt8 > cryptoKeyBlock( &cryptoKeyBlockData[ 0 ], &cryptoKeyBlockData[ sizeof( cryptoKeyBlockData ) ] ); + encryptionKey = hashInput( passwordHash, passwordKeyEncryptor.saltValue, digestAlgorithm, cryptoKeyBlock, + passwordKeyEncryptor.encryptedKeyValue, cipher, passwordKeyEncryptor.keyBits, + passwordKeyEncryptor.blockSize ); + toBlock0( encryptionKey, passwordKeyEncryptor.keyBits / 8 ); + + // Also decrypt the dataIntegrity fields for stream validation. Note that they are optional. + if( !dataIntegrity.encryptedHmacKey.empty() && !dataIntegrity.encryptedHmacValue.empty() ) + { + const EVP_MD* keyDataDigestAlgorithm = toOpenSSLDigestAlgorithm( keyData.hashAlgorithm ); + const EVP_CIPHER* keyDataCipher = toOpenSSLCipherAlgorithm( keyData.cipherAlgorithm, keyData.keyBits, keyData.cipherChaining ); + static const sal_uInt8 integrityKeyBlockData[] = { 0x5f, 0xb2, 0xad, 0x01, 0x0c, 0xb9, 0xe1, 0xf6 }; + vector< sal_uInt8 > integrityKeyBlock( &integrityKeyBlockData[ 0 ], &integrityKeyBlockData[ sizeof( integrityKeyBlockData ) ] ); + vector< sal_uInt8 > integrityKeyIv = generateIv( keyDataDigestAlgorithm, keyData.saltValue, integrityKeyBlock, keyData.blockSize ); + hmacKey = decryptAll( keyDataCipher, integrityKeyIv.data(), encryptionKey.data(), dataIntegrity.encryptedHmacKey.data(), dataIntegrity.encryptedHmacKey.size() ); + toBlock0( hmacKey, OpenSSLDigest::digestSize( keyDataDigestAlgorithm ) ); + + static const sal_uInt8 integrityValueBlockData[] = { 0xa0, 0x67, 0x7f, 0x02, 0xb2, 0x2c, 0x84, 0x33 }; + vector< sal_uInt8 > integrityValueBlock( &integrityValueBlockData[ 0 ], &integrityValueBlockData[ sizeof( integrityValueBlockData ) ] ); + vector< sal_uInt8 > integrityValueIv = generateIv( keyDataDigestAlgorithm, keyData.saltValue, integrityValueBlock, keyData.blockSize ); + hmacValue = decryptAll( keyDataCipher, integrityValueIv.data(), encryptionKey.data(), dataIntegrity.encryptedHmacValue.data(), dataIntegrity.encryptedHmacValue.size() ); + toBlock0( hmacValue, OpenSSLDigest::digestSize( keyDataDigestAlgorithm ) ); + } + + // On success, MUST populate something into the encryption data, even though we'll never use it. + SequenceAsHashMap encryptionData; + encryptionData[ CREATE_OUSTRING( "OOXMLAgileEncryptionPasswordVerified" ) ] <<= sal_True; + return encryptionData.getAsConstNamedValueList(); +} + +bool AgileEncryptionInfo::verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData ) + throw ( Exception ) +{ + // OpenGrok shows how only main/comphelper/source/misc/docpasswordhelper.cxx calls IDocPasswordVerifier::verifyEncryptionData(), + // and only when the password is wrong and the rMediaEncData non-empty, which presumably allows other forms of encryption + // (eg. by certificate) to be used. We only support password for now. + return false; +} + +// Ported from Apache POI's org.apache.poi.poifs.crypt.agile.AgileDecryptor.initCipherForBlock(). +void AgileEncryptionInfo::decryptStream( BinaryXInputStream &aEncryptedPackage, BinaryXOutputStream &aDecryptedPackage ) + throw ( Exception ) +{ + if( encryptionKey.empty() ) + throw Exception( OUString::createFromAscii( "Encryption key not set, was the password wrong?" ), Reference< XInterface >() ); + const EVP_CIPHER* cipherAlgorithm = toOpenSSLCipherAlgorithm( keyData.cipherAlgorithm, keyData.keyBits, keyData.cipherChaining ); + const EVP_MD* digestAlgorithm = toOpenSSLDigestAlgorithm( keyData.hashAlgorithm ); + OpenSSLCipher cipher; + + const sal_uInt64 decryptedSize = aEncryptedPackage.readuInt64(); + + sal_uInt8 inputBuffer[ 4096 ]; + vector< sal_uInt8 > outputBuffer( 4096 + 2*cipher.blockSize() ); + sal_Int32 bytesIn; + int bytesOut; + int finalBytesOut; + sal_uInt64 totalBytesWritten = 0; + + vector< sal_uInt8 > blockBytes( 4 ); + bool done = false; + for ( sal_uInt32 block = 0; !done; block++ ) + { + ByteOrderConverter::writeLittleEndian( blockBytes.data(), block ); + vector< sal_uInt8 > iv = generateIv( digestAlgorithm, keyData.saltValue, blockBytes, keyData.blockSize ); + cipher.initialize( cipherAlgorithm, encryptionKey.data(), iv.data(), 0 ); + cipher.setPadding( 0 ); + + bytesIn = aEncryptedPackage.readMemory( inputBuffer, sizeof( inputBuffer ) ); + if( bytesIn > 0 ) + { + cipher.update( inputBuffer, bytesIn, outputBuffer.data(), &bytesOut ); + cipher.final( &outputBuffer[ bytesOut ], &finalBytesOut ); + bytesOut += finalBytesOut; + if( decryptedSize < (totalBytesWritten + bytesOut) ) + { + bytesOut = decryptedSize % sizeof( inputBuffer ); + done = true; + } + aDecryptedPackage.writeMemory( outputBuffer.data(), bytesOut ); + totalBytesWritten += bytesOut; + } else + done = true; + } + + aDecryptedPackage.flush(); +} + +EncryptionInfo* EncryptionInfo::readEncryptionInfo( const Reference< XComponentContext >& context, Reference< XInputStream >& inputStream ) + throw ( Exception ) +{ + sal_uInt16 nVersionMajor = readUInt16LE( inputStream ); + sal_uInt16 nVersionMinor = readUInt16LE( inputStream ); + if( ( nVersionMajor == 2 && nVersionMinor == 2 ) || + ( nVersionMajor == 3 && nVersionMinor == 2 ) || + ( nVersionMajor == 4 && nVersionMinor == 2 ) ) + { + // 2.3.4.5 Standard Encryption + BinaryXInputStream aInfoStrm( inputStream, false ); + return new StandardEncryptionInfo( aInfoStrm ); + } + else if ( nVersionMajor == 4 && nVersionMajor == 4 ) + { + // 2.3.4.10 Agile Encryption + return new AgileEncryptionInfo( context, inputStream ); + } + else + { + char msg[ 1024 ]; + snprintf( msg, sizeof( msg ), "EncryptionInfo::readEncryptionInfo() error: unsupported EncryptionVersionInfo header with major=%hu minor=%hu", + nVersionMajor, nVersionMinor ); + throw Exception( OUString::createFromAscii( msg ), Reference< XInterface >() ); + } +} + +// ============================================================================ + +} // namespace core +} // namespace oox diff --git a/main/oox/source/core/filterdetect.cxx b/main/oox/source/core/filterdetect.cxx index f36aea307a..b04ec8b760 100644 --- a/main/oox/source/core/filterdetect.cxx +++ b/main/oox/source/core/filterdetect.cxx @@ -22,12 +22,12 @@ #include "oox/core/filterdetect.hxx" +#include "oox/core/encryption.hxx" #include <com/sun/star/io/XStream.hpp> +#include <com/sun/star/logging/LogLevel.hpp> #include <comphelper/docpasswordhelper.hxx> #include <comphelper/mediadescriptor.hxx> -#include <openssl/evp.h> -#include <rtl/digest.h> #include "oox/core/fastparser.hxx" #include "oox/core/relationshandler.hxx" #include "oox/helper/attributelist.hxx" @@ -44,6 +44,7 @@ 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::logging; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::xml::sax; @@ -238,7 +239,8 @@ Reference< XInterface > SAL_CALL FilterDetect_createInstance( const Reference< X // ---------------------------------------------------------------------------- FilterDetect::FilterDetect( const Reference< XComponentContext >& rxContext ) throw( RuntimeException ) : - mxContext( rxContext, UNO_SET_THROW ) + mxContext( rxContext, UNO_SET_THROW ), + logger( rxContext ) { } @@ -246,24 +248,8 @@ FilterDetect::~FilterDetect() { } -/* =========================================================================== */ -/* 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; - // ---------------------------------------------------------------------------- bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStrm ) @@ -272,226 +258,73 @@ bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Ref return aZipStorage.isStorage(); } -// ---------------------------------------------------------------------------- - -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; -}; - -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(); -} - -// ---------------------------------------------------------------------------- - -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 ); - rtlDigestError aError = rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) ); - sal_uInt8 pnX1[ RTL_DIGEST_LENGTH_SHA1 ]; - aError = 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 ); - aError = rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) ); - sal_uInt8 pnX2[ RTL_DIGEST_LENGTH_SHA1 ]; - aError = 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 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 && nVerifierHashSize == 32 ) - { - // check password - EVP_CIPHER_CTX *aes_ctx; - aes_ctx = EVP_CIPHER_CTX_new(); - if ( aes_ctx == NULL ) - return false; - 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_free( aes_ctx ); - - aes_ctx = EVP_CIPHER_CTX_new(); - if ( aes_ctx == NULL ) - return false; - EVP_DecryptInit_ex( aes_ctx, EVP_aes_128_ecb(), 0, pnKey, 0 ); - EVP_CIPHER_CTX_set_padding( aes_ctx, 0 ); - sal_uInt8 pnTmpVerifierHash[ 32 ]; - (void) memset( pnTmpVerifierHash, 0, sizeof(pnTmpVerifierHash) ); - - /*int*/ EVP_DecryptUpdate( aes_ctx, pnTmpVerifierHash, &nOutLen, pnVerifierHash, nVerifierHashSize ); - EVP_CIPHER_CTX_free( aes_ctx ); - - rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 ); - rtlDigestError aError = rtl_digest_update( aDigest, pnTmpVerifier, sizeof( pnTmpVerifier ) ); - sal_uInt8 pnSha1Hash[ RTL_DIGEST_LENGTH_SHA1 ]; - aError = 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 ); - rtlDigestError aError = rtl_digest_update( aDigest, pnBuffer, nBufferSize ); - delete[] pnBuffer; - - size_t nHashSize = RTL_DIGEST_LENGTH_SHA1 + 4; - sal_uInt8* pnHash = new sal_uInt8[ nHashSize ]; - aError = 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 ); - aError = rtl_digest_update( aDigest, pnHash, nHashSize ); - aError = 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 ); - aError = rtl_digest_update( aDigest, pnHash, nHashSize ); - aError = 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[ CREATE_OUSTRING( "AES128EncryptionKey" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( pnKey ), nRequiredKeyLen ); - aEncryptionData[ CREATE_OUSTRING( "AES128EncryptionSalt" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( rEncrInfo.mpnSalt ), rEncrInfo.mnSaltSize ); - aEncryptionData[ CREATE_OUSTRING( "AES128EncryptionVerifier" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( rEncrInfo.mpnEncrVerifier ), sizeof( rEncrInfo.mpnEncrVerifier ) ); - aEncryptionData[ CREATE_OUSTRING( "AES128EncryptionVerifierHash" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( rEncrInfo.mpnEncrVerifierHash ), sizeof( rEncrInfo.mpnEncrVerifierHash ) ); - aResult = aEncryptionData.getAsConstNamedValueList(); - } - - return aResult; -} - // the password verifier ------------------------------------------------------ class PasswordVerifier : public ::comphelper::IDocPasswordVerifier { public: - explicit PasswordVerifier( const PackageEncryptionInfo& rEncryptInfo ); + explicit PasswordVerifier( const ::boost::shared_ptr< EncryptionInfo >& rEncryptInfo, const ::comphelper::EventLogger& rLogger ); virtual ::comphelper::DocPasswordVerifierResult verifyPassword( const OUString& rPassword, Sequence< NamedValue >& o_rEncryptionData ); virtual ::comphelper::DocPasswordVerifierResult verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData ); - inline const sal_uInt8* getKey() const { return &maKey.front(); } - private: - const PackageEncryptionInfo& mrEncryptInfo; - ::std::vector< sal_uInt8 > maKey; + const ::boost::shared_ptr< EncryptionInfo> encryptionInfo; + const ::comphelper::EventLogger logger; }; -PasswordVerifier::PasswordVerifier( const PackageEncryptionInfo& rEncryptInfo ) : - mrEncryptInfo( rEncryptInfo ), - maKey( static_cast< size_t >( rEncryptInfo.mnKeySize / 8 ), 0 ) +PasswordVerifier::PasswordVerifier( const ::boost::shared_ptr< EncryptionInfo>& rEncryptInfo, const ::comphelper::EventLogger& rLogger ) : + encryptionInfo( rEncryptInfo ), + logger( rLogger ) { } ::comphelper::DocPasswordVerifierResult PasswordVerifier::verifyPassword( const OUString& rPassword, Sequence< NamedValue >& o_rEncryptionData ) { - // verifies the password and writes the related decryption key into maKey - o_rEncryptionData = lclGenerateEncryptionKey( mrEncryptInfo, rPassword, &maKey.front(), maKey.size() ); - return o_rEncryptionData.hasElements() ? ::comphelper::DocPasswordVerifierResult_OK : ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD; + try + { + o_rEncryptionData = encryptionInfo->verifyPassword( rPassword ); + if( o_rEncryptionData.hasElements() ) + { + logger.log( LogLevel::FINE, OUString::createFromAscii( "Password is correct" ) ); + return ::comphelper::DocPasswordVerifierResult_OK; + } + else + { + logger.log( LogLevel::WARNING, OUString::createFromAscii( "Password is incorrect" ) ); + return ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD; + } + } + catch ( const Exception &e ) + { + logger.log( LogLevel::WARNING, "Error verifying password: $1$", e.Message ); + return ::comphelper::DocPasswordVerifierResult_ABORT; + } } ::comphelper::DocPasswordVerifierResult PasswordVerifier::verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData ) { - SequenceAsHashMap aHashData( rEncryptionData ); - Sequence< sal_Int8 > aKey = aHashData.getUnpackedValueOrDefault( CREATE_OUSTRING( "AES128EncryptionKey" ), Sequence< sal_Int8 >() ); - Sequence< sal_Int8 > aVerifier = aHashData.getUnpackedValueOrDefault( CREATE_OUSTRING( "AES128EncryptionVerifier" ), Sequence< sal_Int8 >() ); - Sequence< sal_Int8 > aVerifierHash = aHashData.getUnpackedValueOrDefault( CREATE_OUSTRING( "AES128EncryptionVerifierHash" ), Sequence< sal_Int8 >() ); - - bool bResult = lclCheckEncryptionData( - reinterpret_cast< const sal_uInt8* >( aKey.getConstArray() ), aKey.getLength(), - reinterpret_cast< const sal_uInt8* >( aVerifier.getConstArray() ), aVerifier.getLength(), - reinterpret_cast< const sal_uInt8* >( aVerifierHash.getConstArray() ), aVerifierHash.getLength() ); - - return bResult ? ::comphelper::DocPasswordVerifierResult_OK : ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD; + try + { + bool bResult = encryptionInfo->verifyEncryptionData( rEncryptionData ); + if( bResult ) + { + logger.log( LogLevel::FINE, OUString::createFromAscii( "EncryptionData is correct" ) ); + return ::comphelper::DocPasswordVerifierResult_OK; + } + else + { + logger.log( LogLevel::WARNING, OUString::createFromAscii( "EncryptionData is incorrect" ) ); + return ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD; + } + } + catch ( const Exception& e ) + { + logger.log( LogLevel::WARNING, "Error verifying EncryptionData: $1$", e.Message ); + return ::comphelper::DocPasswordVerifierResult_ABORT; + } } } // namespace @@ -523,20 +356,10 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript Reference< XInputStream > xEncryptedPackage( aOleStorage.openInputStream( CREATE_OUSTRING( "EncryptedPackage" ) ), UNO_SET_THROW ); // read the encryption info stream - PackageEncryptionInfo aEncryptInfo; - BinaryXInputStream aInfoStrm( xEncryptionInfo, true ); - bool bValidInfo = lclReadEncryptionInfo( aEncryptInfo, aInfoStrm ); + ::boost::shared_ptr< EncryptionInfo > encryptionInfo( EncryptionInfo::readEncryptionInfo( mxContext, xEncryptionInfo ) ); // check flags and agorithm IDs, requiered are AES128 and SHA-1 - bool bImplemented = bValidInfo && - getFlag( aEncryptInfo.mnFlags, ENCRYPTINFO_CRYPTOAPI ) && - getFlag( aEncryptInfo.mnFlags, ENCRYPTINFO_AES ) && - // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set - ((aEncryptInfo.mnAlgorithmId == 0) || (aEncryptInfo.mnAlgorithmId == ENCRYPT_ALGO_AES128)) && - // hash algorithm ID 0 defaults to SHA-1 too - ((aEncryptInfo.mnAlgorithmIdHash == 0) || (aEncryptInfo.mnAlgorithmIdHash == ENCRYPT_HASH_SHA1)) && - (aEncryptInfo.mnVerifierHashSize == 20); - + bool bImplemented = encryptionInfo->isImplemented(); if( bImplemented ) { /* "VelvetSweatshop" is the built-in default encryption @@ -550,7 +373,7 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript This helper returns either with the correct password (according to the verifier), or with an empty string if user has cancelled the password input dialog. */ - PasswordVerifier aVerifier( aEncryptInfo ); + PasswordVerifier aVerifier( encryptionInfo, logger ); Sequence< NamedValue > aEncryptionData = ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword( aVerifier, rMediaDesc, ::comphelper::DocPasswordRequestType_MS, &aDefaultPasswords ); @@ -561,34 +384,13 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript else { // create temporary file for unencrypted package - Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW ); - Reference< XStream > xTempFile( xFactory->createInstance( CREATE_OUSTRING( "com.sun.star.io.TempFile" ) ), UNO_QUERY_THROW ); + Reference< XMultiComponentFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW ); + Reference< XStream > xTempFile( xFactory->createInstanceWithContext( CREATE_OUSTRING( "com.sun.star.io.TempFile" ), mxContext ), UNO_QUERY_THROW ); Reference< XOutputStream > xDecryptedPackage( xTempFile->getOutputStream(), UNO_SET_THROW ); BinaryXOutputStream aDecryptedPackage( xDecryptedPackage, true ); BinaryXInputStream aEncryptedPackage( xEncryptedPackage, true ); - EVP_CIPHER_CTX *aes_ctx; - aes_ctx = EVP_CIPHER_CTX_new(); - if ( aes_ctx == NULL ) - throw Exception(); - EVP_DecryptInit_ex( aes_ctx, EVP_aes_128_ecb(), 0, aVerifier.getKey(), 0 ); - EVP_CIPHER_CTX_set_padding( aes_ctx, 0 ); - - sal_uInt8 pnInBuffer[ 1024 ]; - sal_uInt8 pnOutBuffer[ 1024 ]; - sal_Int32 nInLen; - int nOutLen; - aEncryptedPackage.skip( 8 ); // decrypted size - while( (nInLen = aEncryptedPackage.readMemory( pnInBuffer, sizeof( pnInBuffer ) )) > 0 ) - { - EVP_DecryptUpdate( aes_ctx, pnOutBuffer, &nOutLen, pnInBuffer, nInLen ); - aDecryptedPackage.writeMemory( pnOutBuffer, nOutLen ); - } - EVP_DecryptFinal_ex( aes_ctx, pnOutBuffer, &nOutLen ); - aDecryptedPackage.writeMemory( pnOutBuffer, nOutLen ); - - EVP_CIPHER_CTX_free( aes_ctx ); - xDecryptedPackage->flush(); + encryptionInfo->decryptStream( aEncryptedPackage, aDecryptedPackage ); aDecryptedPackage.seekToStart(); // store temp file in media descriptor to keep it alive @@ -599,9 +401,12 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript return xDecrInStrm; } } + else + logger.log( LogLevel::WARNING, "Encryption type not implemented" ); } - catch( Exception& ) + catch( Exception& e ) { + logger.log( LogLevel::WARNING, "Error in ::oox::core::FilterDetect::extractUnencryptedPackage(): $1$", e.Message ); } return Reference< XInputStream >(); @@ -665,8 +470,9 @@ OUString SAL_CALL FilterDetect::detect( Sequence< PropertyValue >& rMediaDescSeq aParser.parseStream( aZipStorage, CREATE_OUSTRING( "[Content_Types].xml" ) ); } } - catch( Exception& ) + catch( Exception& e ) { + logger.log( LogLevel::WARNING, "Error in ::oox::core::FilterDetect::detect(): $1$", e.Message ); } // write back changed media descriptor members diff --git a/main/oox/source/helper/binaryoutputstream.cxx b/main/oox/source/helper/binaryoutputstream.cxx index e92aef406f..1d1045da39 100644 --- a/main/oox/source/helper/binaryoutputstream.cxx +++ b/main/oox/source/helper/binaryoutputstream.cxx @@ -58,6 +58,18 @@ BinaryXOutputStream::~BinaryXOutputStream() close(); } +void BinaryXOutputStream::flush() +{ + if( mxOutStrm.is() ) try + { + mxOutStrm->flush(); + } + catch( Exception& ) + { + OSL_ENSURE( false, "BinaryXOutputStream::flush - flushing stream failed" ); + } +} + void BinaryXOutputStream::close() { OSL_ENSURE( !mbAutoClose || mxOutStrm.is(), "BinaryXOutputStream::close - invalid call" ); diff --git a/main/oox/source/helper/openssl_wrapper.cxx b/main/oox/source/helper/openssl_wrapper.cxx new file mode 100644 index 0000000000..76d6b4f724 --- /dev/null +++ b/main/oox/source/helper/openssl_wrapper.cxx @@ -0,0 +1,63 @@ +/************************************************************** + * + * 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 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + *************************************************************/ + + + +#include "oox/helper/openssl_wrapper.hxx" +#include "rtl/ustrbuf.hxx" + +#include <openssl/err.h> + + +namespace oox { + +// ============================================================================ + +using namespace ::com::sun::star::uno; + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; + +// ============================================================================ + +static int error_cb( const char *message, size_t len, void *userData ) +{ + OUStringBuffer* buffer = (OUStringBuffer*) userData; + buffer->appendAscii( "\n " ); + // The message often ends in its own '\n', remove this: + if( len > 0 && message[ len - 1 ] == '\n' ) + buffer->appendAscii( message, len - 1 ); + else + buffer->appendAscii( message, len ); + return 1; +} + +void throwOpenSSLException( const char *prefix ) throw ( Exception ) +{ + OUStringBuffer buffer; + buffer.appendAscii( prefix ); + ERR_print_errors_cb( error_cb, &buffer ); + throw Exception( buffer.makeStringAndClear(), Reference< XInterface >() ); +} + +// ============================================================================ + +} // namespace oox diff --git a/main/oox/source/token/namespaces.hxx.tail b/main/oox/source/token/namespaces.hxx.tail index f1303aa62a..701beb99c9 100644 --- a/main/oox/source/token/namespaces.hxx.tail +++ b/main/oox/source/token/namespaces.hxx.tail @@ -39,6 +39,8 @@ inline sal_Int32 getNamespace( sal_Int32 nToken ) { return nToken & NMSP_MASK; } #define C_TOKEN( token ) OOX_TOKEN( dmlChart, token ) #define CDR_TOKEN( token ) OOX_TOKEN( dmlChartDr, token ) #define DGM_TOKEN( token ) OOX_TOKEN( dmlDiagram, token ) +#define ENCRYPTION_TOKEN( token) OOX_TOKEN( encryption, token ) +#define KEY_ENCRYPTOR_PASSWORD_TOKEN( token ) OOX_TOKEN( keyEncryptorPassword, token ) #define O_TOKEN( token ) OOX_TOKEN( vmlOffice, token ) #define PC_TOKEN( token ) OOX_TOKEN( packageContentTypes, token ) #define PPT_TOKEN( token ) OOX_TOKEN( ppt, token ) diff --git a/main/oox/source/token/namespaces.txt b/main/oox/source/token/namespaces.txt index bd45e8f35d..5f4fbd8925 100644 --- a/main/oox/source/token/namespaces.txt +++ b/main/oox/source/token/namespaces.txt @@ -63,6 +63,11 @@ vmlOffice urn:schemas-microsoft-com:office:office vmlPowerpoint urn:schemas-microsoft-com:office:powerpoint vmlWord urn:schemas-microsoft-com:office:word +# MS Office 2010 encryption (MS-OFFCRYPTO) ------------------------------------ + +encryption http://schemas.microsoft.com/office/2006/encryption +keyEncryptorPassword http://schemas.microsoft.com/office/2006/keyEncryptor/password + # other ----------------------------------------------------------------------- ax http://schemas.microsoft.com/office/2006/activeX diff --git a/main/oox/source/token/tokens.txt b/main/oox/source/token/tokens.txt index cefe2d9c73..10ecf07bd1 100644 --- a/main/oox/source/token/tokens.txt +++ b/main/oox/source/token/tokens.txt @@ -15,8 +15,6 @@ 3TrafficLights2 3cd4 3cd8 -5cd8 -7cd8 3dDkShadow 3dLight 4Arrows @@ -30,6 +28,8 @@ 5ArrowsGray 5Quarters 5Rating +5cd8 +7cd8 A1 A3 A4 @@ -870,6 +870,7 @@ blob block blockArc blockQuote +blockSize blue blueMod blueOff @@ -1152,6 +1153,8 @@ chosung chr christmasTree chromakey +cipherAlgorithm +cipherChaining circle circleNumDbPlain circleNumWdBlackPlain @@ -1524,9 +1527,6 @@ cyan cycle cylinder d -dc -dcmitype -dcterms dLbl dLblPos dLbls @@ -1580,6 +1580,7 @@ dataDxfId dataExtractLoad dataField dataFields +dataIntegrity dataModel dataOnRows dataOnly @@ -1618,6 +1619,9 @@ dbColumn dbPr dbl dblStrike +dc +dcmitype +dcterms ddList ddeItem ddeItems @@ -2002,6 +2006,13 @@ enableRefresh enableWizard enabled encoding +encryptedHmacKey +encryptedHmacValue +encryptedKey +encryptedKeyValue +encryptedVerifierHashInput +encryptedVerifierHashValue +encryption end endA endAngle @@ -2519,7 +2530,9 @@ hardEdge harsh hasCustomPrompt hash +hashAlgorithm hashData +hashSize hc hd2 hd4 @@ -2842,6 +2855,10 @@ keepNext kern key keyAttribute +keyBits +keyData +keyEncryptor +keyEncryptors keywords khaki kinsoku @@ -4309,6 +4326,8 @@ saka salmon salt saltData +saltSize +saltValue sameClick sameDir sampData