This is an automated email from the ASF dual-hosted git repository.
damjan pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/openoffice.git
The following commit(s) were added to refs/heads/trunk by this push:
new 506fa58b19 Implement the (MS Office 2010+) OOXML "Agile encryption"
support, so that we can open such password-protected OOXML files.
506fa58b19 is described below
commit 506fa58b1970084a0caacb50b3a805e469be4756
Author: Damjan Jovanovic <[email protected]>
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
---
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 e17cedc56f..43d0de4cc7 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 ([email protected])
+
+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