1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  */
10 
11 #include <oox/crypto/AgileEngine.hxx>
12 
13 #include <oox/helper/binaryinputstream.hxx>
14 #include <oox/helper/binaryoutputstream.hxx>
15 
16 #include <sax/tools/converter.hxx>
17 
18 #include <comphelper/hash.hxx>
19 #include <comphelper/docpasswordhelper.hxx>
20 #include <comphelper/random.hxx>
21 #include <comphelper/processfactory.hxx>
22 #include <comphelper/base64.hxx>
23 #include <comphelper/sequence.hxx>
24 
25 #include <filter/msfilter/mscodec.hxx>
26 #include <tools/stream.hxx>
27 #include <tools/XmlWriter.hxx>
28 
29 #include <com/sun/star/io/XSeekable.hpp>
30 #include <com/sun/star/io/XStream.hpp>
31 #include <com/sun/star/uno/XComponentContext.hpp>
32 #include <com/sun/star/xml/sax/XFastParser.hpp>
33 #include <com/sun/star/xml/sax/XFastTokenHandler.hpp>
34 #include <com/sun/star/xml/sax/FastParser.hpp>
35 #include <com/sun/star/xml/sax/FastToken.hpp>
36 
37 using namespace css;
38 using namespace css::beans;
39 using namespace css::io;
40 using namespace css::lang;
41 using namespace css::uno;
42 using namespace css::xml::sax;
43 using namespace css::xml;
44 
45 namespace oox {
46 namespace core {
47 
48 namespace {
49 
stripNamespacePrefix(OUString const & rsInputName)50 OUString stripNamespacePrefix(OUString const & rsInputName)
51 {
52     return rsInputName.copy(rsInputName.indexOf(":") + 1);
53 }
54 
55 class AgileTokenHandler : public cppu::WeakImplHelper<XFastTokenHandler>
56 {
57 public:
getTokenFromUTF8(const Sequence<sal_Int8> &)58     virtual sal_Int32 SAL_CALL getTokenFromUTF8(const Sequence< sal_Int8 >& /*nIdentifier*/) override
59     {
60         return FastToken::DONTKNOW;
61     }
62 
getUTF8Identifier(sal_Int32)63     virtual Sequence<sal_Int8> SAL_CALL getUTF8Identifier(sal_Int32 /*nToken*/) override
64     {
65         return Sequence<sal_Int8>();
66     }
67 };
68 
69 class AgileDocumentHandler : public ::cppu::WeakImplHelper<XFastDocumentHandler>
70 {
71     AgileEncryptionInfo& mInfo;
72 
73 public:
AgileDocumentHandler(AgileEncryptionInfo & rInfo)74     explicit AgileDocumentHandler(AgileEncryptionInfo& rInfo) :
75         mInfo(rInfo)
76     {}
77 
startDocument()78     void SAL_CALL startDocument() override {}
endDocument()79     void SAL_CALL endDocument() override {}
processingInstruction(const OUString &,const OUString &)80     void SAL_CALL processingInstruction( const OUString& /*rTarget*/, const OUString& /*rData*/ ) override {}
setDocumentLocator(const Reference<XLocator> &)81     void SAL_CALL setDocumentLocator( const Reference< XLocator >& /*xLocator*/ ) override {}
startFastElement(sal_Int32,const Reference<XFastAttributeList> &)82     void SAL_CALL startFastElement( sal_Int32 /*Element*/, const Reference< XFastAttributeList >& /*Attribs*/ ) override {}
83 
startUnknownElement(const OUString &,const OUString & rName,const Reference<XFastAttributeList> & aAttributeList)84     void SAL_CALL startUnknownElement( const OUString& /*aNamespace*/, const OUString& rName, const Reference< XFastAttributeList >& aAttributeList ) override
85     {
86         const OUString& rLocalName = stripNamespacePrefix(rName);
87 
88         const css::uno::Sequence<Attribute> aUnknownAttributes = aAttributeList->getUnknownAttributes();
89         for (const Attribute& rAttribute : aUnknownAttributes)
90         {
91             const OUString& rAttrLocalName = stripNamespacePrefix(rAttribute.Name);
92 
93             if (rAttrLocalName == "spinCount")
94             {
95                 ::sax::Converter::convertNumber(mInfo.spinCount, rAttribute.Value);
96             }
97             else if (rAttrLocalName == "saltSize")
98             {
99                 ::sax::Converter::convertNumber(mInfo.saltSize, rAttribute.Value);
100             }
101             else if (rAttrLocalName == "blockSize")
102             {
103                 ::sax::Converter::convertNumber(mInfo.blockSize, rAttribute.Value);
104             }
105             else if (rAttrLocalName == "keyBits")
106             {
107                 ::sax::Converter::convertNumber(mInfo.keyBits, rAttribute.Value);
108             }
109             else if (rAttrLocalName == "hashSize")
110             {
111                 ::sax::Converter::convertNumber(mInfo.hashSize, rAttribute.Value);
112             }
113             else if (rAttrLocalName == "cipherAlgorithm")
114             {
115                 mInfo.cipherAlgorithm = rAttribute.Value;
116             }
117             else if (rAttrLocalName == "cipherChaining")
118             {
119                 mInfo.cipherChaining = rAttribute.Value;
120             }
121             else if (rAttrLocalName == "hashAlgorithm")
122             {
123                 mInfo.hashAlgorithm = rAttribute.Value;
124             }
125             else if (rAttrLocalName == "saltValue")
126             {
127                 Sequence<sal_Int8> saltValue;
128                 comphelper::Base64::decode(saltValue, rAttribute.Value);
129                 if (rLocalName == "encryptedKey")
130                     mInfo.saltValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(saltValue);
131                 else if (rLocalName == "keyData")
132                     mInfo.keyDataSalt = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(saltValue);
133             }
134             else if (rAttrLocalName == "encryptedVerifierHashInput")
135             {
136                 Sequence<sal_Int8> encryptedVerifierHashInput;
137                 comphelper::Base64::decode(encryptedVerifierHashInput, rAttribute.Value);
138                 mInfo.encryptedVerifierHashInput = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedVerifierHashInput);
139             }
140             else if (rAttrLocalName == "encryptedVerifierHashValue")
141             {
142                 Sequence<sal_Int8> encryptedVerifierHashValue;
143                 comphelper::Base64::decode(encryptedVerifierHashValue, rAttribute.Value);
144                 mInfo.encryptedVerifierHashValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedVerifierHashValue);
145             }
146             else if (rAttrLocalName == "encryptedKeyValue")
147             {
148                 Sequence<sal_Int8> encryptedKeyValue;
149                 comphelper::Base64::decode(encryptedKeyValue, rAttribute.Value);
150                 mInfo.encryptedKeyValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedKeyValue);
151             }
152             if (rAttrLocalName == "encryptedHmacKey")
153             {
154                 Sequence<sal_Int8> aValue;
155                 comphelper::Base64::decode(aValue, rAttribute.Value);
156                 mInfo.hmacEncryptedKey = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(aValue);
157             }
158             if (rAttrLocalName == "encryptedHmacValue")
159             {
160                 Sequence<sal_Int8> aValue;
161                 comphelper::Base64::decode(aValue, rAttribute.Value);
162                 mInfo.hmacEncryptedValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(aValue);
163             }
164         }
165     }
166 
endFastElement(sal_Int32)167     void SAL_CALL endFastElement( sal_Int32 /*aElement*/ ) override
168     {}
endUnknownElement(const OUString &,const OUString &)169     void SAL_CALL endUnknownElement( const OUString& /*aNamespace*/, const OUString& /*aName*/ ) override
170     {}
171 
createFastChildContext(sal_Int32,const Reference<XFastAttributeList> &)172     Reference< XFastContextHandler > SAL_CALL createFastChildContext( sal_Int32 /*aElement*/, const Reference< XFastAttributeList >& /*aAttribs*/ ) override
173     {
174         return nullptr;
175     }
176 
createUnknownChildContext(const OUString &,const OUString &,const Reference<XFastAttributeList> &)177     Reference< XFastContextHandler > SAL_CALL createUnknownChildContext( const OUString& /*aNamespace*/, const OUString& /*aName*/, const Reference< XFastAttributeList >& /*aAttribs*/ ) override
178     {
179         return this;
180     }
181 
characters(const OUString &)182     void SAL_CALL characters( const OUString& /*aChars*/ ) override
183     {}
184 };
185 
186 constexpr const sal_uInt32 constSegmentLength = 4096;
187 
188 static const std::vector<sal_uInt8> constBlock1 { 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79 };
189 static const std::vector<sal_uInt8> constBlock2 { 0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e };
190 static const std::vector<sal_uInt8> constBlock3 { 0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6 };
191 static const std::vector<sal_uInt8> constBlockHmac1 { 0x5f, 0xb2, 0xad, 0x01, 0x0c, 0xb9, 0xe1, 0xf6 };
192 static const std::vector<sal_uInt8> constBlockHmac2 { 0xa0, 0x67, 0x7f, 0x02, 0xb2, 0x2c, 0x84, 0x33 };
193 
hashCalc(std::vector<sal_uInt8> & output,std::vector<sal_uInt8> & input,const OUString & sAlgorithm)194 bool hashCalc(std::vector<sal_uInt8>& output,
195               std::vector<sal_uInt8>& input,
196               const OUString& sAlgorithm )
197 {
198     if (sAlgorithm == "SHA1")
199     {
200         std::vector<unsigned char> out = comphelper::Hash::calculateHash(input.data(), input.size(), comphelper::HashType::SHA1);
201         output = out;
202         return true;
203     }
204     else if (sAlgorithm == "SHA512")
205     {
206         std::vector<unsigned char> out = comphelper::Hash::calculateHash(input.data(), input.size(), comphelper::HashType::SHA512);
207         output = out;
208         return true;
209     }
210     return false;
211 }
212 
cryptoHashTypeFromString(OUString const & sAlgorithm)213 CryptoHashType cryptoHashTypeFromString(OUString const & sAlgorithm)
214 {
215     if (sAlgorithm == "SHA512")
216         return CryptoHashType::SHA512;
217     return CryptoHashType::SHA1;
218 }
219 
220 } // namespace
221 
AgileEngine()222 AgileEngine::AgileEngine()
223     : meEncryptionPreset(AgileEncryptionPreset::AES_256_SHA512)
224 {}
225 
cryptoType(const AgileEncryptionInfo & rInfo)226 Crypto::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo)
227 {
228     if (rInfo.keyBits == 128 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC")
229         return Crypto::AES_128_CBC;
230     else if (rInfo.keyBits == 256 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC")
231         return Crypto::AES_256_CBC;
232     return Crypto::UNKNOWN;
233 }
234 
calculateIV(comphelper::HashType eType,std::vector<sal_uInt8> const & rSalt,std::vector<sal_uInt8> const & rBlock,sal_Int32 nCipherBlockSize)235 static std::vector<sal_uInt8> calculateIV(comphelper::HashType eType,
236                              std::vector<sal_uInt8> const & rSalt,
237                              std::vector<sal_uInt8> const & rBlock,
238                              sal_Int32 nCipherBlockSize)
239 {
240     comphelper::Hash aHasher(eType);
241     aHasher.update(rSalt.data(), rSalt.size());
242     aHasher.update(rBlock.data(), rBlock.size());
243     std::vector<sal_uInt8> aIV = aHasher.finalize();
244     aIV.resize(roundUp(sal_Int32(aIV.size()), nCipherBlockSize), 0x36);
245     return aIV;
246 }
247 
calculateBlock(std::vector<sal_uInt8> const & rBlock,std::vector<sal_uInt8> & rHashFinal,std::vector<sal_uInt8> & rInput,std::vector<sal_uInt8> & rOutput)248 void AgileEngine::calculateBlock(
249     std::vector<sal_uInt8> const & rBlock,
250     std::vector<sal_uInt8>& rHashFinal,
251     std::vector<sal_uInt8>& rInput,
252     std::vector<sal_uInt8>& rOutput)
253 {
254     std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
255     std::vector<sal_uInt8> dataFinal(mInfo.hashSize + rBlock.size(), 0);
256     std::copy(rHashFinal.begin(), rHashFinal.end(), dataFinal.begin());
257     std::copy(rBlock.begin(), rBlock.end(), dataFinal.begin() + mInfo.hashSize);
258 
259     hashCalc(hash, dataFinal, mInfo.hashAlgorithm);
260 
261     sal_Int32 keySize = mInfo.keyBits / 8;
262     std::vector<sal_uInt8> key(keySize, 0);
263 
264     std::copy(hash.begin(), hash.begin() + keySize, key.begin());
265 
266     Decrypt aDecryptor(key, mInfo.saltValue, cryptoType(mInfo));
267     aDecryptor.update(rOutput, rInput);
268 }
269 
encryptBlock(std::vector<sal_uInt8> const & rBlock,std::vector<sal_uInt8> & rHashFinal,std::vector<sal_uInt8> & rInput,std::vector<sal_uInt8> & rOutput)270 void AgileEngine::encryptBlock(
271     std::vector<sal_uInt8> const & rBlock,
272     std::vector<sal_uInt8> & rHashFinal,
273     std::vector<sal_uInt8> & rInput,
274     std::vector<sal_uInt8> & rOutput)
275 {
276     std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
277     std::vector<sal_uInt8> dataFinal(mInfo.hashSize + rBlock.size(), 0);
278     std::copy(rHashFinal.begin(), rHashFinal.end(), dataFinal.begin());
279     std::copy(rBlock.begin(), rBlock.end(), dataFinal.begin() + mInfo.hashSize);
280 
281     hashCalc(hash, dataFinal, mInfo.hashAlgorithm);
282 
283     sal_Int32 keySize = mInfo.keyBits / 8;
284     std::vector<sal_uInt8> key(keySize, 0);
285 
286     std::copy(hash.begin(), hash.begin() + keySize, key.begin());
287 
288     Encrypt aEncryptor(key, mInfo.saltValue, cryptoType(mInfo));
289 
290     aEncryptor.update(rOutput, rInput);
291 }
292 
calculateHashFinal(const OUString & rPassword,std::vector<sal_uInt8> & aHashFinal)293 void AgileEngine::calculateHashFinal(const OUString& rPassword, std::vector<sal_uInt8>& aHashFinal)
294 {
295     aHashFinal = comphelper::DocPasswordHelper::GetOoxHashAsVector(
296                     rPassword, mInfo.saltValue, mInfo.spinCount,
297                     comphelper::Hash::IterCount::PREPEND, mInfo.hashAlgorithm);
298 }
299 
300 namespace
301 {
302 
generateBytes(std::vector<sal_uInt8> & rBytes,sal_Int32 nSize)303 bool generateBytes(std::vector<sal_uInt8> & rBytes, sal_Int32 nSize)
304 {
305     size_t nMax = std::min(rBytes.size(), size_t(nSize));
306 
307     for (size_t i = 0; i < nMax; ++i)
308     {
309         rBytes[i] = sal_uInt8(comphelper::rng::uniform_uint_distribution(0, 0xFF));
310     }
311 
312     return true;
313 }
314 
315 } // end anonymous namespace
316 
decryptAndCheckVerifierHash(OUString const & rPassword)317 bool AgileEngine::decryptAndCheckVerifierHash(OUString const & rPassword)
318 {
319     std::vector<sal_uInt8> hashFinal(mInfo.hashSize, 0);
320     calculateHashFinal(rPassword, hashFinal);
321 
322     std::vector<sal_uInt8>& encryptedHashInput = mInfo.encryptedVerifierHashInput;
323     std::vector<sal_uInt8> hashInput(mInfo.saltSize, 0);
324     calculateBlock(constBlock1, hashFinal, encryptedHashInput, hashInput);
325 
326     std::vector<sal_uInt8>& encryptedHashValue = mInfo.encryptedVerifierHashValue;
327     std::vector<sal_uInt8> hashValue(encryptedHashValue.size(), 0);
328     calculateBlock(constBlock2, hashFinal, encryptedHashValue, hashValue);
329 
330     std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
331     hashCalc(hash, hashInput, mInfo.hashAlgorithm);
332 
333     return (hash.size() <= hashValue.size() && std::equal(hash.begin(), hash.end(), hashValue.begin()));
334 }
335 
decryptEncryptionKey(OUString const & rPassword)336 void AgileEngine::decryptEncryptionKey(OUString const & rPassword)
337 {
338     sal_Int32 nKeySize = mInfo.keyBits / 8;
339 
340     mKey.clear();
341     mKey.resize(nKeySize, 0);
342 
343     std::vector<sal_uInt8> aPasswordHash(mInfo.hashSize, 0);
344     calculateHashFinal(rPassword, aPasswordHash);
345 
346     calculateBlock(constBlock3, aPasswordHash, mInfo.encryptedKeyValue, mKey);
347 }
348 
349 // TODO: Rename
generateEncryptionKey(OUString const & rPassword)350 bool AgileEngine::generateEncryptionKey(OUString const & rPassword)
351 {
352     bool bResult = decryptAndCheckVerifierHash(rPassword);
353 
354     if (bResult)
355     {
356         decryptEncryptionKey(rPassword);
357         decryptHmacKey();
358         decryptHmacValue();
359     }
360     return bResult;
361 }
362 
decryptHmacKey()363 bool AgileEngine::decryptHmacKey()
364 {
365     // Initialize hmacKey
366     mInfo.hmacKey.clear();
367     mInfo.hmacKey.resize(mInfo.hmacEncryptedKey.size(), 0);
368 
369     // Calculate IV
370     comphelper::HashType eType;
371     if (mInfo.hashAlgorithm == "SHA1")
372         eType = comphelper::HashType::SHA1;
373     else if (mInfo.hashAlgorithm == "SHA512")
374         eType = comphelper::HashType::SHA512;
375     else
376         return false;
377 
378     std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac1, mInfo.blockSize);
379 
380     // Decrypt without key, calculated iv
381     Decrypt aDecrypt(mKey, iv, cryptoType(mInfo));
382     aDecrypt.update(mInfo.hmacKey, mInfo.hmacEncryptedKey);
383 
384     mInfo.hmacKey.resize(mInfo.hashSize, 0);
385 
386     return true;
387 }
388 
decryptHmacValue()389 bool AgileEngine::decryptHmacValue()
390 {
391     // Initialize hmacHash
392     mInfo.hmacHash.clear();
393     mInfo.hmacHash.resize(mInfo.hmacEncryptedValue.size(), 0);
394 
395     // Calculate IV
396     comphelper::HashType eType;
397     if (mInfo.hashAlgorithm == "SHA1")
398         eType = comphelper::HashType::SHA1;
399     else if (mInfo.hashAlgorithm == "SHA512")
400         eType = comphelper::HashType::SHA512;
401     else
402         return false;
403     std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac2, mInfo.blockSize);
404 
405     // Decrypt without key, calculated iv
406     Decrypt aDecrypt(mKey, iv, cryptoType(mInfo));
407     aDecrypt.update(mInfo.hmacHash, mInfo.hmacEncryptedValue);
408 
409     mInfo.hmacHash.resize(mInfo.hashSize, 0);
410 
411     return true;
412 }
413 
checkDataIntegrity()414 bool AgileEngine::checkDataIntegrity()
415 {
416     bool bResult = (mInfo.hmacHash.size() == mInfo.hmacCalculatedHash.size() &&
417                std::equal(mInfo.hmacHash.begin(), mInfo.hmacHash.end(), mInfo.hmacCalculatedHash.begin()));
418 
419     return bResult;
420 }
421 
decrypt(BinaryXInputStream & aInputStream,BinaryXOutputStream & aOutputStream)422 bool AgileEngine::decrypt(BinaryXInputStream& aInputStream,
423                           BinaryXOutputStream& aOutputStream)
424 {
425     CryptoHash aCryptoHash(mInfo.hmacKey, cryptoHashTypeFromString(mInfo.hashAlgorithm));
426 
427     sal_uInt32 totalSize = aInputStream.readuInt32(); // Document unencrypted size - 4 bytes
428     // account for size in HMAC
429     std::vector<sal_uInt8> aSizeBytes(sizeof(sal_uInt32));
430     ByteOrderConverter::writeLittleEndian(aSizeBytes.data(), totalSize);
431     aCryptoHash.update(aSizeBytes);
432 
433     aInputStream.skip(4);  // Reserved 4 Bytes
434     // account for reserved 4 bytes (must be 0)
435     std::vector<sal_uInt8> aReserved{0,0,0,0};
436     aCryptoHash.update(aReserved);
437 
438     // setup decryption
439     std::vector<sal_uInt8>& keyDataSalt = mInfo.keyDataSalt;
440 
441     sal_uInt32 saltSize = mInfo.saltSize;
442     sal_uInt32 keySize = mInfo.keyBits / 8;
443 
444     sal_uInt32 segment = 0;
445 
446     std::vector<sal_uInt8> saltWithBlockKey(saltSize + sizeof(segment), 0);
447     std::copy(keyDataSalt.begin(), keyDataSalt.end(), saltWithBlockKey.begin());
448 
449     std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
450     std::vector<sal_uInt8> iv(keySize, 0);
451 
452     std::vector<sal_uInt8> inputBuffer(constSegmentLength);
453     std::vector<sal_uInt8> outputBuffer(constSegmentLength);
454     sal_uInt32 inputLength;
455     sal_uInt32 outputLength;
456     sal_uInt32 remaining = totalSize;
457 
458     while ((inputLength = aInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0)
459     {
460         sal_uInt8* segmentBegin = reinterpret_cast<sal_uInt8*>(&segment);
461         sal_uInt8* segmentEnd   = segmentBegin + sizeof(segment);
462         std::copy(segmentBegin, segmentEnd, saltWithBlockKey.begin() + saltSize);
463 
464         hashCalc(hash, saltWithBlockKey, mInfo.hashAlgorithm);
465 
466         // Only if hash > keySize
467         std::copy(hash.begin(), hash.begin() + keySize, iv.begin());
468 
469         Decrypt aDecryptor(mKey, iv, AgileEngine::cryptoType(mInfo));
470         outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength);
471 
472         sal_uInt32 writeLength = std::min(outputLength, remaining);
473 
474         aCryptoHash.update(inputBuffer, inputLength);
475 
476         aOutputStream.writeMemory(outputBuffer.data(), writeLength);
477 
478         remaining -= outputLength;
479         segment++;
480     }
481 
482     mInfo.hmacCalculatedHash = aCryptoHash.finalize();
483 
484     return true;
485 }
486 
readEncryptionInfo(uno::Reference<io::XInputStream> & rxInputStream)487 bool AgileEngine::readEncryptionInfo(uno::Reference<io::XInputStream> & rxInputStream)
488 {
489     // Check reserved value
490     std::vector<sal_uInt8> aExpectedReservedBytes(sizeof(sal_uInt32));
491     ByteOrderConverter::writeLittleEndian(aExpectedReservedBytes.data(), msfilter::AGILE_ENCRYPTION_RESERVED);
492 
493     uno::Sequence<sal_Int8> aReadReservedBytes(sizeof(sal_uInt32));
494     rxInputStream->readBytes(aReadReservedBytes, aReadReservedBytes.getLength());
495 
496     if (!std::equal(aReadReservedBytes.begin(), aReadReservedBytes.end(), aExpectedReservedBytes.begin()))
497         return false;
498 
499     mInfo.spinCount = 0;
500     mInfo.saltSize = 0;
501     mInfo.keyBits = 0;
502     mInfo.hashSize = 0;
503     mInfo.blockSize = 0;
504 
505     Reference<XFastDocumentHandler> xFastDocumentHandler(new AgileDocumentHandler(mInfo));
506     Reference<XFastTokenHandler> xFastTokenHandler(new AgileTokenHandler);
507 
508     Reference<XFastParser> xParser(css::xml::sax::FastParser::create(comphelper::getProcessComponentContext()));
509 
510     xParser->setFastDocumentHandler(xFastDocumentHandler);
511     xParser->setTokenHandler(xFastTokenHandler);
512 
513     InputSource aInputSource;
514     aInputSource.aInputStream = rxInputStream;
515     xParser->parseStream(aInputSource);
516 
517     // CHECK info data
518     if (2 > mInfo.blockSize || mInfo.blockSize > 4096)
519         return false;
520 
521     if (0 > mInfo.spinCount || mInfo.spinCount > 10000000)
522         return false;
523 
524     if (1 > mInfo.saltSize|| mInfo.saltSize > 65536) // Check
525         return false;
526 
527     // AES 128 CBC with SHA1
528     if (mInfo.keyBits         == 128 &&
529         mInfo.cipherAlgorithm == "AES" &&
530         mInfo.cipherChaining  == "ChainingModeCBC" &&
531         mInfo.hashAlgorithm   == "SHA1" &&
532         mInfo.hashSize        == msfilter::SHA1_HASH_LENGTH)
533     {
534         return true;
535     }
536 
537     // AES 256 CBC with SHA512
538     if (mInfo.keyBits         == 256 &&
539         mInfo.cipherAlgorithm == "AES" &&
540         mInfo.cipherChaining  == "ChainingModeCBC" &&
541         mInfo.hashAlgorithm   == "SHA512" &&
542         mInfo.hashSize        == msfilter::SHA512_HASH_LENGTH)
543     {
544         return true;
545     }
546 
547     return false;
548 }
549 
generateAndEncryptVerifierHash(OUString const & rPassword)550 bool AgileEngine::generateAndEncryptVerifierHash(OUString const & rPassword)
551 {
552     if (!generateBytes(mInfo.saltValue, mInfo.saltSize))
553         return false;
554 
555     std::vector<sal_uInt8> unencryptedVerifierHashInput(mInfo.saltSize);
556     if (!generateBytes(unencryptedVerifierHashInput, mInfo.saltSize))
557         return false;
558 
559     // HASH - needs to be modified to be multiple of block size
560     sal_Int32 nVerifierHash = roundUp(mInfo.hashSize, mInfo.blockSize);
561     std::vector<sal_uInt8> unencryptedVerifierHashValue;
562     if (!hashCalc(unencryptedVerifierHashValue, unencryptedVerifierHashInput, mInfo.hashAlgorithm))
563         return false;
564     unencryptedVerifierHashValue.resize(nVerifierHash, 0);
565 
566     std::vector<sal_uInt8> hashFinal(mInfo.hashSize, 0);
567     calculateHashFinal(rPassword, hashFinal);
568 
569     encryptBlock(constBlock1, hashFinal, unencryptedVerifierHashInput, mInfo.encryptedVerifierHashInput);
570 
571     encryptBlock(constBlock2, hashFinal, unencryptedVerifierHashValue, mInfo.encryptedVerifierHashValue);
572 
573     return true;
574 }
575 
encryptHmacKey()576 bool AgileEngine::encryptHmacKey()
577 {
578     // Initialize hmacKey
579     mInfo.hmacKey.clear();
580     mInfo.hmacKey.resize(mInfo.hashSize, 0);
581 
582     if (!generateBytes(mInfo.hmacKey, mInfo.hashSize))
583         return false;
584 
585     // Encrypted salt must be multiple of block size
586     sal_Int32 nEncryptedSaltSize = oox::core::roundUp(mInfo.hashSize, mInfo.blockSize);
587 
588     // We need to extend hmacSalt to multiple of block size, padding with 0x36
589     std::vector<sal_uInt8> extendedSalt(mInfo.hmacKey);
590     extendedSalt.resize(nEncryptedSaltSize, 0x36);
591 
592     // Initialize hmacEncryptedKey
593     mInfo.hmacEncryptedKey.clear();
594     mInfo.hmacEncryptedKey.resize(nEncryptedSaltSize, 0);
595 
596     // Calculate IV
597     comphelper::HashType eType;
598     if (mInfo.hashAlgorithm == "SHA1")
599         eType = comphelper::HashType::SHA1;
600     else if (mInfo.hashAlgorithm == "SHA512")
601         eType = comphelper::HashType::SHA512;
602     else
603         return false;
604 
605     std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac1, mInfo.blockSize);
606 
607     // Encrypt without key, calculated iv
608     Encrypt aEncryptor(mKey, iv, cryptoType(mInfo));
609     aEncryptor.update(mInfo.hmacEncryptedKey, extendedSalt);
610 
611     return true;
612 }
613 
encryptHmacValue()614 bool AgileEngine::encryptHmacValue()
615 {
616     sal_Int32 nEncryptedValueSize = roundUp(mInfo.hashSize, mInfo.blockSize);
617     mInfo.hmacEncryptedValue.clear();
618     mInfo.hmacEncryptedValue.resize(nEncryptedValueSize, 0);
619 
620     std::vector<sal_uInt8> extendedHash(mInfo.hmacHash);
621     extendedHash.resize(nEncryptedValueSize, 0x36);
622 
623     // Calculate IV
624     comphelper::HashType eType;
625     if (mInfo.hashAlgorithm == "SHA1")
626         eType = comphelper::HashType::SHA1;
627     else if (mInfo.hashAlgorithm == "SHA512")
628         eType = comphelper::HashType::SHA512;
629     else
630         return false;
631 
632     std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac2, mInfo.blockSize);
633 
634     // Encrypt without key, calculated iv
635     Encrypt aEncryptor(mKey, iv, cryptoType(mInfo));
636     aEncryptor.update(mInfo.hmacEncryptedValue, extendedHash);
637 
638     return true;
639 }
640 
encryptEncryptionKey(OUString const & rPassword)641 bool AgileEngine::encryptEncryptionKey(OUString const & rPassword)
642 {
643     sal_Int32 nKeySize = mInfo.keyBits / 8;
644 
645     mKey.clear();
646     mKey.resize(nKeySize, 0);
647 
648     mInfo.encryptedKeyValue.clear();
649     mInfo.encryptedKeyValue.resize(nKeySize, 0);
650 
651     if (!generateBytes(mKey, nKeySize))
652         return false;
653 
654     std::vector<sal_uInt8> aPasswordHash(mInfo.hashSize, 0);
655     calculateHashFinal(rPassword, aPasswordHash);
656 
657     encryptBlock(constBlock3, aPasswordHash, mKey, mInfo.encryptedKeyValue);
658 
659     return true;
660 }
661 
setupEncryption(OUString const & rPassword)662 bool AgileEngine::setupEncryption(OUString const & rPassword)
663 {
664     if (meEncryptionPreset == AgileEncryptionPreset::AES_128_SHA1)
665         setupEncryptionParameters({ 100000, 16, 128, 20, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA1") });
666     else
667         setupEncryptionParameters({ 100000, 16, 256, 64, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA512") });
668 
669     return setupEncryptionKey(rPassword);
670 }
671 
setupEncryptionParameters(AgileEncryptionParameters const & rAgileEncryptionParameters)672 void AgileEngine::setupEncryptionParameters(AgileEncryptionParameters const & rAgileEncryptionParameters)
673 {
674     mInfo.spinCount = rAgileEncryptionParameters.spinCount;
675     mInfo.saltSize = rAgileEncryptionParameters.saltSize;
676     mInfo.keyBits = rAgileEncryptionParameters.keyBits;
677     mInfo.hashSize = rAgileEncryptionParameters.hashSize;
678     mInfo.blockSize = rAgileEncryptionParameters.blockSize;
679 
680     mInfo.cipherAlgorithm = rAgileEncryptionParameters.cipherAlgorithm;
681     mInfo.cipherChaining = rAgileEncryptionParameters.cipherChaining;
682     mInfo.hashAlgorithm = rAgileEncryptionParameters.hashAlgorithm;
683 
684     mInfo.keyDataSalt.resize(mInfo.saltSize);
685     mInfo.saltValue.resize(mInfo.saltSize);
686     mInfo.encryptedVerifierHashInput.resize(mInfo.saltSize);
687     mInfo.encryptedVerifierHashValue.resize(roundUp(mInfo.hashSize, mInfo.blockSize), 0);
688 }
689 
setupEncryptionKey(OUString const & rPassword)690 bool AgileEngine::setupEncryptionKey(OUString const & rPassword)
691 {
692     if (!generateAndEncryptVerifierHash(rPassword))
693         return false;
694     if (!encryptEncryptionKey(rPassword))
695         return false;
696     if (!generateBytes(mInfo.keyDataSalt, mInfo.saltSize))
697         return false;
698     if (!encryptHmacKey())
699         return false;
700     return true;
701 }
702 
writeEncryptionInfo(BinaryXOutputStream & rStream)703 void AgileEngine::writeEncryptionInfo(BinaryXOutputStream & rStream)
704 {
705     rStream.WriteUInt32(msfilter::VERSION_INFO_AGILE);
706     rStream.WriteUInt32(msfilter::AGILE_ENCRYPTION_RESERVED);
707 
708     SvMemoryStream aMemStream;
709     tools::XmlWriter aXmlWriter(&aMemStream);
710 
711     if (aXmlWriter.startDocument(0/*nIndent*/))
712     {
713         aXmlWriter.startElement("", "encryption", "http://schemas.microsoft.com/office/2006/encryption");
714         aXmlWriter.attribute("xmlns:p", OString("http://schemas.microsoft.com/office/2006/keyEncryptor/password"));
715 
716         aXmlWriter.startElement("keyData");
717         aXmlWriter.attribute("saltSize", mInfo.saltSize);
718         aXmlWriter.attribute("blockSize", mInfo.blockSize);
719         aXmlWriter.attribute("keyBits", mInfo.keyBits);
720         aXmlWriter.attribute("hashSize", mInfo.hashSize);
721         aXmlWriter.attribute("cipherAlgorithm", mInfo.cipherAlgorithm);
722         aXmlWriter.attribute("cipherChaining", mInfo.cipherChaining);
723         aXmlWriter.attribute("hashAlgorithm", mInfo.hashAlgorithm);
724         aXmlWriter.attributeBase64("saltValue", mInfo.keyDataSalt);
725         aXmlWriter.endElement();
726 
727         aXmlWriter.startElement("dataIntegrity");
728         aXmlWriter.attributeBase64("encryptedHmacKey", mInfo.hmacEncryptedKey);
729         aXmlWriter.attributeBase64("encryptedHmacValue", mInfo.hmacEncryptedValue);
730         aXmlWriter.endElement();
731 
732         aXmlWriter.startElement("keyEncryptors");
733         aXmlWriter.startElement("keyEncryptor");
734         aXmlWriter.attribute("uri", OString("http://schemas.microsoft.com/office/2006/keyEncryptor/password"));
735 
736         aXmlWriter.startElement("p", "encryptedKey", "");
737         aXmlWriter.attribute("spinCount", mInfo.spinCount);
738         aXmlWriter.attribute("saltSize", mInfo.saltSize);
739         aXmlWriter.attribute("blockSize", mInfo.blockSize);
740         aXmlWriter.attribute("keyBits", mInfo.keyBits);
741         aXmlWriter.attribute("hashSize", mInfo.hashSize);
742         aXmlWriter.attribute("cipherAlgorithm", mInfo.cipherAlgorithm);
743         aXmlWriter.attribute("cipherChaining", mInfo.cipherChaining);
744         aXmlWriter.attribute("hashAlgorithm", mInfo.hashAlgorithm);
745         aXmlWriter.attributeBase64("saltValue", mInfo.saltValue);
746         aXmlWriter.attributeBase64("encryptedVerifierHashInput", mInfo.encryptedVerifierHashInput);
747         aXmlWriter.attributeBase64("encryptedVerifierHashValue", mInfo.encryptedVerifierHashValue);
748         aXmlWriter.attributeBase64("encryptedKeyValue", mInfo.encryptedKeyValue);
749         aXmlWriter.endElement();
750 
751         aXmlWriter.endElement();
752         aXmlWriter.endElement();
753 
754         aXmlWriter.endElement();
755         aXmlWriter.endDocument();
756     }
757     rStream.writeMemory(aMemStream.GetData(), aMemStream.GetSize());
758 }
759 
encrypt(css::uno::Reference<css::io::XInputStream> & rxInputStream,css::uno::Reference<css::io::XOutputStream> & rxOutputStream,sal_uInt32 nSize)760 void AgileEngine::encrypt(css::uno::Reference<css::io::XInputStream> &  rxInputStream,
761                           css::uno::Reference<css::io::XOutputStream> & rxOutputStream,
762                           sal_uInt32 nSize)
763 {
764     CryptoHash aCryptoHash(mInfo.hmacKey, cryptoHashTypeFromString(mInfo.hashAlgorithm));
765 
766     BinaryXOutputStream aBinaryOutputStream(rxOutputStream, false);
767     BinaryXInputStream aBinaryInputStream(rxInputStream, false);
768 
769     std::vector<sal_uInt8> aSizeBytes(sizeof(sal_uInt32));
770     ByteOrderConverter::writeLittleEndian(aSizeBytes.data(), nSize);
771     aBinaryOutputStream.writeMemory(aSizeBytes.data(), aSizeBytes.size()); // size
772     aCryptoHash.update(aSizeBytes, aSizeBytes.size());
773 
774     std::vector<sal_uInt8> aNull{0,0,0,0};
775     aBinaryOutputStream.writeMemory(aNull.data(), aNull.size()); // reserved
776     aCryptoHash.update(aNull, aNull.size());
777 
778     std::vector<sal_uInt8>& keyDataSalt = mInfo.keyDataSalt;
779 
780     sal_uInt32 saltSize = mInfo.saltSize;
781     sal_uInt32 keySize = mInfo.keyBits / 8;
782 
783     sal_uInt32 nSegment = 0;
784     sal_uInt32 nSegmentByteSize = sizeof(nSegment);
785 
786     std::vector<sal_uInt8> saltWithBlockKey(saltSize + nSegmentByteSize, 0);
787     std::copy(keyDataSalt.begin(), keyDataSalt.end(), saltWithBlockKey.begin());
788 
789     std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
790     std::vector<sal_uInt8> iv(keySize, 0);
791 
792     std::vector<sal_uInt8> inputBuffer(constSegmentLength);
793     std::vector<sal_uInt8> outputBuffer(constSegmentLength);
794     sal_uInt32 inputLength;
795     sal_uInt32 outputLength;
796 
797     while ((inputLength = aBinaryInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0)
798     {
799         sal_uInt32 correctedInputLength = inputLength % mInfo.blockSize == 0 ?
800                         inputLength : oox::core::roundUp(inputLength, sal_uInt32(mInfo.blockSize));
801 
802         // Update Key
803         sal_uInt8* segmentBegin = reinterpret_cast<sal_uInt8*>(&nSegment);
804         sal_uInt8* segmentEnd   = segmentBegin + nSegmentByteSize;
805         std::copy(segmentBegin, segmentEnd, saltWithBlockKey.begin() + saltSize);
806 
807         hashCalc(hash, saltWithBlockKey, mInfo.hashAlgorithm);
808 
809         // Only if hash > keySize
810         std::copy(hash.begin(), hash.begin() + keySize, iv.begin());
811 
812         Encrypt aEncryptor(mKey, iv, AgileEngine::cryptoType(mInfo));
813         outputLength = aEncryptor.update(outputBuffer, inputBuffer, correctedInputLength);
814         aBinaryOutputStream.writeMemory(outputBuffer.data(), outputLength);
815         aCryptoHash.update(outputBuffer, outputLength);
816 
817         nSegment++;
818     }
819     mInfo.hmacHash = aCryptoHash.finalize();
820     encryptHmacValue();
821 }
822 
823 } // namespace core
824 } // namespace oox
825 
826 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
827