Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / oox / source / crypto / AgileEngine.cxx
blob7c2a0e9c93d2b861cf9b17fd70b12bb94920b7c2
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>
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>
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;
45 namespace oox {
46 namespace core {
48 namespace {
50 OUString stripNamespacePrefix(OUString const & rsInputName)
52 return rsInputName.copy(rsInputName.indexOf(":") + 1);
55 class AgileTokenHandler : public cppu::WeakImplHelper<XFastTokenHandler>
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>();
69 class AgileDocumentHandler : public ::cppu::WeakImplHelper<XFastDocumentHandler>
71 AgileEncryptionInfo& mInfo;
73 public:
74 explicit AgileDocumentHandler(AgileEncryptionInfo& rInfo) :
75 mInfo(rInfo)
78 void SAL_CALL startDocument() override {}
79 void SAL_CALL endDocument() override {}
80 void SAL_CALL processingInstruction( const OUString& /*rTarget*/, const OUString& /*rData*/ ) override {}
81 void SAL_CALL setDocumentLocator( const Reference< XLocator >& /*xLocator*/ ) override {}
82 void SAL_CALL startFastElement( sal_Int32 /*Element*/, const Reference< XFastAttributeList >& /*Attribs*/ ) override {}
84 void SAL_CALL startUnknownElement( const OUString& /*aNamespace*/, const OUString& rName, const Reference< XFastAttributeList >& aAttributeList ) override
86 const OUString& rLocalName = stripNamespacePrefix(rName);
88 const css::uno::Sequence<Attribute> aUnknownAttributes = aAttributeList->getUnknownAttributes();
89 for (const Attribute& rAttribute : aUnknownAttributes)
91 const OUString& rAttrLocalName = stripNamespacePrefix(rAttribute.Name);
93 if (rAttrLocalName == "spinCount")
95 ::sax::Converter::convertNumber(mInfo.spinCount, rAttribute.Value);
97 else if (rAttrLocalName == "saltSize")
99 ::sax::Converter::convertNumber(mInfo.saltSize, rAttribute.Value);
101 else if (rAttrLocalName == "blockSize")
103 ::sax::Converter::convertNumber(mInfo.blockSize, rAttribute.Value);
105 else if (rAttrLocalName == "keyBits")
107 ::sax::Converter::convertNumber(mInfo.keyBits, rAttribute.Value);
109 else if (rAttrLocalName == "hashSize")
111 ::sax::Converter::convertNumber(mInfo.hashSize, rAttribute.Value);
113 else if (rAttrLocalName == "cipherAlgorithm")
115 mInfo.cipherAlgorithm = rAttribute.Value;
117 else if (rAttrLocalName == "cipherChaining")
119 mInfo.cipherChaining = rAttribute.Value;
121 else if (rAttrLocalName == "hashAlgorithm")
123 mInfo.hashAlgorithm = rAttribute.Value;
125 else if (rAttrLocalName == "saltValue")
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);
134 else if (rAttrLocalName == "encryptedVerifierHashInput")
136 Sequence<sal_Int8> encryptedVerifierHashInput;
137 comphelper::Base64::decode(encryptedVerifierHashInput, rAttribute.Value);
138 mInfo.encryptedVerifierHashInput = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedVerifierHashInput);
140 else if (rAttrLocalName == "encryptedVerifierHashValue")
142 Sequence<sal_Int8> encryptedVerifierHashValue;
143 comphelper::Base64::decode(encryptedVerifierHashValue, rAttribute.Value);
144 mInfo.encryptedVerifierHashValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedVerifierHashValue);
146 else if (rAttrLocalName == "encryptedKeyValue")
148 Sequence<sal_Int8> encryptedKeyValue;
149 comphelper::Base64::decode(encryptedKeyValue, rAttribute.Value);
150 mInfo.encryptedKeyValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(encryptedKeyValue);
152 if (rAttrLocalName == "encryptedHmacKey")
154 Sequence<sal_Int8> aValue;
155 comphelper::Base64::decode(aValue, rAttribute.Value);
156 mInfo.hmacEncryptedKey = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(aValue);
158 if (rAttrLocalName == "encryptedHmacValue")
160 Sequence<sal_Int8> aValue;
161 comphelper::Base64::decode(aValue, rAttribute.Value);
162 mInfo.hmacEncryptedValue = comphelper::sequenceToContainer<std::vector<sal_uInt8>>(aValue);
167 void SAL_CALL endFastElement( sal_Int32 /*aElement*/ ) override
169 void SAL_CALL endUnknownElement( const OUString& /*aNamespace*/, const OUString& /*aName*/ ) override
172 Reference< XFastContextHandler > SAL_CALL createFastChildContext( sal_Int32 /*aElement*/, const Reference< XFastAttributeList >& /*aAttribs*/ ) override
174 return nullptr;
177 Reference< XFastContextHandler > SAL_CALL createUnknownChildContext( const OUString& /*aNamespace*/, const OUString& /*aName*/, const Reference< XFastAttributeList >& /*aAttribs*/ ) override
179 return this;
182 void SAL_CALL characters( const OUString& /*aChars*/ ) override
186 constexpr const sal_uInt32 constSegmentLength = 4096;
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 };
194 bool hashCalc(std::vector<sal_uInt8>& output,
195 std::vector<sal_uInt8>& input,
196 const OUString& sAlgorithm )
198 if (sAlgorithm == "SHA1")
200 std::vector<unsigned char> out = comphelper::Hash::calculateHash(input.data(), input.size(), comphelper::HashType::SHA1);
201 output = out;
202 return true;
204 else if (sAlgorithm == "SHA512")
206 std::vector<unsigned char> out = comphelper::Hash::calculateHash(input.data(), input.size(), comphelper::HashType::SHA512);
207 output = out;
208 return true;
210 return false;
213 CryptoHashType cryptoHashTypeFromString(OUString const & sAlgorithm)
215 if (sAlgorithm == "SHA512")
216 return CryptoHashType::SHA512;
217 return CryptoHashType::SHA1;
220 } // namespace
222 AgileEngine::AgileEngine()
223 : meEncryptionPreset(AgileEncryptionPreset::AES_256_SHA512)
226 Crypto::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo)
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;
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)
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;
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)
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);
259 hashCalc(hash, dataFinal, mInfo.hashAlgorithm);
261 sal_Int32 keySize = mInfo.keyBits / 8;
262 std::vector<sal_uInt8> key(keySize, 0);
264 std::copy(hash.begin(), hash.begin() + keySize, key.begin());
266 Decrypt aDecryptor(key, mInfo.saltValue, cryptoType(mInfo));
267 aDecryptor.update(rOutput, rInput);
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)
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);
281 hashCalc(hash, dataFinal, mInfo.hashAlgorithm);
283 sal_Int32 keySize = mInfo.keyBits / 8;
284 std::vector<sal_uInt8> key(keySize, 0);
286 std::copy(hash.begin(), hash.begin() + keySize, key.begin());
288 Encrypt aEncryptor(key, mInfo.saltValue, cryptoType(mInfo));
290 aEncryptor.update(rOutput, rInput);
293 void AgileEngine::calculateHashFinal(const OUString& rPassword, std::vector<sal_uInt8>& aHashFinal)
295 aHashFinal = comphelper::DocPasswordHelper::GetOoxHashAsVector(
296 rPassword, mInfo.saltValue, mInfo.spinCount,
297 comphelper::Hash::IterCount::PREPEND, mInfo.hashAlgorithm);
300 namespace
303 bool generateBytes(std::vector<sal_uInt8> & rBytes, sal_Int32 nSize)
305 size_t nMax = std::min(rBytes.size(), size_t(nSize));
307 for (size_t i = 0; i < nMax; ++i)
309 rBytes[i] = sal_uInt8(comphelper::rng::uniform_uint_distribution(0, 0xFF));
312 return true;
315 } // end anonymous namespace
317 bool AgileEngine::decryptAndCheckVerifierHash(OUString const & rPassword)
319 std::vector<sal_uInt8> hashFinal(mInfo.hashSize, 0);
320 calculateHashFinal(rPassword, hashFinal);
322 std::vector<sal_uInt8>& encryptedHashInput = mInfo.encryptedVerifierHashInput;
323 std::vector<sal_uInt8> hashInput(mInfo.saltSize, 0);
324 calculateBlock(constBlock1, hashFinal, encryptedHashInput, hashInput);
326 std::vector<sal_uInt8>& encryptedHashValue = mInfo.encryptedVerifierHashValue;
327 std::vector<sal_uInt8> hashValue(encryptedHashValue.size(), 0);
328 calculateBlock(constBlock2, hashFinal, encryptedHashValue, hashValue);
330 std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
331 hashCalc(hash, hashInput, mInfo.hashAlgorithm);
333 return (hash.size() <= hashValue.size() && std::equal(hash.begin(), hash.end(), hashValue.begin()));
336 void AgileEngine::decryptEncryptionKey(OUString const & rPassword)
338 sal_Int32 nKeySize = mInfo.keyBits / 8;
340 mKey.clear();
341 mKey.resize(nKeySize, 0);
343 std::vector<sal_uInt8> aPasswordHash(mInfo.hashSize, 0);
344 calculateHashFinal(rPassword, aPasswordHash);
346 calculateBlock(constBlock3, aPasswordHash, mInfo.encryptedKeyValue, mKey);
349 // TODO: Rename
350 bool AgileEngine::generateEncryptionKey(OUString const & rPassword)
352 bool bResult = decryptAndCheckVerifierHash(rPassword);
354 if (bResult)
356 decryptEncryptionKey(rPassword);
357 decryptHmacKey();
358 decryptHmacValue();
360 return bResult;
363 bool AgileEngine::decryptHmacKey()
365 // Initialize hmacKey
366 mInfo.hmacKey.clear();
367 mInfo.hmacKey.resize(mInfo.hmacEncryptedKey.size(), 0);
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;
378 std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac1, mInfo.blockSize);
380 // Decrypt without key, calculated iv
381 Decrypt aDecrypt(mKey, iv, cryptoType(mInfo));
382 aDecrypt.update(mInfo.hmacKey, mInfo.hmacEncryptedKey);
384 mInfo.hmacKey.resize(mInfo.hashSize, 0);
386 return true;
389 bool AgileEngine::decryptHmacValue()
391 // Initialize hmacHash
392 mInfo.hmacHash.clear();
393 mInfo.hmacHash.resize(mInfo.hmacEncryptedValue.size(), 0);
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);
405 // Decrypt without key, calculated iv
406 Decrypt aDecrypt(mKey, iv, cryptoType(mInfo));
407 aDecrypt.update(mInfo.hmacHash, mInfo.hmacEncryptedValue);
409 mInfo.hmacHash.resize(mInfo.hashSize, 0);
411 return true;
414 bool AgileEngine::checkDataIntegrity()
416 bool bResult = (mInfo.hmacHash.size() == mInfo.hmacCalculatedHash.size() &&
417 std::equal(mInfo.hmacHash.begin(), mInfo.hmacHash.end(), mInfo.hmacCalculatedHash.begin()));
419 return bResult;
422 bool AgileEngine::decrypt(BinaryXInputStream& aInputStream,
423 BinaryXOutputStream& aOutputStream)
425 CryptoHash aCryptoHash(mInfo.hmacKey, cryptoHashTypeFromString(mInfo.hashAlgorithm));
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);
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);
438 // setup decryption
439 std::vector<sal_uInt8>& keyDataSalt = mInfo.keyDataSalt;
441 sal_uInt32 saltSize = mInfo.saltSize;
442 sal_uInt32 keySize = mInfo.keyBits / 8;
444 sal_uInt32 segment = 0;
446 std::vector<sal_uInt8> saltWithBlockKey(saltSize + sizeof(segment), 0);
447 std::copy(keyDataSalt.begin(), keyDataSalt.end(), saltWithBlockKey.begin());
449 std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
450 std::vector<sal_uInt8> iv(keySize, 0);
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;
458 while ((inputLength = aInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0)
460 sal_uInt8* segmentBegin = reinterpret_cast<sal_uInt8*>(&segment);
461 sal_uInt8* segmentEnd = segmentBegin + sizeof(segment);
462 std::copy(segmentBegin, segmentEnd, saltWithBlockKey.begin() + saltSize);
464 hashCalc(hash, saltWithBlockKey, mInfo.hashAlgorithm);
466 // Only if hash > keySize
467 std::copy(hash.begin(), hash.begin() + keySize, iv.begin());
469 Decrypt aDecryptor(mKey, iv, AgileEngine::cryptoType(mInfo));
470 outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength);
472 sal_uInt32 writeLength = std::min(outputLength, remaining);
474 aCryptoHash.update(inputBuffer, inputLength);
476 aOutputStream.writeMemory(outputBuffer.data(), writeLength);
478 remaining -= outputLength;
479 segment++;
482 mInfo.hmacCalculatedHash = aCryptoHash.finalize();
484 return true;
487 bool AgileEngine::readEncryptionInfo(uno::Reference<io::XInputStream> & rxInputStream)
489 // Check reserved value
490 std::vector<sal_uInt8> aExpectedReservedBytes(sizeof(sal_uInt32));
491 ByteOrderConverter::writeLittleEndian(aExpectedReservedBytes.data(), msfilter::AGILE_ENCRYPTION_RESERVED);
493 uno::Sequence<sal_Int8> aReadReservedBytes(sizeof(sal_uInt32));
494 rxInputStream->readBytes(aReadReservedBytes, aReadReservedBytes.getLength());
496 if (!std::equal(aReadReservedBytes.begin(), aReadReservedBytes.end(), aExpectedReservedBytes.begin()))
497 return false;
499 mInfo.spinCount = 0;
500 mInfo.saltSize = 0;
501 mInfo.keyBits = 0;
502 mInfo.hashSize = 0;
503 mInfo.blockSize = 0;
505 Reference<XFastDocumentHandler> xFastDocumentHandler(new AgileDocumentHandler(mInfo));
506 Reference<XFastTokenHandler> xFastTokenHandler(new AgileTokenHandler);
508 Reference<XFastParser> xParser(css::xml::sax::FastParser::create(comphelper::getProcessComponentContext()));
510 xParser->setFastDocumentHandler(xFastDocumentHandler);
511 xParser->setTokenHandler(xFastTokenHandler);
513 InputSource aInputSource;
514 aInputSource.aInputStream = rxInputStream;
515 xParser->parseStream(aInputSource);
517 // CHECK info data
518 if (2 > mInfo.blockSize || mInfo.blockSize > 4096)
519 return false;
521 if (0 > mInfo.spinCount || mInfo.spinCount > 10000000)
522 return false;
524 if (1 > mInfo.saltSize|| mInfo.saltSize > 65536) // Check
525 return false;
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)
534 return true;
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)
544 return true;
547 return false;
550 bool AgileEngine::generateAndEncryptVerifierHash(OUString const & rPassword)
552 if (!generateBytes(mInfo.saltValue, mInfo.saltSize))
553 return false;
555 std::vector<sal_uInt8> unencryptedVerifierHashInput(mInfo.saltSize);
556 if (!generateBytes(unencryptedVerifierHashInput, mInfo.saltSize))
557 return false;
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);
566 std::vector<sal_uInt8> hashFinal(mInfo.hashSize, 0);
567 calculateHashFinal(rPassword, hashFinal);
569 encryptBlock(constBlock1, hashFinal, unencryptedVerifierHashInput, mInfo.encryptedVerifierHashInput);
571 encryptBlock(constBlock2, hashFinal, unencryptedVerifierHashValue, mInfo.encryptedVerifierHashValue);
573 return true;
576 bool AgileEngine::encryptHmacKey()
578 // Initialize hmacKey
579 mInfo.hmacKey.clear();
580 mInfo.hmacKey.resize(mInfo.hashSize, 0);
582 if (!generateBytes(mInfo.hmacKey, mInfo.hashSize))
583 return false;
585 // Encrypted salt must be multiple of block size
586 sal_Int32 nEncryptedSaltSize = oox::core::roundUp(mInfo.hashSize, mInfo.blockSize);
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);
592 // Initialize hmacEncryptedKey
593 mInfo.hmacEncryptedKey.clear();
594 mInfo.hmacEncryptedKey.resize(nEncryptedSaltSize, 0);
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;
605 std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac1, mInfo.blockSize);
607 // Encrypt without key, calculated iv
608 Encrypt aEncryptor(mKey, iv, cryptoType(mInfo));
609 aEncryptor.update(mInfo.hmacEncryptedKey, extendedSalt);
611 return true;
614 bool AgileEngine::encryptHmacValue()
616 sal_Int32 nEncryptedValueSize = roundUp(mInfo.hashSize, mInfo.blockSize);
617 mInfo.hmacEncryptedValue.clear();
618 mInfo.hmacEncryptedValue.resize(nEncryptedValueSize, 0);
620 std::vector<sal_uInt8> extendedHash(mInfo.hmacHash);
621 extendedHash.resize(nEncryptedValueSize, 0x36);
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;
632 std::vector<sal_uInt8> iv = calculateIV(eType, mInfo.keyDataSalt, constBlockHmac2, mInfo.blockSize);
634 // Encrypt without key, calculated iv
635 Encrypt aEncryptor(mKey, iv, cryptoType(mInfo));
636 aEncryptor.update(mInfo.hmacEncryptedValue, extendedHash);
638 return true;
641 bool AgileEngine::encryptEncryptionKey(OUString const & rPassword)
643 sal_Int32 nKeySize = mInfo.keyBits / 8;
645 mKey.clear();
646 mKey.resize(nKeySize, 0);
648 mInfo.encryptedKeyValue.clear();
649 mInfo.encryptedKeyValue.resize(nKeySize, 0);
651 if (!generateBytes(mKey, nKeySize))
652 return false;
654 std::vector<sal_uInt8> aPasswordHash(mInfo.hashSize, 0);
655 calculateHashFinal(rPassword, aPasswordHash);
657 encryptBlock(constBlock3, aPasswordHash, mKey, mInfo.encryptedKeyValue);
659 return true;
662 bool AgileEngine::setupEncryption(OUString const & rPassword)
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") });
669 return setupEncryptionKey(rPassword);
672 void AgileEngine::setupEncryptionParameters(AgileEncryptionParameters const & rAgileEncryptionParameters)
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;
680 mInfo.cipherAlgorithm = rAgileEncryptionParameters.cipherAlgorithm;
681 mInfo.cipherChaining = rAgileEncryptionParameters.cipherChaining;
682 mInfo.hashAlgorithm = rAgileEncryptionParameters.hashAlgorithm;
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);
690 bool AgileEngine::setupEncryptionKey(OUString const & rPassword)
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;
703 void AgileEngine::writeEncryptionInfo(BinaryXOutputStream & rStream)
705 rStream.WriteUInt32(msfilter::VERSION_INFO_AGILE);
706 rStream.WriteUInt32(msfilter::AGILE_ENCRYPTION_RESERVED);
708 SvMemoryStream aMemStream;
709 tools::XmlWriter aXmlWriter(&aMemStream);
711 if (aXmlWriter.startDocument(0/*nIndent*/))
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"));
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();
727 aXmlWriter.startElement("dataIntegrity");
728 aXmlWriter.attributeBase64("encryptedHmacKey", mInfo.hmacEncryptedKey);
729 aXmlWriter.attributeBase64("encryptedHmacValue", mInfo.hmacEncryptedValue);
730 aXmlWriter.endElement();
732 aXmlWriter.startElement("keyEncryptors");
733 aXmlWriter.startElement("keyEncryptor");
734 aXmlWriter.attribute("uri", OString("http://schemas.microsoft.com/office/2006/keyEncryptor/password"));
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();
751 aXmlWriter.endElement();
752 aXmlWriter.endElement();
754 aXmlWriter.endElement();
755 aXmlWriter.endDocument();
757 rStream.writeMemory(aMemStream.GetData(), aMemStream.GetSize());
760 void AgileEngine::encrypt(css::uno::Reference<css::io::XInputStream> & rxInputStream,
761 css::uno::Reference<css::io::XOutputStream> & rxOutputStream,
762 sal_uInt32 nSize)
764 CryptoHash aCryptoHash(mInfo.hmacKey, cryptoHashTypeFromString(mInfo.hashAlgorithm));
766 BinaryXOutputStream aBinaryOutputStream(rxOutputStream, false);
767 BinaryXInputStream aBinaryInputStream(rxInputStream, false);
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());
774 std::vector<sal_uInt8> aNull{0,0,0,0};
775 aBinaryOutputStream.writeMemory(aNull.data(), aNull.size()); // reserved
776 aCryptoHash.update(aNull, aNull.size());
778 std::vector<sal_uInt8>& keyDataSalt = mInfo.keyDataSalt;
780 sal_uInt32 saltSize = mInfo.saltSize;
781 sal_uInt32 keySize = mInfo.keyBits / 8;
783 sal_uInt32 nSegment = 0;
784 sal_uInt32 nSegmentByteSize = sizeof(nSegment);
786 std::vector<sal_uInt8> saltWithBlockKey(saltSize + nSegmentByteSize, 0);
787 std::copy(keyDataSalt.begin(), keyDataSalt.end(), saltWithBlockKey.begin());
789 std::vector<sal_uInt8> hash(mInfo.hashSize, 0);
790 std::vector<sal_uInt8> iv(keySize, 0);
792 std::vector<sal_uInt8> inputBuffer(constSegmentLength);
793 std::vector<sal_uInt8> outputBuffer(constSegmentLength);
794 sal_uInt32 inputLength;
795 sal_uInt32 outputLength;
797 while ((inputLength = aBinaryInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0)
799 sal_uInt32 correctedInputLength = inputLength % mInfo.blockSize == 0 ?
800 inputLength : oox::core::roundUp(inputLength, sal_uInt32(mInfo.blockSize));
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);
807 hashCalc(hash, saltWithBlockKey, mInfo.hashAlgorithm);
809 // Only if hash > keySize
810 std::copy(hash.begin(), hash.begin() + keySize, iv.begin());
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);
817 nSegment++;
819 mInfo.hmacHash = aCryptoHash.finalize();
820 encryptHmacValue();
823 } // namespace core
824 } // namespace oox
826 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */