[Libreoffice-commits] core.git: Branch 'feature/cib_contract3756' - include/oox oox/Library_oox.mk oox/source unotools/source

Vasily Melenchuk (via logerrit) logerrit at kemper.freedesktop.org
Wed Sep 18 10:40:12 UTC 2019


 include/oox/crypto/AgileEngine.hxx        |    6 
 include/oox/crypto/CryptoEngine.hxx       |    7 
 include/oox/crypto/DocumentDecryption.hxx |   12 -
 include/oox/crypto/DocumentEncryption.hxx |   10 
 include/oox/crypto/IRMEngine.hxx          |   69 +++++
 include/oox/crypto/Standard2007Engine.hxx |    6 
 oox/Library_oox.mk                        |   12 +
 oox/source/core/xmlfilterbase.cxx         |   16 -
 oox/source/crypto/AgileEngine.cxx         |   33 ++
 oox/source/crypto/DocumentDecryption.cxx  |  134 +++++++++--
 oox/source/crypto/DocumentEncryption.cxx  |   41 ++-
 oox/source/crypto/IRMEngine.cxx           |  360 ++++++++++++++++++++++++++++++
 oox/source/crypto/Standard2007Engine.cxx  |   33 ++
 unotools/source/misc/mediadescriptor.cxx  |    5 
 14 files changed, 662 insertions(+), 82 deletions(-)

New commits:
commit 05401ffcd011e8120ae8d08cb7106fbc5d637400
Author:     Vasily Melenchuk <vasily.melenchuk at cib.de>
AuthorDate: Tue Sep 3 21:08:34 2019 +0300
Commit:     Vasily Melenchuk <vasily.melenchuk at cib.de>
CommitDate: Wed Sep 18 12:39:23 2019 +0200

    decrypt/encrypt IRM document
    
    Change-Id: Ic85f0590343654af6cd74de5fb56fde5363932ab
    Reviewed-on: https://gerrit.libreoffice.org/78574
    Reviewed-by: Vasily Melenchuk <vasily.melenchuk at cib.de>
    Tested-by: Vasily Melenchuk <vasily.melenchuk at cib.de>

diff --git a/include/oox/crypto/AgileEngine.hxx b/include/oox/crypto/AgileEngine.hxx
index 0395d42d357e..7a3f4788d1af 100644
--- a/include/oox/crypto/AgileEngine.hxx
+++ b/include/oox/crypto/AgileEngine.hxx
@@ -123,13 +123,15 @@ public:
 
     // Encryption
 
-    void writeEncryptionInfo(BinaryXOutputStream& rStream) override;
+    void writeEncryptionInfo(oox::ole::OleStorage& rOleStorage) override;
 
     void encrypt(css::uno::Reference<css::io::XInputStream>&  rxInputStream,
                  css::uno::Reference<css::io::XOutputStream>& rxOutputStream,
                  sal_uInt32 nSize) override;
 
-    bool setupEncryption(OUString const & rPassword) override;
+    bool setupEncryption(css::uno::Sequence<css::beans::NamedValue>& rMediaEncData) override;
+
+    virtual void createEncryptionData(comphelper::SequenceAsHashMap & aEncryptionData, const OUString rPassword) override;
 
     bool generateAndEncryptVerifierHash(OUString const & rPassword);
 
diff --git a/include/oox/crypto/CryptoEngine.hxx b/include/oox/crypto/CryptoEngine.hxx
index 8a947f10d106..77cac9af1c3b 100644
--- a/include/oox/crypto/CryptoEngine.hxx
+++ b/include/oox/crypto/CryptoEngine.hxx
@@ -18,6 +18,7 @@
 
 #include <com/sun/star/io/XInputStream.hpp>
 #include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
 
 namespace oox {
     class BinaryXInputStream;
@@ -49,9 +50,11 @@ public:
                     BinaryXOutputStream& aOutputStream) = 0;
 
     // Encryption
-    virtual void writeEncryptionInfo(BinaryXOutputStream & rStream) = 0;
+    virtual void writeEncryptionInfo(oox::ole::OleStorage& rOleStorage) = 0;
 
