1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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>
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
;
50 OUString
stripNamespacePrefix(OUString
const & rsInputName
)
52 return rsInputName
.copy(rsInputName
.indexOf(":") + 1);
55 class AgileTokenHandler
: public cppu::WeakImplHelper
<XFastTokenHandler
>
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
;
74 explicit AgileDocumentHandler(AgileEncryptionInfo
& 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
177 Reference
< XFastContextHandler
> SAL_CALL
createUnknownChildContext( const OUString
& /*aNamespace*/, const OUString
& /*aName*/, const Reference
< XFastAttributeList
>& /*aAttribs*/ ) override
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
);
204 else if (sAlgorithm
== "SHA512")
206 std::vector
<unsigned char> out
= comphelper::Hash::calculateHash(input
.data(), input
.size(), comphelper::HashType::SHA512
);
213 CryptoHashType
cryptoHashTypeFromString(OUString
const & sAlgorithm
)
215 if (sAlgorithm
== "SHA512")
216 return CryptoHashType::SHA512
;
217 return CryptoHashType::SHA1
;
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);
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
);
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));
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;
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
);
350 bool AgileEngine::generateEncryptionKey(OUString
const & rPassword
)
352 bool bResult
= decryptAndCheckVerifierHash(rPassword
);
356 decryptEncryptionKey(rPassword
);
363 bool AgileEngine::decryptHmacKey()
365 // Initialize hmacKey
366 mInfo
.hmacKey
.clear();
367 mInfo
.hmacKey
.resize(mInfo
.hmacEncryptedKey
.size(), 0);
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
;
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);
389 bool AgileEngine::decryptHmacValue()
391 // Initialize hmacHash
392 mInfo
.hmacHash
.clear();
393 mInfo
.hmacHash
.resize(mInfo
.hmacEncryptedValue
.size(), 0);
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
;
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);
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()));
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
);
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
;
482 mInfo
.hmacCalculatedHash
= aCryptoHash
.finalize();
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()))
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
);
518 if (2 > mInfo
.blockSize
|| mInfo
.blockSize
> 4096)
521 if (0 > mInfo
.spinCount
|| mInfo
.spinCount
> 10000000)
524 if (1 > mInfo
.saltSize
|| mInfo
.saltSize
> 65536) // Check
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
)
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
)
550 bool AgileEngine::generateAndEncryptVerifierHash(OUString
const & rPassword
)
552 if (!generateBytes(mInfo
.saltValue
, mInfo
.saltSize
))
555 std::vector
<sal_uInt8
> unencryptedVerifierHashInput(mInfo
.saltSize
);
556 if (!generateBytes(unencryptedVerifierHashInput
, mInfo
.saltSize
))
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
))
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
);
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
))
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);
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
;
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
);
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);
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
;
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
);
641 bool AgileEngine::encryptEncryptionKey(OUString
const & rPassword
)
643 sal_Int32 nKeySize
= mInfo
.keyBits
/ 8;
646 mKey
.resize(nKeySize
, 0);
648 mInfo
.encryptedKeyValue
.clear();
649 mInfo
.encryptedKeyValue
.resize(nKeySize
, 0);
651 if (!generateBytes(mKey
, nKeySize
))
654 std::vector
<sal_uInt8
> aPasswordHash(mInfo
.hashSize
, 0);
655 calculateHashFinal(rPassword
, aPasswordHash
);
657 encryptBlock(constBlock3
, aPasswordHash
, mKey
, mInfo
.encryptedKeyValue
);
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") });
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
))
694 if (!encryptEncryptionKey(rPassword
))
696 if (!generateBytes(mInfo
.keyDataSalt
, mInfo
.saltSize
))
698 if (!encryptHmacKey())
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
,
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
));
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
);
819 mInfo
.hmacHash
= aCryptoHash
.finalize();
826 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */