summaryrefslogtreecommitdiff
path: root/oox
diff options
context:
space:
mode:
authorVasily Melenchuk <vasily.melenchuk@cib.de>2019-09-03 21:08:34 +0300
committerThorsten Behrens <Thorsten.Behrens@CIB.de>2020-05-25 10:04:15 +0200
commitb9353394f46e46485fd148f2842f0c1e8e5322e3 (patch)
treebf3dcc20cafbc00275c7154858379361b5147ff9 /oox
parent2f17679a46ca1336cb82ef652e09f423c5b8923d (diff)
[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>
Diffstat (limited to 'oox')
-rw-r--r--oox/Library_oox.mk1
-rw-r--r--oox/qa/unit/CryptoTest.cxx46
-rw-r--r--oox/source/core/filterdetect.cxx20
-rw-r--r--oox/source/core/xmlfilterbase.cxx19
-rw-r--r--oox/source/crypto/AgileEngine.cxx10
-rw-r--r--oox/source/crypto/CryptTools.cxx4
-rw-r--r--oox/source/crypto/DocumentDecryption.cxx185
-rw-r--r--oox/source/crypto/DocumentEncryption.cxx72
-rw-r--r--oox/source/crypto/Standard2007Engine.cxx6
-rw-r--r--oox/source/crypto/StrongEncryptionDataSpace.cxx206
-rw-r--r--oox/util/oox.component4
11 files changed, 450 insertions, 123 deletions
diff --git a/oox/Library_oox.mk b/oox/Library_oox.mk
index cc235b87e360..3d8b46b2a24b 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 e1a4781d234c..c4058619e5c9 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 4a6edbdd7658..0ab68688c9be 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 03d68fc232c4..5c5d5e5076e5 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 d8c184caa46c..f4eb9d21ea6f 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 cc8c5a925b43..ff11ebbc0436 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 1ed26f4d341c..45b820a89302 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>
+
+#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::core {
+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();
+ if (xDataSpaceMap.is())
+ {
+ 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
+ }
+ }
+ 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";
+ }
- switch (aVersion)
+ 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())
{
- 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;
+ // we do not know how to decrypt this document
+ return false;
}
- if (mEngine)
- return mEngine->readEncryptionInfo(xEncryptionInfo);
- 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 1ea421c8392f..6b88549299a1 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);
-
- Reference<XOutputStream> xOutputStream(mrOleStorage.openOutputStream("EncryptedPackage"), UNO_SET_THROW);
+ mxPackageEncryption->setupEncryption(mMediaEncData);
- mEngine.encrypt(xInputStream, xOutputStream, aLength);
+ Sequence<NamedValue> aStreams = mxPackageEncryption->encrypt(xInputStream);
- xOutputStream->flush();
- xOutputStream->closeOutput();
+ for (const NamedValue & aStream : std::as_const(aStreams))
+ {
+ Reference<XOutputStream> xOutputStream(mrOleStorage.openOutputStream(aStream.Name), UNO_SET_THROW);
+ BinaryXOutputStream aBinaryOutputStream(xOutputStream, true);
- Reference<XOutputStream> xEncryptionInfo(mrOleStorage.openOutputStream("EncryptionInfo"), UNO_SET_THROW);
- BinaryXOutputStream aEncryptionInfoBinaryOutputStream(xEncryptionInfo, false);
+ css::uno::Sequence<sal_Int8> aStreamSequence;
+ aStream.Value >>= aStreamSequence;
- mEngine.writeEncryptionInfo(aEncryptionInfoBinaryOutputStream);
+ aBinaryOutputStream.writeData(aStreamSequence);
- 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 0ad782522450..2aaf6f4ec3f3 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 000000000000..2e21a89158f6
--- /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 32a8100b8fb8..ef54da9a11e2 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>