-    virtual bool setupEncryption(const OUString& rPassword) = 0;
+    virtual void createEncryptionData(comphelper::SequenceAsHashMap & aEncryptionData, const OUString rPassword) = 0;
+
+    virtual bool setupEncryption(css::uno::Sequence<css::beans::NamedValue>& rMediaEncData) = 0;
 
     virtual void encrypt(css::uno::Reference<css::io::XInputStream> & rxInputStream,
                          css::uno::Reference<css::io::XOutputStream> & rxOutputStream,
diff --git a/include/oox/crypto/DocumentDecryption.hxx b/include/oox/crypto/DocumentDecryption.hxx
index e32667760c9f..42a4c413ff18 100644
--- a/include/oox/crypto/DocumentDecryption.hxx
+++ b/include/oox/crypto/DocumentDecryption.hxx
@@ -35,16 +35,12 @@ namespace core {
 class OOX_DLLPUBLIC DocumentDecryption
 {
 private:
-    enum CryptoType
-    {
-        UNKNOWN,
-        STANDARD_2007,
-        AGILE
-    };
-
     oox::ole::OleStorage&           mrOleStorage;
     std::unique_ptr<CryptoEngine>   mEngine;
-    CryptoType                      mCryptoType;
+    OUString                        msEngineName;
+
+    bool readStrongEncryptionInfo();
+    bool readIRMEncryptionInfo();
 
 public:
     DocumentDecryption(oox::ole::OleStorage& rOleStorage);
diff --git a/include/oox/crypto/DocumentEncryption.hxx b/include/oox/crypto/DocumentEncryption.hxx
index 944e6b06c910..0d723656a81c 100644
--- a/include/oox/crypto/DocumentEncryption.hxx
+++ b/include/oox/crypto/DocumentEncryption.hxx
@@ -14,7 +14,8 @@
 #include <oox/dllapi.h>
 
 #include <com/sun/star/uno/Reference.hxx>
-#include <oox/crypto/Standard2007Engine.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <oox/crypto/CryptoEngine.hxx>
 #include <rtl/ustring.hxx>
 
 namespace com { namespace sun { namespace star {
@@ -31,15 +32,14 @@ class OOX_DLLPUBLIC DocumentEncryption
 private:
     css::uno::Reference< css::io::XStream > mxDocumentStream;
     oox::ole::OleStorage& mrOleStorage;
-    OUString const maPassword;
-
-    Standard2007Engine mEngine;
+    std::unique_ptr<CryptoEngine>   mEngine;
+    css::uno::Sequence< css::beans::NamedValue >& mMediaEncData;
 
 public:
     DocumentEncryption(
         css::uno::Reference< css::io::XStream > const & xDocumentStream,
         oox::ole::OleStorage& rOleStorage,
-        const OUString& aPassword);
+        css::uno::Sequence< css::beans::NamedValue >& rMediaEncData);
 
     bool encrypt();
 
diff --git a/include/oox/crypto/IRMEngine.hxx b/include/oox/crypto/IRMEngine.hxx
new file mode 100644
index 000000000000..3aadee705c2c
--- /dev/null
+++ b/include/oox/crypto/IRMEngine.hxx
@@ -0,0 +1,69 @@
+/* -*- 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_IRMENGINE_HXX
+#define INCLUDED_OOX_CRYPTO_IRMENGINE_HXX
+
+#include <oox/dllapi.h>
+#include <oox/crypto/CryptoEngine.hxx>
+#include <filter/msfilter/mscodec.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+namespace oox
+{
+class BinaryXInputStream;
+class BinaryXOutputStream;
+}
+
+namespace oox
+{
+namespace core
+{
+struct OOX_DLLPUBLIC IRMEncryptionInfo
+{
+    OString license;
+};
+
+class OOX_DLLPUBLIC IRMEngine : public CryptoEngine
+{
+    IRMEncryptionInfo mInfo;
+
+public:
+    IRMEngine();
+
+    bool readEncryptionInfo(css::uno::Reference<css::io::XInputStream>& rxInputStream) override;
+
+    virtual bool generateEncryptionKey(OUString const& rPassword) override;
+
+    virtual bool decrypt(BinaryXInputStream& aInputStream,
+                         BinaryXOutputStream& aOutputStream) override;
+
+    bool checkDataIntegrity() override;
+
+    void encrypt(css::uno::Reference<css::io::XInputStream>& rxInputStream,
+                 css::uno::Reference<css::io::XOutputStream>& rxOutputStream,
+                 sal_uInt32 nSize) override;
+
+    virtual void writeEncryptionInfo(oox::ole::OleStorage& rOleStorage) override;
+
+    virtual void createEncryptionData(comphelper::SequenceAsHashMap& aEncryptionData,
+                                      const OUString rPassword) override;
+
+    virtual bool
+    setupEncryption(css::uno::Sequence<css::beans::NamedValue>& rMediaEncData) override;
+};
+
+} // namespace core
+} // namespace oox
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/oox/crypto/Standard2007Engine.hxx b/include/oox/crypto/Standard2007Engine.hxx
index 88c45a46889b..7bec4e5f7aee 100644
--- a/include/oox/crypto/Standard2007Engine.hxx
+++ b/include/oox/crypto/Standard2007Engine.hxx
@@ -49,9 +49,11 @@ public:
                  css::uno::Reference<css::io::XOutputStream>& rxOutputStream,
                  sal_uInt32 nSize) override;
 
-    virtual void writeEncryptionInfo(BinaryXOutputStream& rStream) override;
+    virtual void writeEncryptionInfo(oox::ole::OleStorage& rOleStorage) override;
 
-    virtual bool setupEncryption(OUString const & rPassword) override;
+    virtual void createEncryptionData(comphelper::SequenceAsHashMap & aEncryptionData, const OUString rPassword) override;
+
+    virtual bool setupEncryption(css::uno::Sequence<css::beans::NamedValue>& rMediaEncData) override;
 
 };
 
diff --git a/oox/Library_oox.mk b/oox/Library_oox.mk
index 7b5a26866f5d..9e4fafe018a6 100644
--- a/oox/Library_oox.mk
+++ b/oox/Library_oox.mk
@@ -16,8 +16,19 @@ $(eval $(call gb_Library_use_custom_headers,oox,oox/generated))
 $(eval $(call gb_Library_set_include,oox,\
     $$(INCLUDE) \
     -I$(SRCDIR)/oox/inc \
+	-I"C:\Program Files\Active Directory Rights Management Services SDK 2.1\inc"\
 ))
 
+$(eval $(call gb_Library_add_libs,oox,\
+	msipc_s.lib \
+	msipc.lib \
+))
+
+$(eval $(call gb_Library_add_ldflags,oox,\
+	-LIBPATH:"C:\Program Files\Active Directory Rights Management Services SDK 2.1\lib\x64"\
+))
+
+
 ifeq ($(COM)-$(OS)-$(CPUNAME),GCC-LINUX-IA64)
 # at least Debian Linux ia64 fails at compile time on
 # link libooxlo.so which is apparently too large
@@ -98,6 +109,7 @@ $(eval $(call gb_Library_add_exception_objects,oox,\
     oox/source/core/relationshandler \
     oox/source/core/xmlfilterbase \
     oox/source/crypto/AgileEngine \
+    oox/source/crypto/IRMEngine \
     oox/source/crypto/CryptTools \
     oox/source/crypto/DocumentEncryption \
     oox/source/crypto/DocumentDecryption \
diff --git a/oox/source/core/xmlfilterbase.cxx b/oox/source/core/xmlfilterbase.cxx
index 54f8b9f6ca83..c404e3bec6bf 100644
--- a/oox/source/core/xmlfilterbase.cxx
+++ b/oox/source/core/xmlfilterbase.cxx
@@ -912,25 +912,13 @@ bool XmlFilterBase::implFinalizeExport( MediaDescriptor& rMediaDescriptor )
                                         MediaDescriptor::PROP_ENCRYPTIONDATA(),
                                         Sequence< NamedValue >() );
 
-    OUString aPassword;
-
-    for (int i=0; i<aMediaEncData.getLength(); i++)
-    {
-        if (aMediaEncData[i].Name == "OOXPassword")
-        {
-            Any& any = aMediaEncData[i].Value;
-            any >>= aPassword;
-            break;
-        }
-    }
-
-    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);
+        DocumentEncryption encryptor(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 7c2a0e9c93d2..5192ae905723 100644
--- a/oox/source/crypto/AgileEngine.cxx
+++ b/oox/source/crypto/AgileEngine.cxx
@@ -21,6 +21,7 @@
 #include <comphelper/processfactory.hxx>
 #include <comphelper/base64.hxx>
 #include <comphelper/sequence.hxx>
+#include <comphelper/sequenceashashmap.hxx>
 
 #include <filter/msfilter/mscodec.hxx>
 #include <tools/stream.hxx>
@@ -149,13 +150,13 @@ public:
                 comphelper::Base64::decode(encryptedKeyValue, rAttribute.Value);
                 mInfo.encryptedKeyValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedKeyValue);
             }
-            if (rAttrLocalName == "encryptedHmacKey")
+            else if (rAttrLocalName == "encryptedHmacKey")
             {
                 Sequence<sal_Int8> aValue;
                 comphelper::Base64::decode(aValue, rAttribute.Value);
                 mInfo.hmacEncryptedKey = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(aValue);
             }
-            if (rAttrLocalName == "encryptedHmacValue")
+            else if (rAttrLocalName == "encryptedHmacValue")
             {
                 Sequence<sal_Int8> aValue;
                 comphelper::Base64::decode(aValue, rAttribute.Value);
@@ -659,14 +660,29 @@ bool AgileEngine::encryptEncryptionKey(OUString const & rPassword)
     return true;
 }
 
-bool AgileEngine::setupEncryption(OUString const & rPassword)
+bool AgileEngine::setupEncryption(css::uno::Sequence<css::beans::NamedValue>& rMediaEncData)
 {
     if (meEncryptionPreset == AgileEncryptionPreset::AES_128_SHA1)
         setupEncryptionParameters({ 100000, 16, 128, 20, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA1") });
     else
         setupEncryptionParameters({ 100000, 16, 256, 64, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA512") });
 
-    return setupEncryptionKey(rPassword);
+    OUString sPassword;
+    for (int i = 0; i < rMediaEncData.getLength(); i++)
+    {
+        if (rMediaEncData[i].Name == "Password")
+        {
+            OUString sCryptoType;
+            rMediaEncData[i].Value >>= sPassword;
+        }
+    }
+
+    return setupEncryptionKey(sPassword);
+}
+
+void AgileEngine::createEncryptionData(comphelper::SequenceAsHashMap & aEncryptionData, const OUString rPassword)
+{
+    aEncryptionData["OOXPassword"] <<= rPassword;
 }
 
 void AgileEngine::setupEncryptionParameters(AgileEncryptionParameters const & rAgileEncryptionParameters)
@@ -700,8 +716,11 @@ bool AgileEngine::setupEncryptionKey(OUString const & rPassword)
     return true;
 }
 
-void AgileEngine::writeEncryptionInfo(BinaryXOutputStream & rStream)
+void AgileEngine::writeEncryptionInfo(oox::ole::OleStorage& rOleStorage)
 {
+    Reference<XOutputStream> xEncryptionInfo(rOleStorage.openOutputStream("EncryptionInfo"), UNO_SET_THROW);
+    BinaryXOutputStream rStream(xEncryptionInfo, false);
+
     rStream.WriteUInt32(msfilter::VERSION_INFO_AGILE);
     rStream.WriteUInt32(msfilter::AGILE_ENCRYPTION_RESERVED);
 
@@ -755,6 +774,10 @@ void AgileEngine::writeEncryptionInfo(BinaryXOutputStream & rStream)
         aXmlWriter.endDocument();
     }
     rStream.writeMemory(aMemStream.GetData(), aMemStream.GetSize());
+
+    rStream.close();
+    xEncryptionInfo->flush();
+    xEncryptionInfo->closeOutput();
 }
 
 void AgileEngine::encrypt(css::uno::Reference<css::io::XInputStream> &  rxInputStream,
diff --git a/oox/source/crypto/DocumentDecryption.cxx b/oox/source/crypto/DocumentDecryption.cxx
index 6ccbfc18d225..187522ee4a06 100644
--- a/oox/source/crypto/DocumentDecryption.cxx
+++ b/oox/source/crypto/DocumentDecryption.cxx
@@ -18,6 +18,7 @@
 #include <com/sun/star/uno/XComponentContext.hpp>
 #include <oox/crypto/AgileEngine.hxx>
 #include <oox/crypto/Standard2007Engine.hxx>
+#include <oox/crypto/IRMEngine.hxx>
 #include <oox/helper/binaryinputstream.hxx>
 #include <oox/helper/binaryoutputstream.hxx>
 #include <oox/ole/olestorage.hxx>
@@ -28,8 +29,7 @@ namespace core {
 using namespace css;
 
 DocumentDecryption::DocumentDecryption(oox::ole::OleStorage& rOleStorage) :
-    mrOleStorage(rOleStorage),
-    mCryptoType(UNKNOWN)
+    mrOleStorage(rOleStorage)
 {}
 
 bool DocumentDecryption::generateEncryptionKey(const OUString& rPassword)
@@ -39,11 +39,8 @@ bool DocumentDecryption::generateEncryptionKey(const OUString& rPassword)
     return false;
 }
 
-bool DocumentDecryption::readEncryptionInfo()
+bool DocumentDecryption::readStrongEncryptionInfo()
 {
-    if (!mrOleStorage.isStorage())
-        return false;
-
     uno::Reference<io::XInputStream> xEncryptionInfo = mrOleStorage.openInputStream("EncryptionInfo");
 
     BinaryXInputStream aBinaryInputStream(xEncryptionInfo, true);
@@ -51,37 +48,126 @@ bool DocumentDecryption::readEncryptionInfo()
 
     switch (aVersion)
     {
-        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;
+    case msfilter::VERSION_INFO_2007_FORMAT:
+    case msfilter::VERSION_INFO_2007_FORMAT_SP2:
+        msEngineName = "Standard"; // Set encryption info format
+        mEngine.reset(new Standard2007Engine);
+        break;
+    case msfilter::VERSION_INFO_AGILE:
+        msEngineName = "Agile"; // Set encryption info format
+        mEngine.reset(new AgileEngine);
+        break;
+    default:
+        break;
     }
     if (mEngine)
         return mEngine->readEncryptionInfo(xEncryptionInfo);
     return false;
 }
 
-uno::Sequence<beans::NamedValue> DocumentDecryption::createEncryptionData(const OUString& rPassword)
+bool DocumentDecryption::readIRMEncryptionInfo()
 {
-    comphelper::SequenceAsHashMap aEncryptionData;
+    // Read TransformInfo storage for IRM ECMA documents (MS-OFFCRYPTO 2.2.4)
+    uno::Reference<io::XInputStream> xTransformInfoStream = mrOleStorage.openInputStream("\006DataSpaces/TransformInfo/DRMEncryptedTransform/\006Primary");
+    SAL_WARN_IF(!xTransformInfoStream.is(), "oox", "TransormInfo stream is missing!");
+    BinaryXInputStream aBinaryStream(xTransformInfoStream, true);
+
+    // MS-OFFCRYPTO 2.1.8: TransformInfoHeader
+    aBinaryStream.readuInt32();  // TransformLength
+    aBinaryStream.readuInt32();  // TransformType
+    // TransformId
+    sal_uInt32 aStringLength = aBinaryStream.readuInt32();
+    OUString sTransformId = aBinaryStream.readUnicodeArray(aStringLength / 2);
+    aBinaryStream.skip((4 - (aStringLength & 3)) & 3);  // Skip padding
+
+    // TransformName
+    aStringLength = aBinaryStream.readuInt32();
+    OUString sTransformName = aBinaryStream.readUnicodeArray(aStringLength / 2);
+    aBinaryStream.skip((4 - (aStringLength & 3)) & 3);  // Skip padding
+
+    aBinaryStream.readuInt32();  // ReaderVersion
+    aBinaryStream.readuInt32();  // UpdaterVersion
+    aBinaryStream.readuInt32();  // WriterVersion
+
+    // MS-OFFCRYPTO 2.2.5: ExtensibilityHeader
+    aBinaryStream.readuInt32();  // ExtensibilityHeader
+
+    msEngineName = "IRM"; // Set encryption info format
+    mEngine.reset(new IRMEngine);
+    return mEngine->readEncryptionInfo(xTransformInfoStream);
+}
 
-    if (mCryptoType == AGILE)
+bool DocumentDecryption::readEncryptionInfo()
+{
+    if (!mrOleStorage.isStorage())
+        return false;
+
+    // Read 0x6DataSpaces/DataSpaceMap
+    uno::Reference<io::XInputStream> xDataSpaceMap = mrOleStorage.openInputStream("\006DataSpaces/DataSpaceMap");
+    if (xDataSpaceMap.is())
     {
-        aEncryptionData["CryptoType"] <<= OUString("Agile");
+        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");
+
+        OUString sDataSpaceName;
+        // Read each DataSpaceMapEntry (MS-OFFCRYPTO 2.1.6.1)
+        for (sal_uInt32 i = 0; i < aEntryCount; i++)
+        {
+            aDataSpaceStream.readuInt32();  // Entry length
+
+            // 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
+                aDataSpaceStream.readuInt32(); // ReferenceComponentType
+                sal_uInt32 aReferenceComponentNameLength = aDataSpaceStream.readuInt32();
+                OUString sReferenceComponentName = 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 (sDataSpaceName == "DRMEncryptedDataSpace")
+        {
+            return readIRMEncryptionInfo();
+        }
+        else if (sDataSpaceName == "\011DRMDataSpace") // 0x09DRMDataSpace
+        {
+            // TODO: IRM binary file
+        }
+        else if (sDataSpaceName == "StrongEncryptionDataSpace")
+        {
+            return readStrongEncryptionInfo();
+        }
+        else
+        {
+            SAL_WARN("oox", "Unknown dataspace - document will be not decrypted!");
+        }
     }
-    else if (mCryptoType == STANDARD_2007)
+    else
     {
-        aEncryptionData["CryptoType"] <<= OUString("Standard");
+        // 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");
+        return readStrongEncryptionInfo();
     }
+    return false;
+}
+
+uno::Sequence<beans::NamedValue> DocumentDecryption::createEncryptionData(const OUString& rPassword)
+{
+    comphelper::SequenceAsHashMap aEncryptionData;
+
+    aEncryptionData["CryptoType"] <<= msEngineName;
+    mEngine->createEncryptionData(aEncryptionData, rPassword);
 
-    aEncryptionData["OOXPassword"] <<= rPassword;
     return aEncryptionData.getAsConstNamedValueList();
 }
 
diff --git a/oox/source/crypto/DocumentEncryption.cxx b/oox/source/crypto/DocumentEncryption.cxx
index 8cb62c23f006..5fab856c0c18 100644
--- a/oox/source/crypto/DocumentEncryption.cxx
+++ b/oox/source/crypto/DocumentEncryption.cxx
@@ -9,6 +9,8 @@
  */
 
 #include <oox/crypto/DocumentEncryption.hxx>
+#include <oox/crypto/Standard2007Engine.hxx>
+#include <oox/crypto/IRMEngine.hxx>
 
 #include <com/sun/star/io/XInputStream.hpp>
 #include <com/sun/star/io/XOutputStream.hpp>
@@ -27,11 +29,29 @@ using namespace css::uno;
 
 DocumentEncryption::DocumentEncryption(Reference<XStream> const & xDocumentStream,
                                        oox::ole::OleStorage& rOleStorage,
-                                       const OUString& rPassword)
+                                       Sequence<css::beans::NamedValue>& rMediaEncData)
     : 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 == "IRM")
+            {
+                mEngine.reset(new IRMEngine);
+            }
+            else
+            {
+                mEngine.reset(new Standard2007Engine);
+            }
+        }
+    }
+}
 
 bool DocumentEncryption::encrypt()
 {
@@ -47,23 +67,14 @@ bool DocumentEncryption::encrypt()
     if (!mrOleStorage.isStorage())
         return false;
 
-    mEngine.setupEncryption(maPassword);
+    mEngine->setupEncryption(mMediaEncData);
 
     Reference<XOutputStream> xOutputStream(mrOleStorage.openOutputStream("EncryptedPackage"), UNO_SET_THROW);
-
-    mEngine.encrypt(xInputStream, xOutputStream, aLength);
-
+    mEngine->encrypt(xInputStream, xOutputStream, aLength);
     xOutputStream->flush();
     xOutputStream->closeOutput();
 
-    Reference<XOutputStream> xEncryptionInfo(mrOleStorage.openOutputStream("EncryptionInfo"), UNO_SET_THROW);
-    BinaryXOutputStream aEncryptionInfoBinaryOutputStream(xEncryptionInfo, false);
-
-    mEngine.writeEncryptionInfo(aEncryptionInfoBinaryOutputStream);
-
-    aEncryptionInfoBinaryOutputStream.close();
-    xEncryptionInfo->flush();
-    xEncryptionInfo->closeOutput();
+    mEngine->writeEncryptionInfo(mrOleStorage);
 
     return true;
 }
