1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include <oox/crypto/DocumentDecryption.hxx>
13 #include <comphelper/sequenceashashmap.hxx>
15 #include <com/sun/star/beans/NamedValue.hpp>
16 #include <com/sun/star/io/XSeekable.hpp>
17 #include <com/sun/star/io/XStream.hpp>
18 #include <com/sun/star/io/IOException.hpp>
19 #include <com/sun/star/uno/XComponentContext.hpp>
20 #include <com/sun/star/packages/XPackageEncryption.hpp>
21 #include <oox/ole/olestorage.hxx>
22 #include <oox/helper/binaryinputstream.hxx>
24 #include <sal/log.hxx>
29 void lcl_getListOfStreams(oox::StorageBase
* pStorage
, std::vector
<OUString
>& rElementNames
)
31 std::vector
<OUString
> oElementNames
;
32 pStorage
->getElementNames(oElementNames
);
33 for (const auto& sName
: oElementNames
)
35 oox::StorageRef rSubStorage
= pStorage
->openSubStorage(sName
, false);
36 if (rSubStorage
&& rSubStorage
->isStorage())
38 lcl_getListOfStreams(rSubStorage
.get(), rElementNames
);
42 if (pStorage
->isRootStorage())
43 rElementNames
.push_back(sName
);
45 rElementNames
.push_back(pStorage
->getPath() + "/" + sName
);
55 DocumentDecryption::DocumentDecryption(css::uno::Reference
<css::uno::XComponentContext
> xContext
,
56 oox::ole::OleStorage
& rOleStorage
)
57 : mxContext(std::move(xContext
))
58 , mrOleStorage(rOleStorage
)
60 // Get OLE streams into sequences for later use in CryptoEngine
61 std::vector
<OUString
> aStreamNames
;
62 lcl_getListOfStreams(&mrOleStorage
, aStreamNames
);
64 comphelper::SequenceAsHashMap aStreamsData
;
65 for (const auto& sStreamName
: aStreamNames
)
67 uno::Reference
<io::XInputStream
> xStream
= mrOleStorage
.openInputStream(sStreamName
);
69 throw io::IOException("Cannot open OLE input stream for " + sStreamName
+ "!");
71 BinaryXInputStream
aBinaryInputStream(xStream
, true);
73 css::uno::Sequence
<sal_Int8
> oData
;
74 sal_Int32 nStreamSize
= aBinaryInputStream
.size();
75 sal_Int32 nReadBytes
= aBinaryInputStream
.readData(oData
, nStreamSize
);
77 if (nStreamSize
!= nReadBytes
)
79 SAL_WARN("oox", "OLE stream invalid content");
80 throw io::IOException("OLE stream invalid content for " + sStreamName
+ "!");
83 aStreamsData
[sStreamName
] <<= oData
;
85 maStreamsSequence
= aStreamsData
.getAsConstNamedValueList();
88 bool DocumentDecryption::generateEncryptionKey(const OUString
& rPassword
)
90 if (mxPackageEncryption
.is())
91 return mxPackageEncryption
->generateEncryptionKey(rPassword
);
95 bool DocumentDecryption::readEncryptionInfo()
97 if (!mrOleStorage
.isStorage())
100 // Read 0x6DataSpaces/DataSpaceMap
101 uno::Reference
<io::XInputStream
> xDataSpaceMap
102 = mrOleStorage
.openInputStream(u
"\006DataSpaces/DataSpaceMap"_ustr
);
103 OUString sDataSpaceName
;
105 if (xDataSpaceMap
.is())
107 bool bBroken
= false;
109 BinaryXInputStream
aDataSpaceStream(xDataSpaceMap
, true);
110 sal_uInt32 aHeaderLength
= aDataSpaceStream
.readuInt32();
111 SAL_WARN_IF(aHeaderLength
!= 8, "oox",
112 "DataSpaceMap length != 8 is not supported. Some content may be skipped");
113 sal_uInt32 aEntryCount
= aDataSpaceStream
.readuInt32();
114 SAL_WARN_IF(aEntryCount
!= 1, "oox",
115 "DataSpaceMap contains more than one entry. Some content may be skipped");
117 // Read each DataSpaceMapEntry (MS-OFFCRYPTO 2.1.6.1)
118 for (sal_uInt32 i
= 0; i
< aEntryCount
&& !bBroken
; i
++)
120 // entryLen unused for the moment
121 aDataSpaceStream
.skip(sizeof(sal_uInt32
));
123 // Read each DataSpaceReferenceComponent (MS-OFFCRYPTO 2.1.6.2)
124 sal_uInt32 aReferenceComponentCount
= aDataSpaceStream
.readuInt32();
125 for (sal_uInt32 j
= 0; j
< aReferenceComponentCount
&& !bBroken
; j
++)
127 // Read next reference component
128 // refComponentType unused for the moment
129 aDataSpaceStream
.skip(sizeof(sal_uInt32
));
130 sal_uInt32 aReferenceComponentNameLength
= aDataSpaceStream
.readuInt32();
131 // sReferenceComponentName unused for the moment
132 if (aDataSpaceStream
.getRemaining() < aReferenceComponentNameLength
)
137 aDataSpaceStream
.readUnicodeArray(aReferenceComponentNameLength
/ 2);
138 aDataSpaceStream
.skip((4 - (aReferenceComponentNameLength
& 3))
139 & 3); // Skip padding
141 bBroken
|= aDataSpaceStream
.isEof();
144 sal_uInt32 aDataSpaceNameLength
= aDataSpaceStream
.readuInt32();
145 if (aDataSpaceStream
.getRemaining() < aDataSpaceNameLength
)
150 sDataSpaceName
= aDataSpaceStream
.readUnicodeArray(aDataSpaceNameLength
/ 2);
151 aDataSpaceStream
.skip((4 - (aDataSpaceNameLength
& 3)) & 3); // Skip padding
153 bBroken
|= aDataSpaceStream
.isEof();
158 SAL_WARN("oox", "EOF on parsing DataSpaceMapEntry table");
164 // Fallback for documents generated by LO: they sometimes do not have all
165 // required by MS-OFFCRYPTO specification streams (0x6DataSpaces/DataSpaceMap and others)
166 SAL_WARN("oox", "Encrypted package does not contain DataSpaceMap");
167 sDataSpaceName
= "StrongEncryptionDataSpace";
170 uno::Sequence
<uno::Any
> aArguments
;
171 mxPackageEncryption
.set(
172 mxContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
173 "com.sun.star.comp.oox.crypto." + sDataSpaceName
, aArguments
, mxContext
),
174 css::uno::UNO_QUERY
);
176 if (!mxPackageEncryption
.is())
178 // we do not know how to decrypt this document
182 return mxPackageEncryption
->readEncryptionInfo(maStreamsSequence
);
185 uno::Sequence
<beans::NamedValue
> DocumentDecryption::createEncryptionData(const OUString
& rPassword
)
187 if (!mxPackageEncryption
.is())
188 return uno::Sequence
<beans::NamedValue
>();
190 return mxPackageEncryption
->createEncryptionData(rPassword
);
193 bool DocumentDecryption::decrypt(const uno::Reference
<io::XStream
>& xDocumentStream
)
195 bool bResult
= false;
197 if (!mrOleStorage
.isStorage())
200 if (!mxPackageEncryption
.is())
203 // open the required input streams in the encrypted package
204 uno::Reference
<io::XInputStream
> xEncryptedPackage
205 = mrOleStorage
.openInputStream(u
"EncryptedPackage"_ustr
);
207 // create temporary file for unencrypted package
208 uno::Reference
<io::XOutputStream
> xDecryptedPackage
= xDocumentStream
->getOutputStream();
210 bResult
= mxPackageEncryption
->decrypt(xEncryptedPackage
, xDecryptedPackage
);
212 css::uno::Reference
<io::XSeekable
> xSeekable(xDecryptedPackage
, css::uno::UNO_QUERY
);
216 return mxPackageEncryption
->checkDataIntegrity();
221 } // namespace oox::crypto
223 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */