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