diff --git a/oox/source/crypto/IRMEngine.cxx b/oox/source/crypto/IRMEngine.cxx
new file mode 100644
index 000000000000..94bcc773333e
--- /dev/null
+++ b/oox/source/crypto/IRMEngine.cxx
@@ -0,0 +1,360 @@
+/* -*- 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/IRMEngine.hxx>
+
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/binaryoutputstream.hxx>
+
+#include <sax/tools/converter.hxx>
+
+#include <comphelper/hash.hxx>
+#include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/random.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/base64.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+
+#include <filter/msfilter/mscodec.hxx>
+#include <tools/stream.hxx>
+#include <tools/XmlWriter.hxx>
+
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/sax/XFastParser.hpp>
+#include <com/sun/star/xml/sax/XFastTokenHandler.hpp>
+#include <com/sun/star/xml/sax/FastParser.hpp>
+#include <com/sun/star/xml/sax/FastToken.hpp>
+
+#include <msipc.h>
+
+using namespace css;
+using namespace css::beans;
+using namespace css::io;
+using namespace css::lang;
+using namespace css::uno;
+using namespace css::xml::sax;
+using namespace css::xml;
+
+namespace oox
+{
+namespace core
+{
+IRMEngine::IRMEngine() {}
+
+bool IRMEngine::checkDataIntegrity() { return true; }
+
+bool IRMEngine::decrypt(BinaryXInputStream& aInputStream, BinaryXOutputStream& aOutputStream)
+{
+    aInputStream.readInt64(); // Skip stream size
+
+    HRESULT hr = IpcInitialize();
+    if (FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED))
+    {
+        // ERROR_ALREADY_INITIALIZED not an error
+        // TODO: some reaction?
+    }
+
+    // Get decryption key
+    IPC_BUFFER licenseBuffer;
+    IPC_KEY_HANDLE key;
+    licenseBuffer.pvBuffer = (void*)mInfo.license.getStr();
+    licenseBuffer.cbBuffer = mInfo.license.getLength();
+    hr = IpcGetKey(&licenseBuffer, 0, NULL, NULL, &key);
+    if (FAILED(hr))
+    {
+        // TODO: some reaction?
+    }
+
+    // Get size of decrypt block
+    DWORD* blockSize;
+    hr = IpcGetKeyProperty(key, IPC_KI_BLOCK_SIZE, nullptr, (LPVOID*)&blockSize);
+    if (FAILED(hr))
+    {
+        // TODO: some reaction?
+    }
+
+    char* pEncryptedBuffer = new char[*blockSize];
+    char* pDecryptedBuffer = new char[*blockSize];
+    int blockNo = 0;
+    bool lastBlock = false;
+
+    do
+    {
+        sal_uInt32 readBytes = aInputStream.readArray(pEncryptedBuffer, *blockSize);
+        lastBlock = readBytes != *blockSize;
+        DWORD bytes = 0;
+        hr = IpcDecrypt(key, blockNo, lastBlock, (PBYTE)pEncryptedBuffer, *blockSize,
+                        (PBYTE)pDecryptedBuffer, *blockSize, &bytes);
+
+        if (FAILED(hr))
+        {
+            // TODO: some reaction?
+        }
+
+        aOutputStream.writeArray(pDecryptedBuffer, bytes);
+
+        blockNo++;
+    } while (!lastBlock);
+
+    delete[] pEncryptedBuffer;
+    delete[] pDecryptedBuffer;
+
+    return true;
+}
+
+void IRMEngine::createEncryptionData(comphelper::SequenceAsHashMap& aEncryptionData,
+                                     const OUString rPassword)
+{
+    aEncryptionData["OOXPassword"] <<= rPassword;
+
+    css::uno::Sequence<sal_uInt8> seq;
+    seq.realloc(mInfo.license.getLength());
+    memcpy(seq.getArray(), mInfo.license.getStr(), mInfo.license.getLength());
+
+    aEncryptionData["license"] <<= seq;
+}
+
+bool IRMEngine::readEncryptionInfo(uno::Reference<io::XInputStream>& rxInputStream)
+{
+    // MS-OFFCRYPTO 2.2.6: XrMLLicense
+    BinaryXInputStream aBinaryStream(rxInputStream, true);
+    sal_uInt32 aStringLength = aBinaryStream.readuInt32();
+    mInfo.license = aBinaryStream.readCharArray(aStringLength);
+
+    if (mInfo.license.getLength()
+        && static_cast<sal_uChar>(mInfo.license[0]) != 0x0ef) // BOM is missing?
+    {
+        mInfo.license = "\x0ef\x0bb\x0bf" + mInfo.license;
+    }
+
+    // TODO: CHECK info data
+
+    return true;
+}
+
+bool IRMEngine::setupEncryption(css::uno::Sequence<css::beans::NamedValue>& rMediaEncData)
+{
+    for (int i = 0; i < rMediaEncData.getLength(); i++)
+    {
+        if (rMediaEncData[i].Name == "license")
+        {
+            css::uno::Sequence<sal_uInt8> seq;
+            rMediaEncData[i].Value >>= seq;
+            mInfo.license = OString(reinterpret_cast<sal_Char*>(seq.getArray()), seq.getLength());
+        }
+    }
+
+    return true;
+}
+
+void IRMEngine::writeEncryptionInfo(oox::ole::OleStorage& rOleStorage)
+{
+    // Write 0x6DataSpaces/DataSpaceMap
+    Reference<XOutputStream> xDataSpaceMap(
+        rOleStorage.openOutputStream("\006DataSpaces/DataSpaceMap"), UNO_SET_THROW);
+    BinaryXOutputStream aDataSpaceMapStream(xDataSpaceMap, false);
+
+    aDataSpaceMapStream.WriteInt32(8); // Header length
+    aDataSpaceMapStream.WriteInt32(1); // Entries count
+
+    // DataSpaceMapEntry (MS-OFFCRYPTO 2.1.6.1)
+    OUString sDataSpaceName("DRMEncryptedDataSpace");
+    OUString sReferenceComponent("EncryptedPackage");
+
+    aDataSpaceMapStream.WriteInt32(0x58); // Length
+    aDataSpaceMapStream.WriteInt32(1); // References count
+    aDataSpaceMapStream.WriteInt32(0); // References component type
+
+    aDataSpaceMapStream.WriteInt32(sReferenceComponent.getLength() * 2);
+    aDataSpaceMapStream.writeUnicodeArray(sReferenceComponent);
+    while (aDataSpaceMapStream.tell() % 4) // Padding
+    {
+        aDataSpaceMapStream.writeValue<sal_Char>(0);
+    }
+
+    aDataSpaceMapStream.WriteInt32(sDataSpaceName.getLength() * 2);
+    aDataSpaceMapStream.writeUnicodeArray(sDataSpaceName);
+    while (aDataSpaceMapStream.tell() % 4) // Padding
+    {
+        aDataSpaceMapStream.writeValue<sal_Char>(0);
+    }
+
+    // Write length
+    sal_uInt32 nLength = aDataSpaceMapStream.tell() - 8;
+    aDataSpaceMapStream.seek(8);
+    aDataSpaceMapStream.WriteInt32(nLength);
+
+    aDataSpaceMapStream.close();
+    xDataSpaceMap->flush();
+    xDataSpaceMap->closeOutput();
+
+    // Write 0x6DataSpaces/Version
+    Reference<XOutputStream> xVersion(rOleStorage.openOutputStream("\006DataSpaces/Version"),
+                                      UNO_SET_THROW);
+    BinaryXOutputStream aVersionStream(xVersion, false);
+
+    OUString sFeatureIdentifier("Microsoft.Container.DataSpaces");
+    aVersionStream.WriteInt32(sFeatureIdentifier.getLength() * 2);
+    aVersionStream.writeUnicodeArray(sFeatureIdentifier);
+    while (aVersionStream.tell() % 4) // Padding
+    {
+        aVersionStream.writeValue<sal_Char>(0);
+    }
+
+    aVersionStream.WriteInt32(1); // Reader version
+    aVersionStream.WriteInt32(1); // Updater version
+    aVersionStream.WriteInt32(1); // Writer version
+
+    aVersionStream.close();
+    xVersion->flush();
+    xVersion->closeOutput();
+
+    // Write 0x6DataSpaces/DataSpaceInfo/[dataspacename]
+    OUString sStreamName = "\006DataSpaces/DataSpaceInfo/" + sDataSpaceName;
+    Reference<XOutputStream> xDataSpaceInfo(rOleStorage.openOutputStream(sStreamName),
+                                            UNO_SET_THROW);
+    BinaryXOutputStream aDataSpaceInfoStream(xDataSpaceInfo, false);
+
+    aDataSpaceInfoStream.WriteInt32(8); // Header length
+    aDataSpaceInfoStream.WriteInt32(1); // Entries count
+
+    OUString sTransformName("DRMEncryptedTransform");
+    aDataSpaceInfoStream.WriteInt32(sTransformName.getLength() * 2);
+    aDataSpaceInfoStream.writeUnicodeArray(sTransformName);
+    while (aDataSpaceInfoStream.tell() % 4) // Padding
+    {
+        aDataSpaceInfoStream.writeValue<sal_Char>(0);
+    }
+
+    aDataSpaceInfoStream.close();
+    xDataSpaceInfo->flush();
+    xDataSpaceInfo->closeOutput();
+
+    // Write 0x6DataSpaces/TransformInfo/[transformname]
+    sStreamName = "\006DataSpaces/TransformInfo/" + sTransformName + "/\006Primary";
+    Reference<XOutputStream> xTransformInfo(rOleStorage.openOutputStream(sStreamName),
+                                            UNO_SET_THROW);
+    BinaryXOutputStream aTransformInfoStream(xTransformInfo, false);
+
+    // MS-OFFCRYPTO 2.1.8: TransformInfoHeader
+    aTransformInfoStream.WriteInt32(0); // TransformLength, will be written later
+    aTransformInfoStream.WriteInt32(1); // TransformType
+
+    // TransformId
+    OUString sTransformId("{C73DFACD-061F-43B0-8B64-0C620D2A8B50}");
+    aTransformInfoStream.WriteInt32(sTransformId.getLength() * 2);
+    aTransformInfoStream.writeUnicodeArray(sTransformId);
+    while (aTransformInfoStream.tell() % 4) // Padding
+    {
+        aTransformInfoStream.writeValue<sal_Char>(0);
+    }
+
+    // Calculate length and write it into beginning
+    nLength = aTransformInfoStream.tell();
+    aTransformInfoStream.seek(0);
+    aTransformInfoStream.WriteInt32(nLength);
+    aTransformInfoStream.seek(nLength);
+
+    // TransformName
+    OUString sTransformInfoName("Microsoft.Metadata.DRMTransform");
+    aTransformInfoStream.WriteInt32(sTransformInfoName.getLength() * 2);
+    aTransformInfoStream.writeUnicodeArray(sTransformInfoName);
+    while (aTransformInfoStream.tell() % 4) // Padding
+    {
+        aTransformInfoStream.writeValue<sal_Char>(0);
+    }
+
+    aTransformInfoStream.WriteInt32(1); // ReaderVersion
+    aTransformInfoStream.WriteInt32(1); // UpdateVersion
+    aTransformInfoStream.WriteInt32(1); // WriterVersion
+
+    aTransformInfoStream.WriteInt32(4); // Extensibility Header
+
+    aTransformInfoStream.WriteInt32(mInfo.license.getLength() - 3); // LicenseLength
+    aTransformInfoStream.writeArray<sal_Char>(mInfo.license.getStr() + 3,
+                                              mInfo.license.getLength() - 3);
+    aTransformInfoStream.writeValue<sal_Char>(0);
+
+    aTransformInfoStream.close();
+    xTransformInfo->flush();
+    xTransformInfo->closeOutput();
+}
+
+void IRMEngine::encrypt(css::uno::Reference<css::io::XInputStream>& rxInputStream,
+                        css::uno::Reference<css::io::XOutputStream>& rxOutputStream,
+                        sal_uInt32 /*nSize*/)
+{
+    HRESULT hr = IpcInitialize();
+
+    if (FAILED(hr) && hr != HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED))
+    {
+        // ERROR_ALREADY_INITIALIZED not an error
+        // TODO: some reaction?
+    }
+
+    BinaryXInputStream aInputStream(rxInputStream, false);
+    BinaryXOutputStream aOutputStream(rxOutputStream, false);
+    aOutputStream.WriteInt64(aInputStream.size()); // Stream size
+
+    // Get decryption key
+    IPC_BUFFER licenseBuffer;
+    IPC_KEY_HANDLE key;
+    licenseBuffer.pvBuffer = (void*)mInfo.license.getStr();
+    licenseBuffer.cbBuffer = mInfo.license.getLength();
+    hr = IpcGetKey(&licenseBuffer, 0, NULL, NULL, &key);
+    if (FAILED(hr))
+    {
+        // TODO: some reaction?
+    }
+
+    // Get size of encrypt block
+    DWORD* blockSize;
+    hr = IpcGetKeyProperty(key, IPC_KI_BLOCK_SIZE, nullptr, (LPVOID*)&blockSize);
+    if (FAILED(hr))
+    {
+        // TODO: some reaction?
+    }
+
+    char* pEncryptedBuffer = new char[*blockSize];
+    char* pDecryptedBuffer = new char[*blockSize];
+    int blockNo = 0;
+    bool lastBlock = false;
+
+    do
+    {
+        sal_uInt32 readBytes = aInputStream.readArray(pDecryptedBuffer, *blockSize);
+        lastBlock = readBytes != *blockSize;
+        DWORD bytes = 0;
+        hr = IpcEncrypt(key, blockNo, lastBlock, (PBYTE)pDecryptedBuffer, *blockSize,
+                        (PBYTE)pEncryptedBuffer, *blockSize, &bytes);
+
+        if (FAILED(hr))
+        {
+            // TODO: some reaction?
+        }
+
+        aOutputStream.writeArray(pEncryptedBuffer, bytes);
+
+        blockNo++;
+    } while (!lastBlock);
+
+    delete[] pEncryptedBuffer;
+    delete[] pDecryptedBuffer;
+}
+
+bool IRMEngine::generateEncryptionKey(const OUString& /*password*/) { return true; }
+
+} // namespace core
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/crypto/Standard2007Engine.cxx b/oox/source/crypto/Standard2007Engine.cxx
index 38c4e03baf15..05d569c32cec 100644
--- a/oox/source/crypto/Standard2007Engine.cxx
+++ b/oox/source/crypto/Standard2007Engine.cxx
@@ -10,6 +10,7 @@
 
 #include <oox/crypto/Standard2007Engine.hxx>
 
