Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / oox / source / crypto / DocumentDecryption.cxx
blob0209f3d24ba58c769ad66d1418797c3b4a8d5e53
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
9 */
11 #include "oox/crypto/DocumentDecryption.hxx"
13 #include <comphelper/sequenceashashmap.hxx>
14 #include <sax/tools/converter.hxx>
15 #include <cppuhelper/implbase.hxx>
17 #include <com/sun/star/io/XSeekable.hpp>
18 #include <com/sun/star/io/XStream.hpp>
19 #include <com/sun/star/uno/XComponentContext.hpp>
20 #include <com/sun/star/xml/sax/XFastParser.hpp>
21 #include <com/sun/star/xml/sax/XFastTokenHandler.hpp>
22 #include <com/sun/star/xml/sax/FastParser.hpp>
23 #include <com/sun/star/xml/sax/FastToken.hpp>
24 #include <oox/crypto/AgileEngine.hxx>
25 #include <oox/crypto/Standard2007Engine.hxx>
26 #include <oox/helper/binaryinputstream.hxx>
27 #include <oox/helper/binaryoutputstream.hxx>
28 #include <oox/ole/olestorage.hxx>
30 namespace oox {
31 namespace core {
33 using namespace css::beans;
34 using namespace css::io;
35 using namespace css::lang;
36 using namespace css::uno;
37 using namespace css::xml::sax;
38 using namespace css::xml;
40 namespace {
42 std::vector<sal_uInt8> convertToVector(Sequence<sal_Int8>& input)
44 const sal_uInt8* inputArray = reinterpret_cast<const sal_uInt8*>(input.getConstArray());
45 return std::vector<sal_uInt8>(inputArray, inputArray + input.getLength());
48 class AgileTokenHandler : public cppu::WeakImplHelper<XFastTokenHandler>
50 public:
51 virtual sal_Int32 SAL_CALL getTokenFromUTF8( const Sequence< sal_Int8 >& /*nIdentifier*/ ) override
53 return FastToken::DONTKNOW;
56 virtual Sequence<sal_Int8> SAL_CALL getUTF8Identifier(sal_Int32 /*nToken*/) override
58 return Sequence<sal_Int8>();
62 class AgileDocumentHandler : public ::cppu::WeakImplHelper<XFastDocumentHandler>
64 AgileEncryptionInfo& mInfo;
66 public:
67 explicit AgileDocumentHandler(AgileEncryptionInfo& rInfo) :
68 mInfo(rInfo)
71 void SAL_CALL startDocument() override
73 void SAL_CALL endDocument() override
75 void SAL_CALL setDocumentLocator( const Reference< XLocator >& /*xLocator*/ ) override
77 void SAL_CALL startFastElement( sal_Int32 /*Element*/, const Reference< XFastAttributeList >& /*Attribs*/ ) override
80 void SAL_CALL startUnknownElement( const OUString& /*aNamespace*/, const OUString& rName, const Reference< XFastAttributeList >& aAttributeList ) override
82 if (rName == "keyData")
84 for (const Attribute& rAttribute : aAttributeList->getUnknownAttributes())
86 if (rAttribute.Name == "saltValue")
88 Sequence<sal_Int8> keyDataSalt;
89 ::sax::Converter::decodeBase64(keyDataSalt, rAttribute.Value);
90 mInfo.keyDataSalt = convertToVector(keyDataSalt);
94 else if (rName == "encryptedKey")
96 for (const Attribute& rAttribute : aAttributeList->getUnknownAttributes())
98 if (rAttribute.Name == "spinCount")
100 ::sax::Converter::convertNumber(mInfo.spinCount, rAttribute.Value);
102 else if (rAttribute.Name == "saltSize")
104 ::sax::Converter::convertNumber(mInfo.saltSize, rAttribute.Value);
106 else if (rAttribute.Name == "blockSize")
108 ::sax::Converter::convertNumber(mInfo.blockSize, rAttribute.Value);
110 else if (rAttribute.Name == "keyBits")
112 ::sax::Converter::convertNumber(mInfo.keyBits, rAttribute.Value);
114 else if (rAttribute.Name == "hashSize")
116 ::sax::Converter::convertNumber(mInfo.hashSize, rAttribute.Value);
118 else if (rAttribute.Name == "cipherAlgorithm")
120 mInfo.cipherAlgorithm = rAttribute.Value;
122 else if (rAttribute.Name == "cipherChaining")
124 mInfo.cipherChaining = rAttribute.Value;
126 else if (rAttribute.Name == "hashAlgorithm")
128 mInfo.hashAlgorithm = rAttribute.Value;
130 else if (rAttribute.Name == "saltValue")
132 Sequence<sal_Int8> saltValue;
133 ::sax::Converter::decodeBase64(saltValue, rAttribute.Value);
134 mInfo.saltValue = convertToVector(saltValue);
136 else if (rAttribute.Name == "encryptedVerifierHashInput")
138 Sequence<sal_Int8> encryptedVerifierHashInput;
139 ::sax::Converter::decodeBase64(encryptedVerifierHashInput, rAttribute.Value);
140 mInfo.encryptedVerifierHashInput = convertToVector(encryptedVerifierHashInput);
142 else if (rAttribute.Name == "encryptedVerifierHashValue")
144 Sequence<sal_Int8> encryptedVerifierHashValue;
145 ::sax::Converter::decodeBase64(encryptedVerifierHashValue, rAttribute.Value);
146 mInfo.encryptedVerifierHashValue = convertToVector(encryptedVerifierHashValue);
148 else if (rAttribute.Name == "encryptedKeyValue")
150 Sequence<sal_Int8> encryptedKeyValue;
151 ::sax::Converter::decodeBase64(encryptedKeyValue, rAttribute.Value);
152 mInfo.encryptedKeyValue = convertToVector(encryptedKeyValue);
158 void SAL_CALL endFastElement( sal_Int32 /*aElement*/ ) override
160 void SAL_CALL endUnknownElement( const OUString& /*aNamespace*/, const OUString& /*aName*/ ) override
163 Reference< XFastContextHandler > SAL_CALL createFastChildContext( sal_Int32 /*aElement*/, const Reference< XFastAttributeList >& /*aAttribs*/ ) override
165 return nullptr;
168 Reference< XFastContextHandler > SAL_CALL createUnknownChildContext( const OUString& /*aNamespace*/, const OUString& /*aName*/, const Reference< XFastAttributeList >& /*aAttribs*/ ) override
170 return this;
173 void SAL_CALL characters( const OUString& /*aChars*/ ) override
177 } // namespace
179 DocumentDecryption::DocumentDecryption(oox::ole::OleStorage& rOleStorage, Reference<XComponentContext> const & xContext) :
180 mxContext(xContext),
181 mrOleStorage(rOleStorage),
182 mCryptoType(UNKNOWN)
185 bool DocumentDecryption::generateEncryptionKey(const OUString& rPassword)
187 if (mEngine.get())
188 return mEngine->generateEncryptionKey(rPassword);
189 return false;
192 bool DocumentDecryption::readAgileEncryptionInfo(Reference< XInputStream >& xInputStream)
194 AgileEngine* engine = new AgileEngine;
195 mEngine.reset(engine);
196 AgileEncryptionInfo& info = engine->getInfo();
197 info.spinCount = 0;
198 info.saltSize = 0;
199 info.keyBits = 0;
200 info.hashSize = 0;
201 info.blockSize = 0;
203 Reference<XFastDocumentHandler> xFastDocumentHandler( new AgileDocumentHandler(info) );
204 Reference<XFastTokenHandler> xFastTokenHandler ( new AgileTokenHandler );
206 Reference<XFastParser> xParser(css::xml::sax::FastParser::create(mxContext));
208 xParser->setFastDocumentHandler(xFastDocumentHandler);
209 xParser->setTokenHandler(xFastTokenHandler);
211 InputSource aInputSource;
212 aInputSource.aInputStream = xInputStream;
213 xParser->parseStream(aInputSource);
215 // CHECK info data
216 if (2 > info.blockSize || info.blockSize > 4096)
217 return false;
219 if (0 > info.spinCount || info.spinCount > 10000000)
220 return false;
222 if (1 > info.saltSize|| info.saltSize > 65536) // Check
223 return false;
225 // AES 128 CBC with SHA1
226 if (info.keyBits == 128 &&
227 info.cipherAlgorithm == "AES" &&
228 info.cipherChaining == "ChainingModeCBC" &&
229 info.hashAlgorithm == "SHA1" &&
230 info.hashSize == msfilter::SHA1_HASH_LENGTH)
232 return true;
235 // AES 256 CBC with SHA512
236 if (info.keyBits == 256 &&
237 info.cipherAlgorithm == "AES" &&
238 info.cipherChaining == "ChainingModeCBC" &&
239 info.hashAlgorithm == "SHA512" &&
240 info.hashSize == msfilter::SHA512_HASH_LENGTH)
242 return true;
245 return false;
248 bool DocumentDecryption::readStandard2007EncryptionInfo(BinaryInputStream& rStream)
250 Standard2007Engine* engine = new Standard2007Engine;
251 mEngine.reset(engine);
252 msfilter::StandardEncryptionInfo& info = engine->getInfo();
254 info.header.flags = rStream.readuInt32();
255 if (getFlag(info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
256 return false;
258 sal_uInt32 nHeaderSize = rStream.readuInt32();
260 sal_uInt32 actualHeaderSize = sizeof(info.header);
262 if (nHeaderSize < actualHeaderSize)
263 return false;
265 info.header.flags = rStream.readuInt32();
266 info.header.sizeExtra = rStream.readuInt32();
267 info.header.algId = rStream.readuInt32();
268 info.header.algIdHash = rStream.readuInt32();
269 info.header.keyBits = rStream.readuInt32();
270 info.header.providedType = rStream.readuInt32();
271 info.header.reserved1 = rStream.readuInt32();
272 info.header.reserved2 = rStream.readuInt32();
274 rStream.skip(nHeaderSize - actualHeaderSize);
276 info.verifier.saltSize = rStream.readuInt32();
277 rStream.readArray(info.verifier.salt, SAL_N_ELEMENTS(info.verifier.salt));
278 rStream.readArray(info.verifier.encryptedVerifier, SAL_N_ELEMENTS(info.verifier.encryptedVerifier));
279 info.verifier.encryptedVerifierHashSize = rStream.readuInt32();
280 rStream.readArray(info.verifier.encryptedVerifierHash, SAL_N_ELEMENTS(info.verifier.encryptedVerifierHash));
282 if (info.verifier.saltSize != 16)
283 return false;
285 // check flags and algorithm IDs, required are AES128 and SHA-1
286 if (!getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
287 return false;
289 if (!getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
290 return false;
292 // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set
293 if (info.header.algId != 0 && info.header.algId != msfilter::ENCRYPT_ALGO_AES128)
294 return false;
296 // hash algorithm ID 0 defaults to SHA-1 too
297 if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
298 return false;
300 if (info.verifier.encryptedVerifierHashSize != 20)
301 return false;
303 return !rStream.isEof();
306 bool DocumentDecryption::readEncryptionInfo()
308 if (!mrOleStorage.isStorage())
309 return false;
311 Reference<XInputStream> xEncryptionInfo(mrOleStorage.openInputStream("EncryptionInfo"), UNO_SET_THROW);
313 bool bResult = false;
315 BinaryXInputStream aBinaryInputStream(xEncryptionInfo, true);
317 sal_uInt32 aVersion = aBinaryInputStream.readuInt32();
319 switch (aVersion)
321 case msfilter::VERSION_INFO_2007_FORMAT:
322 case msfilter::VERSION_INFO_2007_FORMAT_SP2:
323 mCryptoType = STANDARD_2007; // Set encryption info format
324 bResult = readStandard2007EncryptionInfo( aBinaryInputStream );
325 break;
326 case msfilter::VERSION_INFO_AGILE:
327 mCryptoType = AGILE; // Set encryption info format
328 aBinaryInputStream.skip(4);
329 bResult = readAgileEncryptionInfo( xEncryptionInfo );
330 break;
331 default:
332 break;
335 return bResult;
338 Sequence<NamedValue> DocumentDecryption::createEncryptionData(const OUString& rPassword)
340 comphelper::SequenceAsHashMap aEncryptionData;
342 if (mCryptoType == AGILE)
344 aEncryptionData["CryptoType"] <<= OUString("Agile");
346 else if (mCryptoType == STANDARD_2007)
348 aEncryptionData["CryptoType"] <<= OUString("Standard");
351 aEncryptionData["OOXPassword"] <<= rPassword;
352 return aEncryptionData.getAsConstNamedValueList();
355 bool DocumentDecryption::decrypt(const Reference<XStream>& xDocumentStream)
357 bool aResult = false;
359 if (!mrOleStorage.isStorage())
360 return false;
362 // open the required input streams in the encrypted package
363 Reference<XInputStream> xEncryptedPackage(mrOleStorage.openInputStream("EncryptedPackage"), UNO_SET_THROW);
365 // create temporary file for unencrypted package
366 Reference<XOutputStream> xDecryptedPackage(xDocumentStream->getOutputStream(), UNO_SET_THROW);
367 BinaryXOutputStream aDecryptedPackage(xDecryptedPackage, true);
368 BinaryXInputStream aEncryptedPackage(xEncryptedPackage, true);
370 aResult = mEngine->decrypt(aEncryptedPackage, aDecryptedPackage);
372 xDecryptedPackage->flush();
373 aDecryptedPackage.seekToStart();
375 return aResult;
378 } // namespace core
379 } // namespace oox
381 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */