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