+#include <com/sun/star/io/XStream.hpp>
 #include <oox/crypto/CryptTools.hxx>
 #include <oox/helper/binaryinputstream.hxx>
 #include <oox/helper/binaryoutputstream.hxx>
@@ -17,6 +18,10 @@
 #include <rtl/random.h>
 
 #include <comphelper/hash.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+
+using namespace css::io;
+using namespace css::uno;
 
 namespace oox {
 namespace core {
@@ -188,7 +193,12 @@ bool Standard2007Engine::checkDataIntegrity()
     return true;
 }
 
-bool Standard2007Engine::setupEncryption(OUString const & password)
+void Standard2007Engine::createEncryptionData(comphelper::SequenceAsHashMap & aEncryptionData, const OUString rPassword)
+{
+    aEncryptionData["OOXPassword"] <<= rPassword;
+}
+
+bool Standard2007Engine::setupEncryption(css::uno::Sequence<css::beans::NamedValue>& rMediaEncData)
 {
     mInfo.header.flags        = msfilter::ENCRYPTINFO_AES | msfilter::ENCRYPTINFO_CRYPTOAPI;
     mInfo.header.algId        = msfilter::ENCRYPT_ALGO_AES128;
@@ -202,7 +212,17 @@ bool Standard2007Engine::setupEncryption(OUString const & password)
     mKey.clear();
     mKey.resize(keyLength, 0);
 
-    if (!calculateEncryptionKey(password))
+    OUString sPassword;
+    for (int i = 0; i < rMediaEncData.getLength(); i++)
+    {
+        if (rMediaEncData[i].Name == "Password")
+        {
+            OUString sCryptoType;
+            rMediaEncData[i].Value >>= sPassword;
+        }
+    }
+
+    if (!calculateEncryptionKey(sPassword))
         return false;
 
     if (!generateVerifier())
@@ -211,8 +231,11 @@ bool Standard2007Engine::setupEncryption(OUString const & password)
     return true;
 }
 
-void Standard2007Engine::writeEncryptionInfo(BinaryXOutputStream& rStream)
+void Standard2007Engine::writeEncryptionInfo(oox::ole::OleStorage& rOleStorage)
 {
+    Reference<XOutputStream> xEncryptionInfo(rOleStorage.openOutputStream("EncryptionInfo"), UNO_SET_THROW);
+    BinaryXOutputStream rStream(xEncryptionInfo, false);
+
     rStream.WriteUInt32(msfilter::VERSION_INFO_2007_FORMAT);
 
     sal_uInt32 cspNameSize = (lclCspName.getLength() * 2) + 2;
@@ -228,6 +251,10 @@ void Standard2007Engine::writeEncryptionInfo(BinaryXOutputStream& rStream)
     rStream.WriteUInt16(0);
 
     rStream.writeMemory(&mInfo.verifier, sizeof(msfilter::EncryptionVerifierAES));
+
+    rStream.close();
+    xEncryptionInfo->flush();
+    xEncryptionInfo->closeOutput();
 }
 
 void Standard2007Engine::encrypt(css::uno::Reference<css::io::XInputStream> &  rxInputStream,
diff --git a/unotools/source/misc/mediadescriptor.cxx b/unotools/source/misc/mediadescriptor.cxx
index 0d0839f62b18..67804eaf4fde 100644
--- a/unotools/source/misc/mediadescriptor.cxx
+++ b/unotools/source/misc/mediadescriptor.cxx
@@ -467,8 +467,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;


More information about the Libreoffice-commits mailing list