/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include namespace { void lcl_getListOfStreams(oox::StorageBase* pStorage, std::vector& 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 { namespace core { using namespace css; 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 xStream = mrOleStorage.openInputStream(sStreamName); assert(xStream.is()); BinaryXInputStream aBinaryInputStream(xStream, true); css::uno::Sequence< sal_Int8 > oData; sal_Int32 nStreamSize = aBinaryInputStream.size(); sal_Int32 nReadBytes = aBinaryInputStream.readData(oData, nStreamSize); assert(nStreamSize == nReadBytes); aStreamsData[sStreamName] <<= oData; } maStreamsSequence = aStreamsData.getAsConstNamedValueList(); } bool DocumentDecryption::generateEncryptionKey(const OUString& rPassword) { if (mEngine) return mEngine->generateEncryptionKey(rPassword); return false; } void DocumentDecryption::readStrongEncryptionInfo() { uno::Reference xEncryptionInfo = mrOleStorage.openInputStream("EncryptionInfo"); BinaryXInputStream aBinaryInputStream(xEncryptionInfo, true); sal_uInt32 aVersion = aBinaryInputStream.readuInt32(); switch (aVersion) { case msfilter::VERSION_INFO_2007_FORMAT: case msfilter::VERSION_INFO_2007_FORMAT_SP2: msEngineName = "Standard"; // Set encryption info format mEngine.reset(new Standard2007Engine(mxContext)); break; case msfilter::VERSION_INFO_AGILE: msEngineName = "Agile"; // Set encryption info format mEngine.reset(new AgileEngine(mxContext)); break; default: break; } } bool DocumentDecryption::readEncryptionInfo() { if (!mrOleStorage.isStorage()) return false; // Read 0x6DataSpaces/DataSpaceMap uno::Reference xDataSpaceMap = mrOleStorage.openInputStream("\006DataSpaces/DataSpaceMap"); 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"); 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") { msEngineName = "IRM"; // Set encryption info format mEngine.reset(new IRMEngine(mxContext)); } else if (sDataSpaceName == "\011DRMDataSpace") // 0x09DRMDataSpace { // TODO: IRM binary file } else if (sDataSpaceName == "StrongEncryptionDataSpace") { readStrongEncryptionInfo(); } else { SAL_WARN("oox", "Unknown dataspace - document will be not decrypted!"); } } 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"); readStrongEncryptionInfo(); } if (!mEngine) return false; return mEngine->readEncryptionInfo(maStreamsSequence); } uno::Sequence DocumentDecryption::createEncryptionData(const OUString& rPassword) { comphelper::SequenceAsHashMap aEncryptionData; aEncryptionData["CryptoType"] <<= msEngineName; mEngine->createEncryptionData(aEncryptionData, rPassword); return aEncryptionData.getAsConstNamedValueList(); } bool DocumentDecryption::decrypt(const uno::Reference& xDocumentStream) { bool bResult = false; if (!mrOleStorage.isStorage()) return false; // open the required input streams in the encrypted package uno::Reference xEncryptedPackage(mrOleStorage.openInputStream("EncryptedPackage"), uno::UNO_QUERY); // create temporary file for unencrypted package uno::Reference xDecryptedPackage(xDocumentStream->getOutputStream(), uno::UNO_QUERY); BinaryXOutputStream aDecryptedPackage(xDecryptedPackage, true); BinaryXInputStream aEncryptedPackage(xEncryptedPackage, true); bResult = mEngine->decrypt(aEncryptedPackage, aDecryptedPackage); xDecryptedPackage->flush(); aDecryptedPackage.seekToStart(); if (bResult) return mEngine->checkDataIntegrity(); return bResult; } } // namespace core } // namespace oox /* vim:set shiftwidth=4 softtabstop=4 expandtab: */