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>
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>
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
{
47 std::u16string_view
stripNamespacePrefix(std::u16string_view rsInputName
)
49 size_t idx
= rsInputName
.find(':');
50 if (idx
== std::u16string_view::npos
)
52 return rsInputName
.substr(idx
+ 1);
55 class AgileTokenHandler
: public sax_fastparser::FastTokenHandlerBase
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
74 class AgileDocumentHandler
: public ::cppu::WeakImplHelper
<XFastDocumentHandler
>
76 AgileEncryptionInfo
& mInfo
;
79 explicit AgileDocumentHandler(AgileEncryptionInfo
& 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
182 Reference
< XFastContextHandler
> SAL_CALL
createUnknownChildContext( const OUString
& /*aNamespace*/, const OUString
& /*aName*/, const Reference
< XFastAttributeList
>& /*aAttribs*/ ) override
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
);
209 else if (sAlgorithm
== u
"SHA384")
211 std::vector
<unsigned char> out
= comphelper::Hash::calculateHash(input
.data(), input
.size(), comphelper::HashType::SHA384
);
215 else if (sAlgorithm
== u
"SHA512")
217 std::vector
<unsigned char> out
= comphelper::Hash::calculateHash(input
.data(), input
.size(), comphelper::HashType::SHA512
);
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
;
231 return CryptoHashType::SHA1
;
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);
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
);
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));
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
)
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())
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;
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
);
373 bool AgileEngine::generateEncryptionKey(OUString
const & rPassword
)
375 bool bResult
= decryptAndCheckVerifierHash(rPassword
);
379 decryptEncryptionKey(rPassword
);
386 bool AgileEngine::decryptHmacKey()
388 // Initialize hmacKey
389 mInfo
.hmacKey
.clear();
390 mInfo
.hmacKey
.resize(mInfo
.hmacEncryptedKey
.size(), 0);
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
;
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);
414 bool AgileEngine::decryptHmacValue()
416 // Initialize hmacHash
417 mInfo
.hmacHash
.clear();
418 mInfo
.hmacHash
.resize(mInfo
.hmacEncryptedValue
.size(), 0);
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
;
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);
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()));
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
);
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
;
511 mInfo
.hmacCalculatedHash
= aCryptoHash
.finalize();
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()))
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
);
547 if (2 > mInfo
.blockSize
|| mInfo
.blockSize
> 4096)
550 if (0 > mInfo
.spinCount
|| mInfo
.spinCount
> 10000000)
553 if (1 > mInfo
.saltSize
|| mInfo
.saltSize
> 65536) // Check
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
)
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
)
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
)
589 bool AgileEngine::generateAndEncryptVerifierHash(OUString
const & rPassword
)
591 if (!generateBytes(mInfo
.saltValue
, mInfo
.saltSize
))
594 std::vector
<sal_uInt8
> unencryptedVerifierHashInput(mInfo
.saltSize
);
595 if (!generateBytes(unencryptedVerifierHashInput
, mInfo
.saltSize
))
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
))
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
);
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
))
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);
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
;
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
);
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);
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
;
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
);
684 bool AgileEngine::encryptEncryptionKey(OUString
const & rPassword
)
686 sal_Int32 nKeySize
= mInfo
.keyBits
/ 8;
689 mKey
.resize(nKeySize
, 0);
691 mInfo
.encryptedKeyValue
.clear();
692 mInfo
.encryptedKeyValue
.resize(nKeySize
, 0);
694 if (!generateBytes(mKey
, nKeySize
))
697 std::vector
<sal_uInt8
> aPasswordHash(mInfo
.hashSize
, 0);
698 calculateHashFinal(rPassword
, aPasswordHash
);
700 encryptBlock(constBlock3
, aPasswordHash
, mKey
, mInfo
.encryptedKeyValue
);
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") });
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
))
739 if (!encryptEncryptionKey(rPassword
))
741 if (!generateBytes(mInfo
.keyDataSalt
, mInfo
.saltSize
))
743 if (!encryptHmacKey())
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
,
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
));
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
);
866 mInfo
.hmacHash
= aCryptoHash
.finalize();
870 } // namespace oox::crypto
872 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */