[MS-OFFCRYPTO] convert oox implementation into UNO service

To permit pluggable crypto services, abstract existing
implementation behind an XPackageEncryption API.

Previous code already had two halfway-polymorphic classes (agile and
standard 2007 engine), so we're not adding much additional layers.

As MS crypto always uses OLE storage to wrap content into one single
file, current implementation passes all substorage names down into
XPackageEncryption APi, so different downstream implementations (e.g.
for MS RMS, or Azure AIP) are possible.

Because OleStorage classes are internal to LibO core, access is provided
via XInput/XOutput stream API function.

Change-Id: Icc32a4e0ce215090c3b739f1dcaa0654b36b7f08
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/84436
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
diff --git a/include/oox/crypto/AgileEngine.hxx b/include/oox/crypto/AgileEngine.hxx
index b4aeec6..ac02853 100644
--- a/include/oox/crypto/AgileEngine.hxx
+++ b/include/oox/crypto/AgileEngine.hxx
@@ -25,7 +25,7 @@ namespace oox {
}

namespace oox {
namespace core {
namespace crypto {

struct OOX_DLLPUBLIC AgileEncryptionInfo
{
@@ -125,7 +125,7 @@ public:

    void writeEncryptionInfo(BinaryXOutputStream& rStream) override;

    void encrypt(css::uno::Reference<css::io::XInputStream>&  rxInputStream,
    void encrypt(const css::uno::Reference<css::io::XInputStream>&  rxInputStream,
                 css::uno::Reference<css::io::XOutputStream>& rxOutputStream,
                 sal_uInt32 nSize) override;

@@ -141,7 +141,7 @@ public:
    bool setupEncryptionKey(OUString const & rPassword);
};

} // namespace core
} // namespace crypto
} // namespace oox

#endif
diff --git a/include/oox/crypto/CryptTools.hxx b/include/oox/crypto/CryptTools.hxx
index 4e8d8e58..31d90ef 100644
--- a/include/oox/crypto/CryptTools.hxx
+++ b/include/oox/crypto/CryptTools.hxx
@@ -27,7 +27,7 @@
#include <memory>

namespace oox {
namespace core {
namespace crypto {

/** Rounds up the input to the nearest multiple
 *
@@ -114,7 +114,7 @@ public:
};


} // namespace core
} // namespace crypto
} // namespace oox

#endif
diff --git a/include/oox/crypto/CryptoEngine.hxx b/include/oox/crypto/CryptoEngine.hxx
index 8a947f1..72bde89 100644
--- a/include/oox/crypto/CryptoEngine.hxx
+++ b/include/oox/crypto/CryptoEngine.hxx
@@ -25,7 +25,7 @@ namespace oox {
}

namespace oox {
namespace core {
namespace crypto {

class CryptoEngine
{
@@ -53,14 +53,14 @@ public:

    virtual bool setupEncryption(const OUString& rPassword) = 0;

    virtual void encrypt(css::uno::Reference<css::io::XInputStream> & rxInputStream,
    virtual void encrypt(const css::uno::Reference<css::io::XInputStream> & rxInputStream,
                         css::uno::Reference<css::io::XOutputStream> & rxOutputStream,
                         sal_uInt32 nSize) = 0;

    virtual bool checkDataIntegrity() = 0;
};

} // namespace core
} // namespace crypto
} // namespace oox

#endif
diff --git a/include/oox/crypto/DocumentDecryption.hxx b/include/oox/crypto/DocumentDecryption.hxx
index 7919fa7..2c05812 100644
--- a/include/oox/crypto/DocumentDecryption.hxx
+++ b/include/oox/crypto/DocumentDecryption.hxx
@@ -17,7 +17,6 @@

#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/Sequence.hxx>
#include <oox/crypto/CryptoEngine.hxx>
#include <rtl/ustring.hxx>

namespace com::sun::star {
@@ -25,29 +24,24 @@ namespace com::sun::star {
    namespace io { class XInputStream; }
    namespace io { class XStream; }
    namespace uno { class XComponentContext; }
    namespace packages { class XPackageEncryption; }
}

namespace oox::ole { class OleStorage; }

namespace oox {
namespace core {
namespace crypto {

class DocumentDecryption
{
private:
    enum CryptoType
    {
        UNKNOWN,
        STANDARD_2007,
        AGILE
    };

    oox::ole::OleStorage&           mrOleStorage;
    std::unique_ptr<CryptoEngine>   mEngine;
    CryptoType                      mCryptoType;
    css::uno::Reference< css::uno::XComponentContext > mxContext;
    oox::ole::OleStorage&                      mrOleStorage;
    css::uno::Sequence<css::beans::NamedValue> maStreamsSequence;
    css::uno::Reference< css::packages::XPackageEncryption > mxPackageEncryption;

public:
    DocumentDecryption(oox::ole::OleStorage& rOleStorage);
    DocumentDecryption(const css::uno::Reference< css::uno::XComponentContext >& rxContext, oox::ole::OleStorage& rOleStorage);

    bool decrypt(const css::uno::Reference< css::io::XStream >& xDocumentStream);
    bool readEncryptionInfo();
@@ -57,7 +51,7 @@ public:

};

} // namespace core
} // namespace crypto
} // namespace oox

#endif
diff --git a/include/oox/crypto/DocumentEncryption.hxx b/include/oox/crypto/DocumentEncryption.hxx
index 9be7c99..1748065 100644
--- a/include/oox/crypto/DocumentEncryption.hxx
+++ b/include/oox/crypto/DocumentEncryption.hxx
@@ -14,38 +14,43 @@
#include <oox/dllapi.h>

#include <com/sun/star/uno/Reference.hxx>
#include <oox/crypto/Standard2007Engine.hxx>
#include <com/sun/star/uno/Sequence.hxx>
#include <rtl/ustring.hxx>

namespace com::sun::star {
    namespace io { class XStream; }
    namespace packages { class XPackageEncryption; }
    namespace beans { struct NamedValue; }
    namespace uno { class XComponentContext; }
}

namespace oox::ole { class OleStorage; }

namespace oox {
namespace core {
namespace crypto {

class DocumentEncryption
{
private:
    css::uno::Reference< css::uno::XComponentContext > mxContext;
    css::uno::Reference< css::io::XStream > mxDocumentStream;
    oox::ole::OleStorage& mrOleStorage;
    OUString maPassword;

    Standard2007Engine mEngine;
    css::uno::Reference< css::packages::XPackageEncryption > mxPackageEncryption;
    const css::uno::Sequence< css::beans::NamedValue >& mMediaEncData;

public:
    DocumentEncryption(
    DocumentEncryption(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
        css::uno::Reference< css::io::XStream > const & xDocumentStream,
        oox::ole::OleStorage& rOleStorage,
        const OUString& aPassword);
        const css::uno::Sequence< css::beans::NamedValue >& rMediaEncData);

    bool encrypt();

};

} // namespace core
} // namespace crypto
} // namespace oox

#endif
diff --git a/include/oox/crypto/Standard2007Engine.hxx b/include/oox/crypto/Standard2007Engine.hxx
index 7583447..4a6eaae 100644
--- a/include/oox/crypto/Standard2007Engine.hxx
+++ b/include/oox/crypto/Standard2007Engine.hxx
@@ -23,7 +23,7 @@ namespace oox {
}

namespace oox {
namespace core {
namespace crypto {

class OOX_DLLPUBLIC Standard2007Engine final : public CryptoEngine
{
@@ -45,7 +45,7 @@ public:

    bool checkDataIntegrity() override;

    void encrypt(css::uno::Reference<css::io::XInputStream>&  rxInputStream,
    void encrypt(const css::uno::Reference<css::io::XInputStream>&  rxInputStream,
                 css::uno::Reference<css::io::XOutputStream>& rxOutputStream,
                 sal_uInt32 nSize) override;

@@ -55,7 +55,7 @@ public:

};

} // namespace core
} // namespace crypto
} // namespace oox

#endif
diff --git a/include/oox/crypto/StrongEncryptionDataSpace.hxx b/include/oox/crypto/StrongEncryptionDataSpace.hxx
new file mode 100644
index 0000000..d287970
--- /dev/null
+++ b/include/oox/crypto/StrongEncryptionDataSpace.hxx
@@ -0,0 +1,76 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 */

#ifndef INCLUDED_OOX_CRYPTO_STRONGENCRYPTINDATASPACE_HXX
#define INCLUDED_OOX_CRYPTO_STRONGENCRYPTINDATASPACE_HXX

#include <oox/dllapi.h>
#include <cppuhelper/implbase.hxx>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/packages/XPackageEncryption.hpp>
#include <com/sun/star/io/XInputStream.hpp>
#include <oox/crypto/CryptoEngine.hxx>

namespace com::sun::star::uno
{
class XComponentContext;
}

namespace oox
{
namespace crypto
{
class OOX_DLLPUBLIC StrongEncryptionDataSpace final
    : public cppu::WeakImplHelper<css::lang::XServiceInfo, css::packages::XPackageEncryption>
{
    css::uno::Reference<css::uno::XComponentContext> mxContext;
    std::unique_ptr<CryptoEngine> mCryptoEngine;

    css::uno::Reference<css::io::XInputStream>
    getStream(const css::uno::Sequence<css::beans::NamedValue>& rStreams,
              const rtl::OUString sStreamName);

public:
    StrongEncryptionDataSpace(const css::uno::Reference<css::uno::XComponentContext>& rxContext);

    // Decryption

    virtual sal_Bool SAL_CALL generateEncryptionKey(const OUString& rPassword) override;
    virtual sal_Bool SAL_CALL
    readEncryptionInfo(const css::uno::Sequence<css::beans::NamedValue>& aStreams) override;
    virtual sal_Bool SAL_CALL
    decrypt(const css::uno::Reference<css::io::XInputStream>& rxInputStream,
            css::uno::Reference<css::io::XOutputStream>& rxOutputStream) override;

    virtual sal_Bool SAL_CALL checkDataIntegrity() override;

    // Encryption

    virtual css::uno::Sequence<css::beans::NamedValue>
        SAL_CALL encrypt(const css::uno::Reference<css::io::XInputStream>& rxInputStream) override;

    virtual sal_Bool SAL_CALL
    setupEncryption(const css::uno::Sequence<css::beans::NamedValue>& rMediaEncData) override;

    virtual css::uno::Sequence<css::beans::NamedValue>
        SAL_CALL createEncryptionData(const OUString& rPassword) override;

    // com.sun.star.lang.XServiceInfo
    virtual OUString SAL_CALL getImplementationName() override;
    virtual sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override;
    virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
};

} // namespace crypto
} // namespace oox

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk
index 6126268..aacbe14 100644
--- a/offapi/UnoApi_offapi.mk
+++ b/offapi/UnoApi_offapi.mk
@@ -2934,6 +2934,8 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/packages,\
	NoRawFormatException \
	WrongPasswordException \
	XDataSinkEncrSupport \
	XPackageEncryption \
	PackageEncryption \
))
$(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/packages/manifest,\
	XManifestReader \
diff --git a/offapi/com/sun/star/packages/PackageEncryption.idl b/offapi/com/sun/star/packages/PackageEncryption.idl
new file mode 100644
index 0000000..a2ab55e
--- /dev/null
+++ b/offapi/com/sun/star/packages/PackageEncryption.idl
@@ -0,0 +1,25 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
#ifndef __com_sun_star_packages_PackageEncryption_idl__
#define __com_sun_star_packages_PackageEncryption_idl__

#include <com/sun/star/packages/XPackageEncryption.idl>


module com { module sun { module star { module packages {


service PackageEncryption : XPackageEncryption;


}; }; }; };

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/offapi/com/sun/star/packages/XPackageEncryption.idl b/offapi/com/sun/star/packages/XPackageEncryption.idl
new file mode 100644
index 0000000..402c3e2
--- /dev/null
+++ b/offapi/com/sun/star/packages/XPackageEncryption.idl
@@ -0,0 +1,134 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
#ifndef __com_sun_star_packages_XPackageEncryption_idl__
#define __com_sun_star_packages_XPackageEncryption_idl__

#include <com/sun/star/uno/XInterface.idl>

#include <com/sun/star/io/XInputStream.idl>
#include <com/sun/star/io/XOutputStream.idl>


module com {  module sun {  module star {  module packages {


/** Allows to transparently plug-in crypto for PackageStreams.

    @since LibreOffice 6.5
 */
interface XPackageEncryption: com::sun::star::uno::XInterface
{
    /** Read package crypto information

        @param rStreams
        Substreams of the package (in the case of MS encryption, those
        are OLE substorage streams).

        @returns
        True if crypto info could be retrieved, and engine initialised. False otherwise.
     */
    boolean readEncryptionInfo([in] sequence < com::sun::star::beans::NamedValue > rStreams);

    /** Set or refresh encrytion key

        @param rPassword
        Optional password to use for generating encryption key.

        @returns
        True if key setup was successful. False otherwise.
     */
    boolean generateEncryptionKey([in] string rPassword);

    /** Decrypt document content

        After crypto setup via readEncryptionInfo(), pipe package bits through
        encryption engine.

        @param rxInputStream
        Input data (encrypted)

        @param rxOutputStream
        Output data (decrypted)

        @returns
        True if decryption finished without error. False otherwise.
     */
    boolean decrypt([in] com::sun::star::io::XInputStream rxInputStream,
                    [out] com::sun::star::io::XOutputStream rxOutputStream);

    /** Create key-value list of encryption meta data

        After generateEncryptionKey() succeeded in setting up crypto,
        use this method to create requisite meta data. Depending on
        underlying crypto, this can be a salt, init vector, or other
        algorithm-specific information that needs to be stored
        alongside an encrypted document

        @param rPassword
        Same password as provided to generateEncryptionKey

        @returns
        Sequence of opaque key-value pairs needed for decrypting this
        setup. Can be passed back into other instances of this service
        via setupEncryption()
     */
    sequence<com::sun::star::beans::NamedValue> createEncryptionData([in] string rPassword);

    /** Set key-value list of encryption meta data

        Use this method to setup requisite encryption meta
        data. Depending on the underlying crypto, this can be a salt, init
        vector, or other algorithm-specific information that needs to
        be stored alongside an encrypted document

        @returns
        True if encryption algo setup finished without error. False otherwise.
     */
    boolean setupEncryption([in] sequence<com::sun::star::beans::NamedValue> rMediaEncData);

    /** Encrypt given stream

        After setting up crypto via setupEncryption(), use this method to encrypt content.

        @returns
        Sequence of named output streams, specific to the crypto
        provider. The names of sequence entry denote the substream
        identifiers, if any. In the case of MS OLE storage, it's the
        substorage names.
     */
    sequence<com::sun::star::beans::NamedValue> encrypt([in] com::sun::star::io::XInputStream rxInputStream);

    /** Check if decryption meta data is valid

        Some implementations might for example check HMAC values
        here. Call this before trusting encrypted data.

        @returns
        True if decryption algo setup finished without error and
        consistency checks have passed. False otherwise.
     */
    boolean checkDataIntegrity();
};


}; }; }; };

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/Library_oox.mk b/oox/Library_oox.mk
index cc235b8..3d8b46b 100644
--- a/oox/Library_oox.mk
+++ b/oox/Library_oox.mk
@@ -102,6 +102,7 @@ $(eval $(call gb_Library_add_exception_objects,oox,\
    oox/source/crypto/DocumentEncryption \
    oox/source/crypto/DocumentDecryption \
    oox/source/crypto/Standard2007Engine \
    oox/source/crypto/StrongEncryptionDataSpace \
    oox/source/docprop/docprophandler \
    oox/source/docprop/ooxmldocpropimport \
    oox/source/drawingml/chart/axiscontext \
diff --git a/oox/qa/unit/CryptoTest.cxx b/oox/qa/unit/CryptoTest.cxx
index e1a4781..c405861 100644
--- a/oox/qa/unit/CryptoTest.cxx
+++ b/oox/qa/unit/CryptoTest.cxx
@@ -66,7 +66,7 @@ void CryptoTest::testCryptoHash()
                                    aContentString.getStr() + aContentString.getLength());
    std::vector<sal_uInt8> aKey = { 'k', 'e', 'y' };
    {
        oox::core::CryptoHash aCryptoHash(aKey, oox::core::CryptoHashType::SHA1);
        oox::crypto::CryptoHash aCryptoHash(aKey, oox::crypto::CryptoHashType::SHA1);
        aCryptoHash.update(aContent);
        std::vector<sal_uInt8> aHash = aCryptoHash.finalize();
        CPPUNIT_ASSERT_EQUAL(std::string("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"),
@@ -74,7 +74,7 @@ void CryptoTest::testCryptoHash()
    }

    {
        oox::core::CryptoHash aCryptoHash(aKey, oox::core::CryptoHashType::SHA256);
        oox::crypto::CryptoHash aCryptoHash(aKey, oox::crypto::CryptoHashType::SHA256);
        aCryptoHash.update(aContent);
        std::vector<sal_uInt8> aHash = aCryptoHash.finalize();
        CPPUNIT_ASSERT_EQUAL(
@@ -83,7 +83,7 @@ void CryptoTest::testCryptoHash()
    }

    {
        oox::core::CryptoHash aCryptoHash(aKey, oox::core::CryptoHashType::SHA512);
        oox::crypto::CryptoHash aCryptoHash(aKey, oox::crypto::CryptoHashType::SHA512);
        aCryptoHash.update(aContent);
        std::vector<sal_uInt8> aHash = aCryptoHash.finalize();
        CPPUNIT_ASSERT_EQUAL(
@@ -95,18 +95,18 @@ void CryptoTest::testCryptoHash()

void CryptoTest::testRoundUp()
{
    CPPUNIT_ASSERT_EQUAL(16, oox::core::roundUp(16, 16));
    CPPUNIT_ASSERT_EQUAL(32, oox::core::roundUp(32, 16));
    CPPUNIT_ASSERT_EQUAL(64, oox::core::roundUp(64, 16));
    CPPUNIT_ASSERT_EQUAL(16, oox::crypto::roundUp(16, 16));
    CPPUNIT_ASSERT_EQUAL(32, oox::crypto::roundUp(32, 16));
    CPPUNIT_ASSERT_EQUAL(64, oox::crypto::roundUp(64, 16));

    CPPUNIT_ASSERT_EQUAL(16, oox::core::roundUp(01, 16));
    CPPUNIT_ASSERT_EQUAL(32, oox::core::roundUp(17, 16));
    CPPUNIT_ASSERT_EQUAL(32, oox::core::roundUp(31, 16));
    CPPUNIT_ASSERT_EQUAL(16, oox::crypto::roundUp(01, 16));
    CPPUNIT_ASSERT_EQUAL(32, oox::crypto::roundUp(17, 16));
    CPPUNIT_ASSERT_EQUAL(32, oox::crypto::roundUp(31, 16));
}

void CryptoTest::testStandard2007()
{
    oox::core::Standard2007Engine aEngine;
    oox::crypto::Standard2007Engine aEngine;
    {
        aEngine.setupEncryption("Password");

@@ -173,7 +173,7 @@ void CryptoTest::testStandard2007()

void CryptoTest::testAgileEncryptionVerifier()
{
    oox::core::AgileEngine aEngine;
    oox::crypto::AgileEngine aEngine;

    OUString aPassword("Password");

@@ -200,9 +200,9 @@ void CryptoTest::testAgileEncrpytionInfoWritingAndParsing()
    { // Preset AES128 - SHA1
        SvMemoryStream aEncryptionInfo;
        {
            oox::core::AgileEngine aEngine;
            oox::crypto::AgileEngine aEngine;

            aEngine.setPreset(oox::core::AgileEncryptionPreset::AES_128_SHA1);
            aEngine.setPreset(oox::crypto::AgileEncryptionPreset::AES_128_SHA1);
            aEngine.setupEncryption(aPassword);
            aKeyDataSalt = aEngine.getInfo().keyDataSalt;

@@ -218,7 +218,7 @@ void CryptoTest::testAgileEncrpytionInfoWritingAndParsing()
        aEncryptionInfo.Seek(STREAM_SEEK_TO_BEGIN);

        {
            oox::core::AgileEngine aEngine;
            oox::crypto::AgileEngine aEngine;

            uno::Reference<io::XInputStream> xInputStream(
                new utl::OSeekableInputStreamWrapper(aEncryptionInfo));
@@ -227,7 +227,7 @@ void CryptoTest::testAgileEncrpytionInfoWritingAndParsing()

            CPPUNIT_ASSERT(aEngine.readEncryptionInfo(xInputStream));

            oox::core::AgileEncryptionInfo& rInfo = aEngine.getInfo();
            oox::crypto::AgileEncryptionInfo& rInfo = aEngine.getInfo();
            CPPUNIT_ASSERT_EQUAL(sal_Int32(100000), rInfo.spinCount);
            CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo.saltSize);
            CPPUNIT_ASSERT_EQUAL(sal_Int32(128), rInfo.keyBits);
@@ -246,9 +246,9 @@ void CryptoTest::testAgileEncrpytionInfoWritingAndParsing()
    { // Preset AES256 - SHA512
        SvMemoryStream aEncryptionInfo;
        {
            oox::core::AgileEngine aEngine;
            oox::crypto::AgileEngine aEngine;

            aEngine.setPreset(oox::core::AgileEncryptionPreset::AES_256_SHA512);
            aEngine.setPreset(oox::crypto::AgileEncryptionPreset::AES_256_SHA512);
            aEngine.setupEncryption(aPassword);
            aKeyDataSalt = aEngine.getInfo().keyDataSalt;

@@ -264,7 +264,7 @@ void CryptoTest::testAgileEncrpytionInfoWritingAndParsing()
        aEncryptionInfo.Seek(STREAM_SEEK_TO_BEGIN);

        {
            oox::core::AgileEngine aEngine;
            oox::crypto::AgileEngine aEngine;

            uno::Reference<io::XInputStream> xInputStream(
                new utl::OSeekableInputStreamWrapper(aEncryptionInfo));
@@ -273,7 +273,7 @@ void CryptoTest::testAgileEncrpytionInfoWritingAndParsing()

            CPPUNIT_ASSERT(aEngine.readEncryptionInfo(xInputStream));

            oox::core::AgileEncryptionInfo& rInfo = aEngine.getInfo();
            oox::crypto::AgileEncryptionInfo& rInfo = aEngine.getInfo();
            CPPUNIT_ASSERT_EQUAL(sal_Int32(100000), rInfo.spinCount);
            CPPUNIT_ASSERT_EQUAL(sal_Int32(16), rInfo.saltSize);
            CPPUNIT_ASSERT_EQUAL(sal_Int32(256), rInfo.keyBits);
@@ -301,7 +301,7 @@ void CryptoTest::testAgileDataIntegrityHmacKey()

    SvMemoryStream aEncryptionInfo;
    {
        oox::core::AgileEngine aEngine;
        oox::crypto::AgileEngine aEngine;
        aEngine.setupEncryption(aPassword);
        oox::BinaryXOutputStream aBinaryEncryptionInfoOutputStream(
            new utl::OSeekableOutputStreamWrapper(aEncryptionInfo), true);
@@ -316,7 +316,7 @@ void CryptoTest::testAgileDataIntegrityHmacKey()
    aEncryptionInfo.Seek(STREAM_SEEK_TO_BEGIN);

    {
        oox::core::AgileEngine aEngine;
        oox::crypto::AgileEngine aEngine;

        uno::Reference<io::XInputStream> xInputStream(
            new utl::OSeekableInputStreamWrapper(aEncryptionInfo));
@@ -346,7 +346,7 @@ void CryptoTest::testAgileEncryptingAndDecrypting()
    OString aTestString = OUStringToOString("1234567890ABCDEFGH", RTL_TEXTENCODING_UTF8);

    {
        oox::core::AgileEngine aEngine;
        oox::crypto::AgileEngine aEngine;

        // Setup input
        SvMemoryStream aUnencryptedInput;
@@ -381,7 +381,7 @@ void CryptoTest::testAgileEncryptingAndDecrypting()
    aEncryptionInfo.Seek(STREAM_SEEK_TO_BEGIN);

    {
        oox::core::AgileEngine aEngine;
        oox::crypto::AgileEngine aEngine;

        // Read encryption info
        uno::Reference<io::XInputStream> xEncryptionInfo(
diff --git a/oox/source/core/filterdetect.cxx b/oox/source/core/filterdetect.cxx
index 4a6edbd..0ab6868 100644
--- a/oox/source/core/filterdetect.cxx
+++ b/oox/source/core/filterdetect.cxx
@@ -276,23 +276,31 @@ bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Ref
class PasswordVerifier : public IDocPasswordVerifier
{
public:
    explicit PasswordVerifier( DocumentDecryption& aDecryptor );
    explicit PasswordVerifier( crypto::DocumentDecryption& aDecryptor );

    virtual DocPasswordVerifierResult verifyPassword( const OUString& rPassword, Sequence<NamedValue>& rEncryptionData ) override;

    virtual DocPasswordVerifierResult verifyEncryptionData( const Sequence<NamedValue>& rEncryptionData ) override;
private:
    DocumentDecryption& mDecryptor;
    crypto::DocumentDecryption& mDecryptor;
};

PasswordVerifier::PasswordVerifier( DocumentDecryption& aDecryptor ) :
PasswordVerifier::PasswordVerifier( crypto::DocumentDecryption& aDecryptor ) :
    mDecryptor(aDecryptor)
{}

comphelper::DocPasswordVerifierResult PasswordVerifier::verifyPassword( const OUString& rPassword, Sequence<NamedValue>& rEncryptionData )
{
    if(mDecryptor.generateEncryptionKey(rPassword))
        rEncryptionData = mDecryptor.createEncryptionData(rPassword);
    try
    {
        if (mDecryptor.generateEncryptionKey(rPassword))
            rEncryptionData = mDecryptor.createEncryptionData(rPassword);
    }
    catch (...)
    {
        // Any exception is a reason to abort
        return comphelper::DocPasswordVerifierResult::Abort;
    }

    return rEncryptionData.hasElements() ? comphelper::DocPasswordVerifierResult::OK : comphelper::DocPasswordVerifierResult::WrongPassword;
}
@@ -326,7 +334,7 @@ Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescript
    {
        try
        {
            DocumentDecryption aDecryptor(aOleStorage);
            crypto::DocumentDecryption aDecryptor(mxContext, aOleStorage);

            if( aDecryptor.readEncryptionInfo() )
            {
diff --git a/oox/source/core/xmlfilterbase.cxx b/oox/source/core/xmlfilterbase.cxx
index 03d68fc..5c5d5e5 100644
--- a/oox/source/core/xmlfilterbase.cxx
+++ b/oox/source/core/xmlfilterbase.cxx
@@ -881,13 +881,7 @@ Reference<XStream> XmlFilterBase::implGetOutputStream( MediaDescriptor& rMediaDe
                                        MediaDescriptor::PROP_ENCRYPTIONDATA(),
                                        Sequence< NamedValue >() );

    OUString aPassword;
    auto pProp = std::find_if(aMediaEncData.begin(), aMediaEncData.end(),
        [](const NamedValue& rProp) { return rProp.Name == "OOXPassword"; });
    if (pProp != aMediaEncData.end())
        pProp->Value >>= aPassword;

    if (aPassword.isEmpty())
    if (aMediaEncData.getLength() == 0)
    {
        return FilterBase::implGetOutputStream( rMediaDescriptor );
    }
@@ -908,20 +902,13 @@ bool XmlFilterBase::implFinalizeExport( MediaDescriptor& rMediaDescriptor )
                                        MediaDescriptor::PROP_ENCRYPTIONDATA(),
                                        Sequence< NamedValue >() );

    OUString aPassword;

    auto pProp = std::find_if(aMediaEncData.begin(), aMediaEncData.end(),
        [](const NamedValue& rProp) { return rProp.Name == "OOXPassword"; });
    if (pProp != aMediaEncData.end())
        pProp->Value >>= aPassword;

    if (!aPassword.isEmpty())
    if (aMediaEncData.getLength())
    {
        commitStorage();

        Reference< XStream> xDocumentStream (FilterBase::implGetOutputStream(rMediaDescriptor));
        oox::ole::OleStorage aOleStorage( getComponentContext(), xDocumentStream, true );
        DocumentEncryption encryptor(getMainDocumentStream(), aOleStorage, aPassword);
        crypto::DocumentEncryption encryptor( getComponentContext(), getMainDocumentStream(), aOleStorage, aMediaEncData );
        bRet = encryptor.encrypt();
        if (bRet)
            aOleStorage.commit();
diff --git a/oox/source/crypto/AgileEngine.cxx b/oox/source/crypto/AgileEngine.cxx
index d8c184c..f4eb9d2 100644
--- a/oox/source/crypto/AgileEngine.cxx
+++ b/oox/source/crypto/AgileEngine.cxx
@@ -40,7 +40,7 @@ using namespace css::uno;
using namespace css::xml::sax;
using namespace css::xml;

namespace oox::core {
namespace oox::crypto {

namespace {

@@ -585,7 +585,7 @@ bool AgileEngine::encryptHmacKey()
        return false;

    // Encrypted salt must be multiple of block size
    sal_Int32 nEncryptedSaltSize = oox::core::roundUp(mInfo.hashSize, mInfo.blockSize);
    sal_Int32 nEncryptedSaltSize = oox::crypto::roundUp(mInfo.hashSize, mInfo.blockSize);

    // We need to extend hmacSalt to multiple of block size, padding with 0x36
    std::vector<sal_uInt8> extendedSalt(mInfo.hmacKey);
@@ -759,7 +759,7 @@ void AgileEngine::writeEncryptionInfo(BinaryXOutputStream & rStream)
    rStream.writeMemory(aMemStream.GetData(), aMemStream.GetSize());
}

void AgileEngine::encrypt(css::uno::Reference<css::io::XInputStream> &  rxInputStream,
void AgileEngine::encrypt(const css::uno::Reference<css::io::XInputStream> &  rxInputStream,
                          css::uno::Reference<css::io::XOutputStream> & rxOutputStream,
                          sal_uInt32 nSize)
{
@@ -799,7 +799,7 @@ void AgileEngine::encrypt(css::uno::Reference<css::io::XInputStream> &  rxInputS
    while ((inputLength = aBinaryInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0)
    {
        sal_uInt32 correctedInputLength = inputLength % mInfo.blockSize == 0 ?
                        inputLength : oox::core::roundUp(inputLength, sal_uInt32(mInfo.blockSize));
                        inputLength : oox::crypto::roundUp(inputLength, sal_uInt32(mInfo.blockSize));

        // Update Key
        sal_uInt8* segmentBegin = reinterpret_cast<sal_uInt8*>(&nSegment);
@@ -822,6 +822,6 @@ void AgileEngine::encrypt(css::uno::Reference<css::io::XInputStream> &  rxInputS
    encryptHmacValue();
}

} // namespace oox::core
} // namespace oox::crypto

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/crypto/CryptTools.cxx b/oox/source/crypto/CryptTools.cxx
index cc8c5a9..ff11ebb 100644
--- a/oox/source/crypto/CryptTools.cxx
+++ b/oox/source/crypto/CryptTools.cxx
@@ -24,7 +24,7 @@
#include <pk11pub.h>
#endif // USE_TLS_NSS

namespace oox::core {
namespace oox::crypto {

#if USE_TLS_OPENSSL
struct CryptoImpl
@@ -478,6 +478,6 @@ std::vector<sal_uInt8> CryptoHash::finalize()
    return aHash;
}

} // namespace oox::core
} // namespace oox::crypto

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/crypto/DocumentDecryption.cxx b/oox/source/crypto/DocumentDecryption.cxx
index 1ed26f4..45b820a 100644
--- a/oox/source/crypto/DocumentDecryption.cxx
+++ b/oox/source/crypto/DocumentDecryption.cxx
@@ -13,26 +13,89 @@
#include <comphelper/sequenceashashmap.hxx>

#include <com/sun/star/beans/NamedValue.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/io/XStream.hpp>
#include <oox/crypto/AgileEngine.hxx>
#include <oox/crypto/Standard2007Engine.hxx>
#include <oox/helper/binaryinputstream.hxx>
#include <oox/helper/binaryoutputstream.hxx>
#include <com/sun/star/io/IOException.hpp>
#include <com/sun/star/packages/XPackageEncryption.hpp>
#include <oox/ole/olestorage.hxx>
#include <oox/helper/binaryinputstream.hxx>
#include <filter/msfilter/mscodec.hxx>

namespace oox::core {
#include <com/sun/star/task/PasswordRequestMode.hpp>
#include <comphelper/docpasswordrequest.hxx>
#include <comphelper/stillreadwriteinteraction.hxx>
#include <com/sun/star/task/InteractionHandler.hpp>
#include <com/sun/star/task/PasswordContainer.hpp>
#include <com/sun/star/task/XInteractionHandler.hpp>

#include <sal/log.hxx>

namespace {

void lcl_getListOfStreams(oox::StorageBase* pStorage, std::vector<OUString>& rElementNames)
{
    std::vector< OUString > oElementNames;
    pStorage->getElementNames(oElementNames);
    for (const auto & sName : oElementNames)
    {
        oox::StorageRef rSubStorage = pStorage->openSubStorage(sName, false);
        if (rSubStorage && rSubStorage->isStorage())
        {
            lcl_getListOfStreams(rSubStorage.get(), rElementNames);
        }
        else
        {
            if (pStorage->isRootStorage())
                rElementNames.push_back(sName);
            else
                rElementNames.push_back(pStorage->getPath() + "/" + sName);
        }
    }
}

}

namespace oox::crypto {

using namespace css;

DocumentDecryption::DocumentDecryption(oox::ole::OleStorage& rOleStorage) :
    mrOleStorage(rOleStorage),
    mCryptoType(UNKNOWN)
{}
DocumentDecryption::DocumentDecryption(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
    oox::ole::OleStorage& rOleStorage) :
    mxContext(rxContext),
    mrOleStorage(rOleStorage)
{
    // Get OLE streams into sequences for later use in CryptoEngine
    std::vector< OUString > aStreamNames;
    lcl_getListOfStreams(&mrOleStorage, aStreamNames);

    comphelper::SequenceAsHashMap aStreamsData;
    for (const auto & sStreamName : aStreamNames)
    {
        uno::Reference<io::XInputStream> xStream = mrOleStorage.openInputStream(sStreamName);
        if (!xStream.is())
            throw io::IOException( "Cannot open OLE input stream for " + sStreamName + "!" );

        BinaryXInputStream aBinaryInputStream(xStream, true);

        css::uno::Sequence< sal_Int8 > oData;
        sal_Int32 nStreamSize = aBinaryInputStream.size();
        sal_Int32 nReadBytes = aBinaryInputStream.readData(oData, nStreamSize);

        if (nStreamSize != nReadBytes)
        {
            SAL_WARN("oox", "OLE stream invalid content");
            throw io::IOException( "OLE stream invalid content for " + sStreamName + "!" );
        }

        aStreamsData[sStreamName] <<= oData;
    }
    maStreamsSequence = aStreamsData.getAsConstNamedValueList();
}

bool DocumentDecryption::generateEncryptionKey(const OUString& rPassword)
{
    if (mEngine)
        return mEngine->generateEncryptionKey(rPassword);
    if (mxPackageEncryption.is())
        return mxPackageEncryption->generateEncryptionKey(rPassword);
    return false;
}

@@ -41,45 +104,70 @@ bool DocumentDecryption::readEncryptionInfo()
    if (!mrOleStorage.isStorage())
        return false;

    uno::Reference<io::XInputStream> xEncryptionInfo = mrOleStorage.openInputStream("EncryptionInfo");
    // Read 0x6DataSpaces/DataSpaceMap
    uno::Reference<io::XInputStream> xDataSpaceMap = mrOleStorage.openInputStream("\006DataSpaces/DataSpaceMap");
    OUString sDataSpaceName;

    BinaryXInputStream aBinaryInputStream(xEncryptionInfo, true);
    sal_uInt32 aVersion = aBinaryInputStream.readuInt32();

    switch (aVersion)
    if (xDataSpaceMap.is())
    {
        case msfilter::VERSION_INFO_2007_FORMAT:
        case msfilter::VERSION_INFO_2007_FORMAT_SP2:
            mCryptoType = STANDARD_2007; // Set encryption info format
            mEngine.reset(new Standard2007Engine);
            break;
        case msfilter::VERSION_INFO_AGILE:
            mCryptoType = AGILE; // Set encryption info format
            mEngine.reset(new AgileEngine);
            break;
        default:
            break;
        BinaryXInputStream aDataSpaceStream(xDataSpaceMap, true);
        sal_uInt32 aHeaderLength = aDataSpaceStream.readuInt32();
        SAL_WARN_IF(aHeaderLength != 8, "oox", "DataSpaceMap length != 8 is not supported. Some content may be skipped");
        sal_uInt32 aEntryCount = aDataSpaceStream.readuInt32();
        SAL_WARN_IF(aEntryCount != 1, "oox", "DataSpaceMap contains more than one entry. Some content may be skipped");

        // Read each DataSpaceMapEntry (MS-OFFCRYPTO 2.1.6.1)
        for (sal_uInt32 i = 0; i < aEntryCount; i++)
        {
            // entryLen unused for the moment
            aDataSpaceStream.skip(sizeof(sal_uInt32));

            // Read each DataSpaceReferenceComponent (MS-OFFCRYPTO 2.1.6.2)
            sal_uInt32 aReferenceComponentCount = aDataSpaceStream.readuInt32();
            for (sal_uInt32 j = 0; j < aReferenceComponentCount; j++)
            {
                // Read next reference component
                // refComponentType unused for the moment
                aDataSpaceStream.skip(sizeof(sal_uInt32));
                sal_uInt32 aReferenceComponentNameLength = aDataSpaceStream.readuInt32();
                // sReferenceComponentName unused for the moment
                aDataSpaceStream.readUnicodeArray(aReferenceComponentNameLength / 2);
                aDataSpaceStream.skip((4 - (aReferenceComponentNameLength & 3)) & 3);  // Skip padding
            }

            sal_uInt32 aDataSpaceNameLength = aDataSpaceStream.readuInt32();
            sDataSpaceName = aDataSpaceStream.readUnicodeArray(aDataSpaceNameLength / 2);
            aDataSpaceStream.skip((4 - (aDataSpaceNameLength & 3)) & 3);  // Skip padding
        }
    }
    if (mEngine)
        return mEngine->readEncryptionInfo(xEncryptionInfo);
    return false;
    else
    {
        // Fallback for documents generated by LO: they sometimes do not have all
        // required by MS-OFFCRYPTO specification streams (0x6DataSpaces/DataSpaceMap and others)
        SAL_WARN("oox", "Encrypted package does not contain DataSpaceMap");
        sDataSpaceName = "StrongEncryptionDataSpace";
    }

    uno::Sequence< uno::Any > aArguments;
    mxPackageEncryption.set(
        mxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
            "com.sun.star.comp.oox.crypto." + sDataSpaceName, aArguments, mxContext), css::uno::UNO_QUERY);

    if (!mxPackageEncryption.is())
    {
        // we do not know how to decrypt this document
        return false;
    }

    return mxPackageEncryption->readEncryptionInfo(maStreamsSequence);
}

uno::Sequence<beans::NamedValue> DocumentDecryption::createEncryptionData(const OUString& rPassword)
{
    comphelper::SequenceAsHashMap aEncryptionData;
    if (!mxPackageEncryption.is())
        return uno::Sequence<beans::NamedValue>();

    if (mCryptoType == AGILE)
    {
        aEncryptionData["CryptoType"] <<= OUString("Agile");
    }
    else if (mCryptoType == STANDARD_2007)
    {
        aEncryptionData["CryptoType"] <<= OUString("Standard");
    }

    aEncryptionData["OOXPassword"] <<= rPassword;
    return aEncryptionData.getAsConstNamedValueList();
    return mxPackageEncryption->createEncryptionData(rPassword);
}

bool DocumentDecryption::decrypt(const uno::Reference<io::XStream>& xDocumentStream)
@@ -89,25 +177,26 @@ bool DocumentDecryption::decrypt(const uno::Reference<io::XStream>& xDocumentStr
    if (!mrOleStorage.isStorage())
        return false;

    if (!mxPackageEncryption.is())
        return false;

    // open the required input streams in the encrypted package
    uno::Reference<io::XInputStream> xEncryptedPackage = mrOleStorage.openInputStream("EncryptedPackage");

    // create temporary file for unencrypted package
    uno::Reference<io::XOutputStream> xDecryptedPackage = xDocumentStream->getOutputStream();
    BinaryXOutputStream aDecryptedPackage(xDecryptedPackage, true);
    BinaryXInputStream aEncryptedPackage(xEncryptedPackage, true);

    bResult = mEngine->decrypt(aEncryptedPackage, aDecryptedPackage);
    bResult = mxPackageEncryption->decrypt(xEncryptedPackage, xDecryptedPackage);

    xDecryptedPackage->flush();
    aDecryptedPackage.seekToStart();
    css::uno::Reference<io::XSeekable> xSeekable(xDecryptedPackage, css::uno::UNO_QUERY);
    xSeekable->seek(0);

    if (bResult)
        return mEngine->checkDataIntegrity();
        return mxPackageEncryption->checkDataIntegrity();

    return bResult;
}

} // namespace oox::core
} // namespace oox::crypto

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/crypto/DocumentEncryption.cxx b/oox/source/crypto/DocumentEncryption.cxx
index 1ea421c..6b88549 100644
--- a/oox/source/crypto/DocumentEncryption.cxx
+++ b/oox/source/crypto/DocumentEncryption.cxx
@@ -14,58 +14,90 @@
#include <com/sun/star/io/XOutputStream.hpp>
#include <com/sun/star/io/XStream.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/packages/XPackageEncryption.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>

#include <oox/helper/binaryoutputstream.hxx>
#include <oox/ole/olestorage.hxx>
#include <sal/log.hxx>

namespace oox::core {
namespace oox::crypto {

using namespace css::io;
using namespace css::uno;
using namespace css::beans;

DocumentEncryption::DocumentEncryption(Reference<XStream> const & xDocumentStream,
DocumentEncryption::DocumentEncryption(const Reference< XComponentContext >& rxContext,
                                       Reference<XStream> const & xDocumentStream,
                                       oox::ole::OleStorage& rOleStorage,
                                       const OUString& rPassword)
    : mxDocumentStream(xDocumentStream)
                                       const Sequence<NamedValue>& rMediaEncData)
    : mxContext(rxContext)
    , mxDocumentStream(xDocumentStream)
    , mrOleStorage(rOleStorage)
    , maPassword(rPassword)
{}
    , mMediaEncData(rMediaEncData)
{
    // Select engine
    for (int i = 0; i < rMediaEncData.getLength(); i++)
    {
        if (rMediaEncData[i].Name == "CryptoType")
        {
            OUString sCryptoType;
            rMediaEncData[i].Value >>= sCryptoType;

            if (sCryptoType == "Standard")
                sCryptoType = "StrongEncryptionDataSpace";

            Sequence<Any> aArguments;
            mxPackageEncryption.set(
                mxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
                    "com.sun.star.comp.oox.crypto." + sCryptoType, aArguments, mxContext), css::uno::UNO_QUERY);

            if (!mxPackageEncryption.is())
            {
                SAL_WARN("oox", "Requested encryption method \"" << sCryptoType << "\" is not supported");
            }

            break;
        }
    }
}

bool DocumentEncryption::encrypt()
{
    if (!mxPackageEncryption.is())
        return false;

    Reference<XInputStream> xInputStream (mxDocumentStream->getInputStream(), UNO_SET_THROW);
    Reference<XSeekable> xSeekable(xInputStream, UNO_QUERY);

    if (!xSeekable.is())
        return false;

    sal_uInt32 aLength = xSeekable->getLength(); // check length of the stream
    xSeekable->seek(0); // seek to begin of the document stream

    if (!mrOleStorage.isStorage())
        return false;

    mEngine.setupEncryption(maPassword);
    mxPackageEncryption->setupEncryption(mMediaEncData);

    Reference<XOutputStream> xOutputStream(mrOleStorage.openOutputStream("EncryptedPackage"), UNO_SET_THROW);
    Sequence<NamedValue> aStreams = mxPackageEncryption->encrypt(xInputStream);

    mEngine.encrypt(xInputStream, xOutputStream, aLength);
    for (const NamedValue & aStream : std::as_const(aStreams))
    {
        Reference<XOutputStream> xOutputStream(mrOleStorage.openOutputStream(aStream.Name), UNO_SET_THROW);
        BinaryXOutputStream aBinaryOutputStream(xOutputStream, true);

    xOutputStream->flush();
    xOutputStream->closeOutput();
        css::uno::Sequence<sal_Int8> aStreamSequence;
        aStream.Value >>= aStreamSequence;

    Reference<XOutputStream> xEncryptionInfo(mrOleStorage.openOutputStream("EncryptionInfo"), UNO_SET_THROW);
    BinaryXOutputStream aEncryptionInfoBinaryOutputStream(xEncryptionInfo, false);
        aBinaryOutputStream.writeData(aStreamSequence);

    mEngine.writeEncryptionInfo(aEncryptionInfoBinaryOutputStream);

    aEncryptionInfoBinaryOutputStream.close();
    xEncryptionInfo->flush();
    xEncryptionInfo->closeOutput();
        aBinaryOutputStream.close();
    }

    return true;
}

} // namespace oox::core
} // namespace oox::crypto

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/crypto/Standard2007Engine.cxx b/oox/source/crypto/Standard2007Engine.cxx
index 0ad7825..2aaf6f4 100644
--- a/oox/source/crypto/Standard2007Engine.cxx
+++ b/oox/source/crypto/Standard2007Engine.cxx
@@ -17,7 +17,7 @@

#include <comphelper/hash.hxx>

namespace oox::core {
namespace oox::crypto {

/* =========================================================================== */
/*  Kudos to Caolan McNamara who provided the core decryption implementations. */
@@ -228,7 +228,7 @@ void Standard2007Engine::writeEncryptionInfo(BinaryXOutputStream& rStream)
    rStream.writeMemory(&mInfo.verifier, sizeof(msfilter::EncryptionVerifierAES));
}

void Standard2007Engine::encrypt(css::uno::Reference<css::io::XInputStream> &  rxInputStream,
void Standard2007Engine::encrypt(const css::uno::Reference<css::io::XInputStream> &  rxInputStream,
                                 css::uno::Reference<css::io::XOutputStream> & rxOutputStream,
                                 sal_uInt32 nSize)
{
@@ -316,6 +316,6 @@ bool Standard2007Engine::readEncryptionInfo(css::uno::Reference<css::io::XInputS
    return !aBinaryStream.isEof();
}

} // namespace oox::core
} // namespace oox::crypto

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/crypto/StrongEncryptionDataSpace.cxx b/oox/source/crypto/StrongEncryptionDataSpace.cxx
new file mode 100644
index 0000000..2e21a89
--- /dev/null
+++ b/oox/source/crypto/StrongEncryptionDataSpace.cxx
@@ -0,0 +1,206 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 */

#include <oox/crypto/StrongEncryptionDataSpace.hxx>
#include <oox/crypto/AgileEngine.hxx>
#include <oox/crypto/Standard2007Engine.hxx>
#include <oox/helper/binaryoutputstream.hxx>
#include <oox/helper/binaryinputstream.hxx>
#include <com/sun/star/io/SequenceInputStream.hpp>
#include <com/sun/star/io/XSequenceOutputStream.hpp>

#include <comphelper/sequenceashashmap.hxx>
#include <cppuhelper/supportsservice.hxx>

using namespace css;
using namespace css::beans;
using namespace css::io;
using namespace css::lang;
using namespace css::uno;

namespace oox
{
namespace crypto
{
StrongEncryptionDataSpace::StrongEncryptionDataSpace(const Reference<XComponentContext>& rxContext)
    : mxContext(rxContext)
    , mCryptoEngine(new Standard2007Engine)
{
}

sal_Bool StrongEncryptionDataSpace::generateEncryptionKey(const OUString& rPassword)
{
    if (!mCryptoEngine)
        return false;

    return mCryptoEngine->generateEncryptionKey(rPassword);
}

sal_Bool StrongEncryptionDataSpace::checkDataIntegrity()
{
    if (!mCryptoEngine)
        return false;

    return mCryptoEngine->checkDataIntegrity();
}

sal_Bool StrongEncryptionDataSpace::decrypt(const Reference<XInputStream>& rxInputStream,
                                            Reference<XOutputStream>& rxOutputStream)
{
    if (!mCryptoEngine)
        return false;

    BinaryXInputStream aInputStream(rxInputStream, true);
    BinaryXOutputStream aOutputStream(rxOutputStream, true);

    mCryptoEngine->decrypt(aInputStream, aOutputStream);

    rxOutputStream->flush();
    return true;
}

Reference<XInputStream> StrongEncryptionDataSpace::getStream(const Sequence<NamedValue>& rStreams,
                                                             const OUString sStreamName)
{
    for (const auto& aStream : rStreams)
    {
        if (aStream.Name == sStreamName)
        {
            Sequence<sal_Int8> aSeq;
            aStream.Value >>= aSeq;
            Reference<XInputStream> aStream2(
                io::SequenceInputStream::createStreamFromSequence(mxContext, aSeq),
                UNO_QUERY_THROW);
            return aStream2;
        }
    }
    return nullptr;
}

sal_Bool StrongEncryptionDataSpace::readEncryptionInfo(const Sequence<NamedValue>& aStreams)
{
    Reference<XInputStream> xEncryptionInfo = getStream(aStreams, "EncryptionInfo");
    if (!xEncryptionInfo.is())
        return false;

    BinaryXInputStream aBinaryInputStream(xEncryptionInfo, true);
    sal_uInt32 aVersion = aBinaryInputStream.readuInt32();

    switch (aVersion)
    {
        case msfilter::VERSION_INFO_2007_FORMAT:
        case msfilter::VERSION_INFO_2007_FORMAT_SP2:
            mCryptoEngine.reset(new Standard2007Engine);
            break;
        case msfilter::VERSION_INFO_AGILE:
            mCryptoEngine.reset(new AgileEngine());
            break;
        default:
            break;
    }

    if (!mCryptoEngine)
        return false;

    return mCryptoEngine->readEncryptionInfo(xEncryptionInfo);
}

sal_Bool StrongEncryptionDataSpace::setupEncryption(const Sequence<NamedValue>& rMediaEncData)
{
    if (!mCryptoEngine)
        return false;

    OUString sPassword;
    for (const auto& aParam : rMediaEncData)
    {
        if (aParam.Name == "OOXPassword")
        {
            aParam.Value >>= sPassword;
        }
    }

    return mCryptoEngine->setupEncryption(sPassword);
}

Sequence<NamedValue> StrongEncryptionDataSpace::createEncryptionData(const OUString& rPassword)
{
    comphelper::SequenceAsHashMap aEncryptionData;
    aEncryptionData["OOXPassword"] <<= rPassword;
    aEncryptionData["CryptoType"] <<= OUString("StrongEncryptionDataSpace");

    return aEncryptionData.getAsConstNamedValueList();
}

Sequence<NamedValue>
StrongEncryptionDataSpace::encrypt(const Reference<XInputStream>& rxInputStream)
{
    if (!mCryptoEngine)
        return Sequence<NamedValue>();

    Reference<XSeekable> xSeekable(rxInputStream, UNO_QUERY);
    if (!xSeekable.is())
        return Sequence<NamedValue>();

    sal_uInt32 aLength = xSeekable->getLength(); // check length of the stream

    Reference<XOutputStream> xOutputStream(
        mxContext->getServiceManager()->createInstanceWithContext(
            "com.sun.star.io.SequenceOutputStream", mxContext),
        UNO_QUERY);

    mCryptoEngine->encrypt(rxInputStream, xOutputStream, aLength);

    comphelper::SequenceAsHashMap aStreams;

    Reference<XSequenceOutputStream> xEncodedFileSequenceStream(xOutputStream, UNO_QUERY);
    aStreams["EncryptedPackage"] <<= xEncodedFileSequenceStream->getWrittenBytes();

    Reference<XOutputStream> aEncryptionInfoStream(
        mxContext->getServiceManager()->createInstanceWithContext(
            "com.sun.star.io.SequenceOutputStream", mxContext),
        UNO_QUERY);
    BinaryXOutputStream rStream(aEncryptionInfoStream, false);
    mCryptoEngine->writeEncryptionInfo(rStream);
    aEncryptionInfoStream->flush();
    Reference<XSequenceOutputStream> aEncryptionInfoSequenceStream(aEncryptionInfoStream,
                                                                   UNO_QUERY);

    aStreams["EncryptionInfo"] <<= aEncryptionInfoSequenceStream->getWrittenBytes();

    return aStreams.getAsConstNamedValueList();
}

OUString SAL_CALL StrongEncryptionDataSpace::getImplementationName()
{
    return "com.sun.star.comp.oox.crypto.StrongEncryptionDataSpace";
}

sal_Bool SAL_CALL StrongEncryptionDataSpace::supportsService(const OUString& rServiceName)
{
    return cppu::supportsService(this, rServiceName);
}

css::uno::Sequence<OUString> SAL_CALL StrongEncryptionDataSpace::getSupportedServiceNames()
{
    Sequence<OUString> aServices{ "com.sun.star.packages.PackageEncryption" };
    return aServices;
}

} // namespace crypto
} // namespace oox

extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
com_sun_star_comp_oox_crypto_StrongEncryptionDataSpace_get_implementation(
    uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
{
    return cppu::acquire(new oox::crypto::StrongEncryptionDataSpace(pCtx));
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/util/oox.component b/oox/util/oox.component
index 32a8100..ef54da9 100644
--- a/oox/util/oox.component
+++ b/oox/util/oox.component
@@ -40,4 +40,8 @@
    constructor="com_sun_star_comp_oox_ShapeContextHandler_get_implementation">
    <service name="com.sun.star.xml.sax.FastShapeContextHandler"/>
  </implementation>
  <implementation name="com.sun.star.comp.oox.crypto.StrongEncryptionDataSpace"
    constructor="com_sun_star_comp_oox_crypto_StrongEncryptionDataSpace_get_implementation">
    <service name="com.sun.star.packages.PackageEncryption"/>
  </implementation>
</component>
diff --git a/sfx2/source/dialog/filedlghelper.cxx b/sfx2/source/dialog/filedlghelper.cxx
index e390338..1400bf7 100644
--- a/sfx2/source/dialog/filedlghelper.cxx
+++ b/sfx2/source/dialog/filedlghelper.cxx
@@ -2697,7 +2697,8 @@ ErrCode RequestPassword(const std::shared_ptr<const SfxFilter>& pCurrentFilter, 
                if (bOOXML)
                {
                    ::comphelper::SequenceAsHashMap aHashData;
                    aHashData[ OUString( "OOXPassword"  ) ] <<= pPasswordRequest->getPassword();
                    aHashData[ OUString( "OOXPassword" ) ] <<= pPasswordRequest->getPassword();
                    aHashData[ OUString( "CryptoType" ) ] <<= OUString( "Standard" );
                    aEncryptionData = aHashData.getAsConstNamedValueList();
                }
                else
diff --git a/solenv/bin/native-code.py b/solenv/bin/native-code.py
index f5012b2..d2165c1 100755
--- a/solenv/bin/native-code.py
+++ b/solenv/bin/native-code.py
@@ -358,6 +358,7 @@ core_constructor_list = [
    "com_sun_star_comp_oox_docprop_DocumentPropertiesImporter_get_implementation",
    "com_sun_star_comp_oox_ppt_PowerPointImport_get_implementation",
    "com_sun_star_comp_oox_ShapeContextHandler_get_implementation",
    "com_sun_star_comp_oox_crypto_StrongEncryptionDataSpace_get_implementation",
    ]

# edit group for apps, where you can edit documents
diff --git a/sw/qa/inc/swmodeltestbase.hxx b/sw/qa/inc/swmodeltestbase.hxx
index c6cbf9f..32db863 100644
--- a/sw/qa/inc/swmodeltestbase.hxx
+++ b/sw/qa/inc/swmodeltestbase.hxx
@@ -808,6 +808,7 @@ protected:
            {
                OUString sPassword = OUString::createFromAscii(pPassword);
                css::uno::Sequence<css::beans::NamedValue> aEncryptionData {
                    { "CryptoType", css::uno::makeAny(OUString("Standard")) },
                    { "OOXPassword", css::uno::makeAny(sPassword) }
                };
                aMediaDescriptor[utl::MediaDescriptor::PROP_ENCRYPTIONDATA()] <<= aEncryptionData;
diff --git a/unotools/source/misc/mediadescriptor.cxx b/unotools/source/misc/mediadescriptor.cxx
index 1b830fb..7f8f4e3 100644
--- a/unotools/source/misc/mediadescriptor.cxx
+++ b/unotools/source/misc/mediadescriptor.cxx
@@ -473,8 +473,9 @@ css::uno::Sequence< css::beans::NamedValue > MediaDescriptor::requestAndVerifyDo
    erase( PROP_PASSWORD() );
    erase( PROP_ENCRYPTIONDATA() );

    // insert valid password into media descriptor (but not a default password)
    if( aEncryptionData.hasElements() && !bIsDefaultPassword )
    // insert encryption info into media descriptor
    // TODO
    if( aEncryptionData.hasElements() )
        (*this)[ PROP_ENCRYPTIONDATA() ] <<= aEncryptionData;

    return aEncryptionData;