diff options
author | Vasily Melenchuk <vasily.melenchuk@cib.de> | 2019-09-03 21:08:34 +0300 |
---|---|---|
committer | Thorsten Behrens <Thorsten.Behrens@CIB.de> | 2020-05-25 10:04:15 +0200 |
commit | b9353394f46e46485fd148f2842f0c1e8e5322e3 (patch) | |
tree | bf3dcc20cafbc00275c7154858379361b5147ff9 /oox | |
parent | 2f17679a46ca1336cb82ef652e09f423c5b8923d (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.mk | 1 | ||||
-rw-r--r-- | oox/qa/unit/CryptoTest.cxx | 46 | ||||
-rw-r--r-- | oox/source/core/filterdetect.cxx | 20 | ||||
-rw-r--r-- | oox/source/core/xmlfilterbase.cxx | 19 | ||||
-rw-r--r-- | oox/source/crypto/AgileEngine.cxx | 10 | ||||
-rw-r--r-- | oox/source/crypto/CryptTools.cxx | 4 | ||||
-rw-r--r-- | oox/source/crypto/DocumentDecryption.cxx | 185 | ||||
-rw-r--r-- | oox/source/crypto/DocumentEncryption.cxx | 72 | ||||
-rw-r--r-- | oox/source/crypto/Standard2007Engine.cxx | 6 | ||||
-rw-r--r-- | oox/source/crypto/StrongEncryptionDataSpace.cxx | 206 | ||||
-rw-r--r-- | oox/util/oox.component | 4 |
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> |