Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / oox / source / crypto / AgileEngine.cxx
blobae2568d0f3f6f3a33f9fe0f87066b43092949c12
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/AgileEngine.hxx>
13 #include <oox/helper/binaryinputstream.hxx>
14 #include <oox/helper/binaryoutputstream.hxx>
16 #include <sax/tools/converter.hxx>
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>
25 #include <filter/msfilter/mscodec.hxx>
26 #include <tools/stream.hxx>
27 #include <tools/XmlWriter.hxx>
28 #include <sax/fastattribs.hxx>
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>
35 using namespace css;
36 using namespace css::beans;
37 using namespace css::io;
38 using namespace css::lang;
39 using namespace css::uno;
40 using namespace css::xml::sax;
41 using namespace css::xml;
43 namespace oox::crypto {
45 namespace {
47 std::u16string_view stripNamespacePrefix(std::u16string_view rsInputName)
49 size_t idx = rsInputName.find(':');
50 if (idx == std::u16string_view::npos)
51 return rsInputName;
52 return rsInputName.substr(idx + 1);
55 class AgileTokenHandler : public sax_fastparser::FastTokenHandlerBase
57 public:
58 virtual sal_Int32 SAL_CALL getTokenFromUTF8(const Sequence< sal_Int8 >& /*nIdentifier*/) override
60 return FastToken::DONTKNOW;
63 virtual Sequence<sal_Int8> SAL_CALL getUTF8Identifier(sal_Int32 /*nToken*/) override
65 return Sequence<sal_Int8>();
68 virtual sal_Int32 getTokenDirect( const char * /* pToken */, sal_Int32 /* nLength */ ) const override
70 return -1;
74 class AgileDocumentHandler : public ::cppu::WeakImplHelper<XFastDocumentHandler>
76 AgileEncryptionInfo& mInfo;
78 public:
79 explicit AgileDocumentHandler(AgileEncryptionInfo& rInfo) :
80 mInfo(rInfo)
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 {}
89 void SAL_CALL startUnknownElement( const OUString& /*aNamespace*/, const OUString& rName, const Reference< XFastAttributeList >& aAttributeList ) override
91 std::u16string_view rLocalName = stripNamespacePrefix(rName);
93 const css::uno::Sequence<Attribute> aUnknownAttributes = aAttributeList->getUnknownAttributes();
94 for (const Attribute& rAttribute : aUnknownAttributes)
96 std::u16string_view rAttrLocalName = stripNamespacePrefix(rAttribute.Name);
98 if (rAttrLocalName == u"spinCount")
100 ::sax::Converter::convertNumber(mInfo.spinCount, rAttribute.Value);
102 else if (rAttrLocalName == u"saltSize")
104 ::sax::Converter::convertNumber(mInfo.saltSize, rAttribute.Value);
106 else if (rAttrLocalName == u"blockSize")
108 ::sax::Converter::convertNumber(mInfo.blockSize, rAttribute.Value);
110 else if (rAttrLocalName == u"keyBits")
112 ::sax::Converter::convertNumber(mInfo.keyBits, rAttribute.Value);
114 else if (rAttrLocalName == u"hashSize")
116 ::sax::Converter::convertNumber(mInfo.hashSize, rAttribute.Value);
118 else if (rAttrLocalName == u"cipherAlgorithm")
120 mInfo.cipherAlgorithm = rAttribute.Value;
122 else if (rAttrLocalName == u"cipherChaining")
124 mInfo.cipherChaining = rAttribute.Value;
126 else if (rAttrLocalName == u"hashAlgorithm")
128 mInfo.hashAlgorithm = rAttribute.Value;
130 else if (rAttrLocalName == u"saltValue")
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);
139 else if (rAttrLocalName == u"encryptedVerifierHashInput")
141 Sequence<sal_Int8> encryptedVerifierHashInput;
142 comphelper::Base64::decode(encryptedVerifierHashInput, rAttribute.Value);
143 mInfo.encryptedVerifierHashInput = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedVerifierHashInput);
145 else if (rAttrLocalName == u"encryptedVerifierHashValue")
147 Sequence<sal_Int8> encryptedVerifierHashValue;
148 comphelper::Base64::decode(encryptedVerifierHashValue, rAttribute.Value);
149 mInfo.encryptedVerifierHashValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedVerifierHashValue);
151 else if (rAttrLocalName == u"encryptedKeyValue")
153 Sequence<sal_Int8> encryptedKeyValue;
154 comphelper::Base64::decode(encryptedKeyValue, rAttribute.Value);
155 mInfo.encryptedKeyValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedKeyValue);
157 if (rAttrLocalName == u"encryptedHmacKey")
159 Sequence<sal_Int8> aValue;
160 comphelper::Base64::decode(aValue, rAttribute.Value);
161 mInfo.hmacEncryptedKey = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(aValue);
163 if (rAttrLocalName == u"encryptedHmacValue")
165 Sequence<sal_Int8> aValue;
166 comphelper::Base64::decode(aValue, rAttribute.Value);
167 mInfo.hmacEncryptedValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(aValue);
172 void SAL_CALL endFastElement( sal_Int32 /*aElement*/ ) override
174 void SAL_CALL endUnknownElement( const OUString& /*aNamespace*/, const OUString& /*aName*/ ) override
177 Reference< XFastContextHandler > SAL_CALL createFastChildContext( sal_Int32 /*aElement*/, const Reference< XFastAttributeList >& /*aAttribs*/ ) override
179 return nullptr;
182 Reference< XFastContextHandler > SAL_CALL createUnknownChildContext( const OUString& /*aNamespace*/, const OUString& /*aName*/, const Reference< XFastAttributeList >& /*aAttribs*/ ) override
184 return this;
187 void SAL_CALL characters( const OUString& /*aChars*/ ) override
191 constexpr const sal_uInt32 constSegmentLength = 4096;
193 const std::vector<sal_uInt8> constBlock1 { 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79 };
194 const std::vector<sal_uInt8> constBlock2 { 0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e };
195 const std::vector<sal_uInt8> constBlock3 { 0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6 };
196 const std::vector<sal_uInt8> constBlockHmac1 { 0x5f, 0xb2, 0xad, 0x01, 0x0c, 0xb9, 0xe1, 0xf6 };
197 const std::vector<sal_uInt8> constBlockHmac2 { 0xa0, 0x67, 0x7f, 0x02, 0xb2, 0x2c, 0x84, 0x33 };
199 bool hashCalc(std::vector<sal_uInt8>& output,
200 std::vector<sal_uInt8>& input,
201 std::u16string_view sAlgorithm )
203 if (sAlgorithm == u"SHA1")
205 std::vector<unsigned char> out = comphelper::Hash::calculateHash(input.data(), input.size(), comphelper::HashType::SHA1);
206 output = out;
207 return true;
209 else if (sAlgorithm == u"SHA384")
211 std::vector<unsigned char> out = comphelper::Hash::calculateHash(input.data(), input.size(), comphelper::HashType::SHA384);
212 output = out;
213 return true;
215 else if (sAlgorithm == u"SHA512")
217 std::vector<unsigned char> out = comphelper::Hash::calculateHash(input.data(), input.size(), comphelper::HashType::SHA512);
218 output = out;
219 return true;
221 return false;
224 CryptoHashType cryptoHashTypeFromString(std::u16string_view sAlgorithm)
226 if (sAlgorithm == u"SHA512")
227 return CryptoHashType::SHA512;
228 else if (sAlgorithm == u"SHA384")
229 return CryptoHashType::SHA384;
230 else
231 return CryptoHashType::SHA1;
234 } // namespace
236 AgileEngine::AgileEngine()
237 : meEncryptionPreset(AgileEncryptionPreset::AES_256_SHA512)
240 Crypto::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo)
242 if (rInfo.keyBits == 128 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC")
243 return Crypto::AES_128_CBC;
244 else if (rInfo.keyBits == 256 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC")
245 return Crypto::AES_256_CBC;
246 return Crypto::UNKNOWN;
249 static std::vector<sal_uInt8> calculateIV(comphelper::HashType eType,
250 std::vector<sal_uInt8> const & rSalt,
251 std::vector<sal_uInt8> const & rBlock,
252 sal_Int32 nCipherBlockSize)
254 comphelper::Hash aHasher(eType);
255 aHasher.update(rSalt.data(), rSalt.size());
256 aHasher.update(rBlock.data(), rBlock.size());
257 std::vector<sal_uInt8> aIV = aHasher.finalize();
258 aIV.resize(roundUp(sal_Int32(aIV.size()), nCipherBlockSize), 0x36);
259 return aIV;
262 void AgileEngine::calculateBlock(
263 std::vector<sal_uInt8> const & rBlock,
264 std::vector<sal_uInt8>& rHashFinal,
265 std::vector<sal_uInt8>& rInput,
266 std::vector<sal_uInt8>& rOutput)
268 std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
269 std::vector<sal_uInt8> dataFinal(mInfo.hashSize + rBlock.size(), 0);
270 std::copy(rHashFinal.begin(), rHashFinal.end(), dataFinal.begin());
271 std::copy(rBlock.begin(), rBlock.end(), dataFinal.begin() + mInfo.hashSize);
273 hashCalc(hash, dataFinal, mInfo.hashAlgorithm);
275 sal_Int32 keySize = mInfo.keyBits / 8;
276 std::vector<sal_uInt8> key(keySize, 0);
278 std::copy(hash.begin(), hash.begin() + keySize, key.begin());
280 Decrypt aDecryptor(key, mInfo.saltValue, cryptoType(mInfo));
281 aDecryptor.update(rOutput, rInput);
284 void AgileEngine::encryptBlock(
285 std::vector<sal_uInt8> const & rBlock,
286 std::vector<sal_uInt8> & rHashFinal,
287 std::vector<sal_uInt8> & rInput,
288 std::vector<sal_uInt8> & rOutput)
290 std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
291 std::vector<sal_uInt8> dataFinal(mInfo.hashSize + rBlock.size(), 0);
292 std::copy(rHashFinal.begin(), rHashFinal.end(), dataFinal.begin());
293 std::copy(rBlock.begin(), rBlock.end(), dataFinal.begin() + mInfo.hashSize);
295 hashCalc(hash, dataFinal, mInfo.hashAlgorithm);
297 sal_Int32 keySize = mInfo.keyBits / 8;
298 std::vector<sal_uInt8> key(keySize, 0);
300 std::copy(hash.begin(), hash.begin() + keySize, key.begin());
302 Encrypt aEncryptor(key, mInfo.saltValue, cryptoType(mInfo));
304 aEncryptor.update(rOutput, rInput);
307 void AgileEngine::calculateHashFinal(const OUString& rPassword, std::vector<sal_uInt8>& aHashFinal)
309 aHashFinal = comphelper::DocPasswordHelper::GetOoxHashAsVector(
310 rPassword, mInfo.saltValue, mInfo.spinCount,
311 comphelper::Hash::IterCount::PREPEND, mInfo.hashAlgorithm);
314 namespace
317 bool generateBytes(std::vector<sal_uInt8> & rBytes, sal_Int32 nSize)
319 size_t nMax = std::min(rBytes.size(), size_t(nSize));
321 for (size_t i = 0; i < nMax; ++i)
323 rBytes[i] = sal_uInt8(comphelper::rng::uniform_uint_distribution(0, 0xFF));
326 return true;
329 } // end anonymous namespace
331 bool AgileEngine::decryptAndCheckVerifierHash(OUString const & rPassword)
333 std::vector<sal_uInt8>& encryptedHashValue = mInfo.encryptedVerifierHashValue;
334 size_t encryptedHashValueSize = encryptedHashValue.size();
335 size_t nHashValueSize = mInfo.hashSize;
336 if (nHashValueSize > encryptedHashValueSize)
337 return false;
339 std::vector<sal_uInt8> hashFinal(nHashValueSize, 0);
340 calculateHashFinal(rPassword, hashFinal);
342 std::vector<sal_uInt8>& encryptedHashInput = mInfo.encryptedVerifierHashInput;
343 // SALT - needs to be a multiple of block size (?)
344 sal_uInt32 nSaltSize = roundUp(mInfo.saltSize, mInfo.blockSize);
345 if (nSaltSize < encryptedHashInput.size())
346 return false;
347 std::vector<sal_uInt8> hashInput(nSaltSize, 0);
348 calculateBlock(constBlock1, hashFinal, encryptedHashInput, hashInput);
350 std::vector<sal_uInt8> hashValue(encryptedHashValueSize, 0);
351 calculateBlock(constBlock2, hashFinal, encryptedHashValue, hashValue);
353 std::vector<sal_uInt8> hash(nHashValueSize, 0);
354 hashCalc(hash, hashInput, mInfo.hashAlgorithm);
356 return std::equal(hash.begin(), hash.end(), hashValue.begin());
359 void AgileEngine::decryptEncryptionKey(OUString const & rPassword)
361 sal_Int32 nKeySize = mInfo.keyBits / 8;
363 mKey.clear();
364 mKey.resize(nKeySize, 0);
366 std::vector<sal_uInt8> aPasswordHash(mInfo.hashSize, 0);
367 calculateHashFinal(rPassword, aPasswordHash);
369 calculateBlock(constBlock3, aPasswordHash, mInfo.encryptedKeyValue, mKey);
372 // TODO: Rename
373 bool AgileEngine::generateEncryptionKey(OUString const & rPassword)
375 bool bResult = decryptAndCheckVerifierHash(rPassword);
377 if (bResult)
379 decryptEncryptionKey(rPassword);
380 decryptHmacKey();
381 decryptHmacValue();
383 return bResult;
386 bool AgileEngine::decryptHmacKey()
388 // Initialize hmacKey
389 mInfo.hmacKey.clear();
390 mInfo.hmacKey.resize(mInfo.hmacEncryptedKey.size(), 0);
392 // Calculate IV
393 comphelper::HashType eType;
394 if (mInfo.hashAlgorithm == "SHA1")
395 eType = comphelper::HashType::SHA1;
396 else if (mInfo.hashAlgorithm == "SHA384")
397 eType = comphelper::HashType::SHA384;
398 else if (mInfo.hashAlgorithm == "SHA512")
399 eType = comphelper::HashType::SHA512;
400 else
401 return false;
403 std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac1, mInfo.blockSize);
405 // Decrypt without key, calculated iv
406 Decrypt aDecrypt(mKey, iv, cryptoType(mInfo));
407 aDecrypt.update(mInfo.hmacKey, mInfo.hmacEncryptedKey);
409 mInfo.hmacKey.resize(mInfo.hashSize, 0);
411 return true;
414 bool AgileEngine::decryptHmacValue()
416 // Initialize hmacHash
417 mInfo.hmacHash.clear();
418 mInfo.hmacHash.resize(mInfo.hmacEncryptedValue.size(), 0);
420 // Calculate IV
421 comphelper::HashType eType;
422 if (mInfo.hashAlgorithm == "SHA1")
423 eType = comphelper::HashType::SHA1;
424 else if (mInfo.hashAlgorithm == "SHA384")
425 eType = comphelper::HashType::SHA384;
426 else if (mInfo.hashAlgorithm == "SHA512")
427 eType = comphelper::HashType::SHA512;
428 else
429 return false;
430 std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac2, mInfo.blockSize);
432 // Decrypt without key, calculated iv
433 Decrypt aDecrypt(mKey, iv, cryptoType(mInfo));
434 aDecrypt.update(mInfo.hmacHash, mInfo.hmacEncryptedValue);
436 mInfo.hmacHash.resize(mInfo.hashSize, 0);
438 return true;
441 bool AgileEngine::checkDataIntegrity()
443 bool bResult = (mInfo.hmacHash.size() == mInfo.hmacCalculatedHash.size() &&
444 std::equal(mInfo.hmacHash.begin(), mInfo.hmacHash.end(), mInfo.hmacCalculatedHash.begin()));
446 return bResult;
449 bool AgileEngine::decrypt(BinaryXInputStream& aInputStream,
450 BinaryXOutputStream& aOutputStream)
452 CryptoHash aCryptoHash(mInfo.hmacKey, cryptoHashTypeFromString(mInfo.hashAlgorithm));
454 sal_uInt32 totalSize = aInputStream.readuInt32(); // Document unencrypted size - 4 bytes
455 // account for size in HMAC
456 std::vector<sal_uInt8> aSizeBytes(sizeof(sal_uInt32));
457 ByteOrderConverter::writeLittleEndian(aSizeBytes.data(), totalSize);
458 aCryptoHash.update(aSizeBytes);
460 aInputStream.skip(4); // Reserved 4 Bytes
461 // account for reserved 4 bytes (must be 0)
462 std::vector<sal_uInt8> aReserved{0,0,0,0};
463 aCryptoHash.update(aReserved);
465 // setup decryption
466 std::vector<sal_uInt8>& keyDataSalt = mInfo.keyDataSalt;
468 sal_uInt32 saltSize = mInfo.saltSize;
469 sal_uInt32 keySize = mInfo.keyBits / 8;
471 sal_uInt32 segment = 0;
473 std::vector<sal_uInt8> saltWithBlockKey(saltSize + sizeof(segment), 0);
474 std::copy(keyDataSalt.begin(), keyDataSalt.end(), saltWithBlockKey.begin());
476 std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
477 std::vector<sal_uInt8> iv(keySize, 0);
479 std::vector<sal_uInt8> inputBuffer(constSegmentLength);
480 std::vector<sal_uInt8> outputBuffer(constSegmentLength);
481 sal_uInt32 inputLength;
482 sal_uInt32 outputLength;
483 sal_uInt32 remaining = totalSize;
485 while ((inputLength = aInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0)
487 auto p = saltWithBlockKey.begin() + saltSize;
488 p[0] = segment & 0xFF;
489 p[1] = (segment >> 8) & 0xFF;
490 p[2] = (segment >> 16) & 0xFF;
491 p[3] = segment >> 24;
493 hashCalc(hash, saltWithBlockKey, mInfo.hashAlgorithm);
495 // Only if hash > keySize
496 std::copy(hash.begin(), hash.begin() + keySize, iv.begin());
498 Decrypt aDecryptor(mKey, iv, AgileEngine::cryptoType(mInfo));
499 outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength);
501 sal_uInt32 writeLength = std::min(outputLength, remaining);
503 aCryptoHash.update(inputBuffer, inputLength);
505 aOutputStream.writeMemory(outputBuffer.data(), writeLength);
507 remaining -= outputLength;
508 segment++;
511 mInfo.hmacCalculatedHash = aCryptoHash.finalize();
513 return true;
516 bool AgileEngine::readEncryptionInfo(uno::Reference<io::XInputStream> & rxInputStream)
518 // Check reserved value
519 std::vector<sal_uInt8> aExpectedReservedBytes(sizeof(sal_uInt32));
520 ByteOrderConverter::writeLittleEndian(aExpectedReservedBytes.data(), msfilter::AGILE_ENCRYPTION_RESERVED);
522 uno::Sequence<sal_Int8> aReadReservedBytes(sizeof(sal_uInt32));
523 rxInputStream->readBytes(aReadReservedBytes, aReadReservedBytes.getLength());
525 if (!std::equal(std::cbegin(aReadReservedBytes), std::cend(aReadReservedBytes), aExpectedReservedBytes.begin()))
526 return false;
528 mInfo.spinCount = 0;
529 mInfo.saltSize = 0;
530 mInfo.keyBits = 0;
531 mInfo.hashSize = 0;
532 mInfo.blockSize = 0;
534 Reference<XFastDocumentHandler> xFastDocumentHandler(new AgileDocumentHandler(mInfo));
535 Reference<XFastTokenHandler> xFastTokenHandler(new AgileTokenHandler);
537 Reference<XFastParser> xParser(css::xml::sax::FastParser::create(comphelper::getProcessComponentContext()));
539 xParser->setFastDocumentHandler(xFastDocumentHandler);
540 xParser->setTokenHandler(xFastTokenHandler);
542 InputSource aInputSource;
543 aInputSource.aInputStream = rxInputStream;
544 xParser->parseStream(aInputSource);
546 // CHECK info data
547 if (2 > mInfo.blockSize || mInfo.blockSize > 4096)
548 return false;
550 if (0 > mInfo.spinCount || mInfo.spinCount > 10000000)
551 return false;
553 if (1 > mInfo.saltSize|| mInfo.saltSize > 65536) // Check
554 return false;
556 // AES 128 CBC with SHA1
557 if (mInfo.keyBits == 128 &&
558 mInfo.cipherAlgorithm == "AES" &&
559 mInfo.cipherChaining == "ChainingModeCBC" &&
560 mInfo.hashAlgorithm == "SHA1" &&
561 mInfo.hashSize == comphelper::SHA1_HASH_LENGTH)
563 return true;
566 // AES 128 CBC with SHA384
567 if (mInfo.keyBits == 128 &&
568 mInfo.cipherAlgorithm == "AES" &&
569 mInfo.cipherChaining == "ChainingModeCBC" &&
570 mInfo.hashAlgorithm == "SHA384" &&
571 mInfo.hashSize == comphelper::SHA384_HASH_LENGTH)
573 return true;
576 // AES 256 CBC with SHA512
577 if (mInfo.keyBits == 256 &&
578 mInfo.cipherAlgorithm == "AES" &&
579 mInfo.cipherChaining == "ChainingModeCBC" &&
580 mInfo.hashAlgorithm == "SHA512" &&
581 mInfo.hashSize == comphelper::SHA512_HASH_LENGTH)
583 return true;
586 return false;
589 bool AgileEngine::generateAndEncryptVerifierHash(OUString const & rPassword)
591 if (!generateBytes(mInfo.saltValue, mInfo.saltSize))
592 return false;
594 std::vector<sal_uInt8> unencryptedVerifierHashInput(mInfo.saltSize);
595 if (!generateBytes(unencryptedVerifierHashInput, mInfo.saltSize))
596 return false;
598 // HASH - needs to be modified to be multiple of block size
599 sal_Int32 nVerifierHash = roundUp(mInfo.hashSize, mInfo.blockSize);
600 std::vector<sal_uInt8> unencryptedVerifierHashValue;
601 if (!hashCalc(unencryptedVerifierHashValue, unencryptedVerifierHashInput, mInfo.hashAlgorithm))
602 return false;
603 unencryptedVerifierHashValue.resize(nVerifierHash, 0);
605 std::vector<sal_uInt8> hashFinal(mInfo.hashSize, 0);
606 calculateHashFinal(rPassword, hashFinal);
608 encryptBlock(constBlock1, hashFinal, unencryptedVerifierHashInput, mInfo.encryptedVerifierHashInput);
610 encryptBlock(constBlock2, hashFinal, unencryptedVerifierHashValue, mInfo.encryptedVerifierHashValue);
612 return true;
615 bool AgileEngine::encryptHmacKey()
617 // Initialize hmacKey
618 mInfo.hmacKey.clear();
619 mInfo.hmacKey.resize(mInfo.hashSize, 0);
621 if (!generateBytes(mInfo.hmacKey, mInfo.hashSize))
622 return false;
624 // Encrypted salt must be multiple of block size
625 sal_Int32 nEncryptedSaltSize = oox::crypto::roundUp(mInfo.hashSize, mInfo.blockSize);
627 // We need to extend hmacSalt to multiple of block size, padding with 0x36
628 std::vector<sal_uInt8> extendedSalt(mInfo.hmacKey);
629 extendedSalt.resize(nEncryptedSaltSize, 0x36);
631 // Initialize hmacEncryptedKey
632 mInfo.hmacEncryptedKey.clear();
633 mInfo.hmacEncryptedKey.resize(nEncryptedSaltSize, 0);
635 // Calculate IV
636 comphelper::HashType eType;
637 if (mInfo.hashAlgorithm == "SHA1")
638 eType = comphelper::HashType::SHA1;
639 else if (mInfo.hashAlgorithm == "SHA384")
640 eType = comphelper::HashType::SHA384;
641 else if (mInfo.hashAlgorithm == "SHA512")
642 eType = comphelper::HashType::SHA512;
643 else
644 return false;
646 std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac1, mInfo.blockSize);
648 // Encrypt without key, calculated iv
649 Encrypt aEncryptor(mKey, iv, cryptoType(mInfo));
650 aEncryptor.update(mInfo.hmacEncryptedKey, extendedSalt);
652 return true;
655 bool AgileEngine::encryptHmacValue()
657 sal_Int32 nEncryptedValueSize = roundUp(mInfo.hashSize, mInfo.blockSize);
658 mInfo.hmacEncryptedValue.clear();
659 mInfo.hmacEncryptedValue.resize(nEncryptedValueSize, 0);
661 std::vector<sal_uInt8> extendedHash(mInfo.hmacHash);
662 extendedHash.resize(nEncryptedValueSize, 0x36);
664 // Calculate IV
665 comphelper::HashType eType;
666 if (mInfo.hashAlgorithm == "SHA1")
667 eType = comphelper::HashType::SHA1;
668 else if (mInfo.hashAlgorithm == "SHA384")
669 eType = comphelper::HashType::SHA384;
670 else if (mInfo.hashAlgorithm == "SHA512")
671 eType = comphelper::HashType::SHA512;
672 else
673 return false;
675 std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac2, mInfo.blockSize);
677 // Encrypt without key, calculated iv
678 Encrypt aEncryptor(mKey, iv, cryptoType(mInfo));
679 aEncryptor.update(mInfo.hmacEncryptedValue, extendedHash);
681 return true;
684 bool AgileEngine::encryptEncryptionKey(OUString const & rPassword)
686 sal_Int32 nKeySize = mInfo.keyBits / 8;
688 mKey.clear();
689 mKey.resize(nKeySize, 0);
691 mInfo.encryptedKeyValue.clear();
692 mInfo.encryptedKeyValue.resize(nKeySize, 0);
694 if (!generateBytes(mKey, nKeySize))
695 return false;
697 std::vector<sal_uInt8> aPasswordHash(mInfo.hashSize, 0);
698 calculateHashFinal(rPassword, aPasswordHash);
700 encryptBlock(constBlock3, aPasswordHash, mKey, mInfo.encryptedKeyValue);
702 return true;
705 bool AgileEngine::setupEncryption(OUString const & rPassword)
707 if (meEncryptionPreset == AgileEncryptionPreset::AES_128_SHA1)
708 setupEncryptionParameters({ 100000, 16, 128, 20, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA1") });
709 else if (meEncryptionPreset == AgileEncryptionPreset::AES_128_SHA384)
710 setupEncryptionParameters({ 100000, 16, 128, 48, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA384") });
711 else
712 setupEncryptionParameters({ 100000, 16, 256, 64, 16, OUString("AES"), OUString("ChainingModeCBC"), OUString("SHA512") });
714 return setupEncryptionKey(rPassword);
717 void AgileEngine::setupEncryptionParameters(AgileEncryptionParameters const & rAgileEncryptionParameters)
719 mInfo.spinCount = rAgileEncryptionParameters.spinCount;
720 mInfo.saltSize = rAgileEncryptionParameters.saltSize;
721 mInfo.keyBits = rAgileEncryptionParameters.keyBits;
722 mInfo.hashSize = rAgileEncryptionParameters.hashSize;
723 mInfo.blockSize = rAgileEncryptionParameters.blockSize;
725 mInfo.cipherAlgorithm = rAgileEncryptionParameters.cipherAlgorithm;
726 mInfo.cipherChaining = rAgileEncryptionParameters.cipherChaining;
727 mInfo.hashAlgorithm = rAgileEncryptionParameters.hashAlgorithm;
729 mInfo.keyDataSalt.resize(mInfo.saltSize);
730 mInfo.saltValue.resize(mInfo.saltSize);
731 mInfo.encryptedVerifierHashInput.resize(mInfo.saltSize);
732 mInfo.encryptedVerifierHashValue.resize(roundUp(mInfo.hashSize, mInfo.blockSize), 0);
735 bool AgileEngine::setupEncryptionKey(OUString const & rPassword)
737 if (!generateAndEncryptVerifierHash(rPassword))
738 return false;
739 if (!encryptEncryptionKey(rPassword))
740 return false;
741 if (!generateBytes(mInfo.keyDataSalt, mInfo.saltSize))
742 return false;
743 if (!encryptHmacKey())
744 return false;
745 return true;
748 void AgileEngine::writeEncryptionInfo(BinaryXOutputStream & rStream)
750 rStream.WriteUInt32(msfilter::VERSION_INFO_AGILE);
751 rStream.WriteUInt32(msfilter::AGILE_ENCRYPTION_RESERVED);
753 SvMemoryStream aMemStream;
754 tools::XmlWriter aXmlWriter(&aMemStream);
756 if (aXmlWriter.startDocument(0/*nIndent*/))
758 aXmlWriter.startElement("", "encryption", "http://schemas.microsoft.com/office/2006/encryption");
759 aXmlWriter.attribute("xmlns:p", OString("http://schemas.microsoft.com/office/2006/keyEncryptor/password"));
761 aXmlWriter.startElement("keyData");
762 aXmlWriter.attribute("saltSize", mInfo.saltSize);
763 aXmlWriter.attribute("blockSize", mInfo.blockSize);
764 aXmlWriter.attribute("keyBits", mInfo.keyBits);
765 aXmlWriter.attribute("hashSize", mInfo.hashSize);
766 aXmlWriter.attribute("cipherAlgorithm", mInfo.cipherAlgorithm);
767 aXmlWriter.attribute("cipherChaining", mInfo.cipherChaining);
768 aXmlWriter.attribute("hashAlgorithm", mInfo.hashAlgorithm);
769 aXmlWriter.attributeBase64("saltValue", mInfo.keyDataSalt);
770 aXmlWriter.endElement();
772 aXmlWriter.startElement("dataIntegrity");
773 aXmlWriter.attributeBase64("encryptedHmacKey", mInfo.hmacEncryptedKey);
774 aXmlWriter.attributeBase64("encryptedHmacValue", mInfo.hmacEncryptedValue);
775 aXmlWriter.endElement();
777 aXmlWriter.startElement("keyEncryptors");
778 aXmlWriter.startElement("keyEncryptor");
779 aXmlWriter.attribute("uri", OString("http://schemas.microsoft.com/office/2006/keyEncryptor/password"));
781 aXmlWriter.startElement("p", "encryptedKey", "");
782 aXmlWriter.attribute("spinCount", mInfo.spinCount);
783 aXmlWriter.attribute("saltSize", mInfo.saltSize);
784 aXmlWriter.attribute("blockSize", mInfo.blockSize);
785 aXmlWriter.attribute("keyBits", mInfo.keyBits);
786 aXmlWriter.attribute("hashSize", mInfo.hashSize);
787 aXmlWriter.attribute("cipherAlgorithm", mInfo.cipherAlgorithm);
788 aXmlWriter.attribute("cipherChaining", mInfo.cipherChaining);
789 aXmlWriter.attribute("hashAlgorithm", mInfo.hashAlgorithm);
790 aXmlWriter.attributeBase64("saltValue", mInfo.saltValue);
791 aXmlWriter.attributeBase64("encryptedVerifierHashInput", mInfo.encryptedVerifierHashInput);
792 aXmlWriter.attributeBase64("encryptedVerifierHashValue", mInfo.encryptedVerifierHashValue);
793 aXmlWriter.attributeBase64("encryptedKeyValue", mInfo.encryptedKeyValue);
794 aXmlWriter.endElement();
796 aXmlWriter.endElement();
797 aXmlWriter.endElement();
799 aXmlWriter.endElement();
800 aXmlWriter.endDocument();
802 rStream.writeMemory(aMemStream.GetData(), aMemStream.GetSize());
805 void AgileEngine::encrypt(const css::uno::Reference<css::io::XInputStream> & rxInputStream,
806 css::uno::Reference<css::io::XOutputStream> & rxOutputStream,
807 sal_uInt32 nSize)
809 CryptoHash aCryptoHash(mInfo.hmacKey, cryptoHashTypeFromString(mInfo.hashAlgorithm));
811 BinaryXOutputStream aBinaryOutputStream(rxOutputStream, false);
812 BinaryXInputStream aBinaryInputStream(rxInputStream, false);
814 std::vector<sal_uInt8> aSizeBytes(sizeof(sal_uInt32));
815 ByteOrderConverter::writeLittleEndian(aSizeBytes.data(), nSize);
816 aBinaryOutputStream.writeMemory(aSizeBytes.data(), aSizeBytes.size()); // size
817 aCryptoHash.update(aSizeBytes, aSizeBytes.size());
819 std::vector<sal_uInt8> aNull{0,0,0,0};
820 aBinaryOutputStream.writeMemory(aNull.data(), aNull.size()); // reserved
821 aCryptoHash.update(aNull, aNull.size());
823 std::vector<sal_uInt8>& keyDataSalt = mInfo.keyDataSalt;
825 sal_uInt32 saltSize = mInfo.saltSize;
826 sal_uInt32 keySize = mInfo.keyBits / 8;
828 sal_uInt32 nSegment = 0;
829 sal_uInt32 nSegmentByteSize = sizeof(nSegment);
831 std::vector<sal_uInt8> saltWithBlockKey(saltSize + nSegmentByteSize, 0);
832 std::copy(keyDataSalt.begin(), keyDataSalt.end(), saltWithBlockKey.begin());
834 std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
835 std::vector<sal_uInt8> iv(keySize, 0);
837 std::vector<sal_uInt8> inputBuffer(constSegmentLength);
838 std::vector<sal_uInt8> outputBuffer(constSegmentLength);
839 sal_uInt32 inputLength;
840 sal_uInt32 outputLength;
842 while ((inputLength = aBinaryInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0)
844 sal_uInt32 correctedInputLength = inputLength % mInfo.blockSize == 0 ?
845 inputLength : oox::crypto::roundUp(inputLength, sal_uInt32(mInfo.blockSize));
847 // Update Key
848 auto p = saltWithBlockKey.begin() + saltSize;
849 p[0] = nSegment & 0xFF;
850 p[1] = (nSegment >> 8) & 0xFF;
851 p[2] = (nSegment >> 16) & 0xFF;
852 p[3] = nSegment >> 24;
854 hashCalc(hash, saltWithBlockKey, mInfo.hashAlgorithm);
856 // Only if hash > keySize
857 std::copy(hash.begin(), hash.begin() + keySize, iv.begin());
859 Encrypt aEncryptor(mKey, iv, AgileEngine::cryptoType(mInfo));
860 outputLength = aEncryptor.update(outputBuffer, inputBuffer, correctedInputLength);
861 aBinaryOutputStream.writeMemory(outputBuffer.data(), outputLength);
862 aCryptoHash.update(outputBuffer, outputLength);
864 nSegment++;
866 mInfo.hmacHash = aCryptoHash.finalize();
867 encryptHmacValue();
870 } // namespace oox::crypto
872 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */