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


Reply via email to