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