[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