LibreOffice Module oox (master)  1
CryptTools.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 #include <com/sun/star/uno/RuntimeException.hpp>
13 
14 #include <config_oox.h>
15 
16 #if USE_TLS_OPENSSL
17 #include <openssl/evp.h>
18 #include <openssl/sha.h>
19 #include <openssl/hmac.h>
20 #endif // USE_TLS_OPENSSL
21 
22 #if USE_TLS_NSS
23 #include <nss.h>
24 #include <pk11pub.h>
25 #endif // USE_TLS_NSS
26 
27 namespace oox::crypto {
28 
29 #if USE_TLS_OPENSSL
30 
31 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
32 
33 static HMAC_CTX *HMAC_CTX_new(void)
34 {
35  HMAC_CTX *pContext = new HMAC_CTX;
36  HMAC_CTX_init(pContext);
37  return pContext;
38 }
39 
40 static void HMAC_CTX_free(HMAC_CTX *pContext)
41 {
42  HMAC_CTX_cleanup(pContext);
43  delete pContext;
44 }
45 #endif
46 
47 namespace
48 {
49  struct cipher_delete
50  {
51  void operator()(EVP_CIPHER_CTX* p) { EVP_CIPHER_CTX_free(p); }
52  };
53 
54  struct hmac_delete
55  {
56  void operator()(HMAC_CTX* p) { HMAC_CTX_free(p); }
57  };
58 }
59 
60 struct CryptoImpl
61 {
62  std::unique_ptr<EVP_CIPHER_CTX, cipher_delete> mpContext;
63  std::unique_ptr<HMAC_CTX, hmac_delete> mpHmacContext;
64 
65  CryptoImpl() = default;
66 
67  void setupEncryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType eType)
68  {
69  mpContext.reset(EVP_CIPHER_CTX_new());
70  EVP_CIPHER_CTX_init(mpContext.get());
71 
72  const EVP_CIPHER* cipher = getCipher(eType);
73  if (cipher == nullptr)
74  return;
75 
76  if (iv.empty())
77  EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), nullptr);
78  else
79  EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), iv.data());
80  EVP_CIPHER_CTX_set_padding(mpContext.get(), 0);
81  }
82 
83  void setupDecryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType eType)
84  {
85  mpContext.reset(EVP_CIPHER_CTX_new());
86  EVP_CIPHER_CTX_init(mpContext.get());
87 
88  const EVP_CIPHER* pCipher = getCipher(eType);
89  if (pCipher == nullptr)
90  return;
91 
92  const size_t nMinKeySize = EVP_CIPHER_key_length(pCipher);
93  if (key.size() < nMinKeySize)
94  key.resize(nMinKeySize, 0);
95 
96  if (iv.empty())
97  EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), nullptr);
98  else
99  {
100  const size_t nMinIVSize = EVP_CIPHER_iv_length(pCipher);
101  if (iv.size() < nMinIVSize)
102  iv.resize(nMinIVSize, 0);
103 
104  EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), iv.data());
105  }
106  EVP_CIPHER_CTX_set_padding(mpContext.get(), 0);
107  }
108 
109  void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
110  {
111  mpHmacContext.reset(HMAC_CTX_new());
112  const EVP_MD* aEvpMd = nullptr;
113  switch (eType)
114  {
116  aEvpMd = EVP_sha1(); break;
118  aEvpMd = EVP_sha256(); break;
120  aEvpMd = EVP_sha512(); break;
121  }
122  HMAC_Init_ex(mpHmacContext.get(), rKey.data(), rKey.size(), aEvpMd, nullptr);
123  }
124 
125  ~CryptoImpl()
126  {
127  if (mpContext)
128  EVP_CIPHER_CTX_cleanup(mpContext.get());
129  }
130 
131  static const EVP_CIPHER* getCipher(Crypto::CryptoType type)
132  {
133  switch(type)
134  {
135  case Crypto::CryptoType::AES_128_ECB:
136  return EVP_aes_128_ecb();
137  case Crypto::CryptoType::AES_128_CBC:
138  return EVP_aes_128_cbc();
139  case Crypto::CryptoType::AES_256_CBC:
140  return EVP_aes_256_cbc();
141  default:
142  break;
143  }
144  return nullptr;
145  }
146 };
147 
148 #elif USE_TLS_NSS
149 
150 #define MAX_WRAPPED_KEY_LEN 128
151 
152 struct CryptoImpl
153 {
154  PK11SlotInfo* mSlot;
155  PK11Context* mContext;
156  SECItem* mSecParam;
157  PK11SymKey* mSymKey;
158  PK11Context* mWrapKeyContext;
159  PK11SymKey* mWrapKey;
160 
161  CryptoImpl()
162  : mSlot(nullptr)
163  , mContext(nullptr)
164  , mSecParam(nullptr)
165  , mSymKey(nullptr)
166  , mWrapKeyContext(nullptr)
167  , mWrapKey(nullptr)
168  {
169  // Initialize NSS, database functions are not needed
170  NSS_NoDB_Init(nullptr);
171  }
172 
173  ~CryptoImpl()
174  {
175  if (mContext)
176  PK11_DestroyContext(mContext, PR_TRUE);
177  if (mSecParam)
178  SECITEM_FreeItem(mSecParam, PR_TRUE);
179  if (mSymKey)
180  PK11_FreeSymKey(mSymKey);
181  if (mWrapKeyContext)
182  PK11_DestroyContext(mWrapKeyContext, PR_TRUE);
183  if (mWrapKey)
184  PK11_FreeSymKey(mWrapKey);
185  if (mSlot)
186  PK11_FreeSlot(mSlot);
187  }
188 
189  PK11SymKey* ImportSymKey(CK_MECHANISM_TYPE mechanism, CK_ATTRIBUTE_TYPE operation, SECItem* key)
190  {
191  mSymKey = PK11_ImportSymKey(mSlot, mechanism, PK11_OriginUnwrap, operation, key, nullptr);
192  if (!mSymKey) //rhbz#1614419 maybe failed due to FIPS, use rhbz#1461450 style workaround
193  {
194  /*
195  * Without FIPS it would be possible to just use
196  * mSymKey = PK11_ImportSymKey( mSlot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr );
197  * with FIPS NSS Level 2 certification has to be "workarounded" (so it becomes Level 1) by using
198  * following method:
199  * 1. Generate wrap key
200  * 2. Encrypt authkey with wrap key
201  * 3. Unwrap encrypted authkey using wrap key
202  */
203 
204  /*
205  * Generate wrapping key
206  */
207  CK_MECHANISM_TYPE wrap_mechanism = PK11_GetBestWrapMechanism(mSlot);
208  int wrap_key_len = PK11_GetBestKeyLength(mSlot, wrap_mechanism);
209  mWrapKey = PK11_KeyGen(mSlot, wrap_mechanism, nullptr, wrap_key_len, nullptr);
210  if (!mWrapKey)
211  throw css::uno::RuntimeException("PK11_KeyGen SymKey failure", css::uno::Reference<css::uno::XInterface>());
212 
213  /*
214  * Encrypt authkey with wrapping key
215  */
216 
217  /*
218  * Initialization of IV is not needed because PK11_GetBestWrapMechanism should return ECB mode
219  */
220  SECItem tmp_sec_item = {};
221  mWrapKeyContext = PK11_CreateContextBySymKey(wrap_mechanism, CKA_ENCRYPT, mWrapKey, &tmp_sec_item);
222  if (!mWrapKeyContext)
223  throw css::uno::RuntimeException("PK11_CreateContextBySymKey failure", css::uno::Reference<css::uno::XInterface>());
224 
225  unsigned char wrapped_key_data[MAX_WRAPPED_KEY_LEN];
226  int wrapped_key_len = sizeof(wrapped_key_data);
227 
228  if (PK11_CipherOp(mWrapKeyContext, wrapped_key_data, &wrapped_key_len,
229  sizeof(wrapped_key_data), key->data, key->len) != SECSuccess)
230  {
231  throw css::uno::RuntimeException("PK11_CipherOp failure", css::uno::Reference<css::uno::XInterface>());
232  }
233 
234  if (PK11_Finalize(mWrapKeyContext) != SECSuccess)
235  throw css::uno::RuntimeException("PK11_Finalize failure", css::uno::Reference<css::uno::XInterface>());
236 
237  /*
238  * Finally unwrap sym key
239  */
240  SECItem wrapped_key = {};
241  wrapped_key.data = wrapped_key_data;
242  wrapped_key.len = wrapped_key_len;
243 
244  mSymKey = PK11_UnwrapSymKey(mWrapKey, wrap_mechanism, &tmp_sec_item, &wrapped_key,
245  mechanism, operation, key->len);
246  }
247  return mSymKey;
248  }
249 
250  void setupCryptoContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType type, CK_ATTRIBUTE_TYPE operation)
251  {
252  CK_MECHANISM_TYPE mechanism = static_cast<CK_ULONG>(-1);
253 
254  SECItem ivItem;
255  ivItem.type = siBuffer;
256  if(iv.empty())
257  ivItem.data = nullptr;
258  else
259  ivItem.data = iv.data();
260  ivItem.len = iv.size();
261 
262  SECItem* pIvItem = nullptr;
263 
264  switch(type)
265  {
266  case Crypto::CryptoType::AES_128_ECB:
267  mechanism = CKM_AES_ECB;
268  break;
269  case Crypto::CryptoType::AES_128_CBC:
270  mechanism = CKM_AES_CBC;
271  pIvItem = &ivItem;
272  break;
273  case Crypto::CryptoType::AES_256_CBC:
274  mechanism = CKM_AES_CBC;
275  pIvItem = &ivItem;
276  break;
277  default:
278  break;
279  }
280 
281  mSlot = PK11_GetBestSlot(mechanism, nullptr);
282 
283  if (!mSlot)
284  throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference<css::uno::XInterface>());
285 
286  SECItem keyItem;
287  keyItem.type = siBuffer;
288  keyItem.data = key.data();
289  keyItem.len = key.size();
290 
291  mSymKey = ImportSymKey(mechanism, CKA_ENCRYPT, &keyItem);
292  if (!mSymKey)
293  throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference<css::uno::XInterface>());
294 
295  mSecParam = PK11_ParamFromIV(mechanism, pIvItem);
296  mContext = PK11_CreateContextBySymKey(mechanism, operation, mSymKey, mSecParam);
297  }
298 
299  void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
300  {
301  CK_MECHANISM_TYPE aMechanism = static_cast<CK_ULONG>(-1);
302 
303  switch(eType)
304  {
306  aMechanism = CKM_SHA_1_HMAC;
307  break;
309  aMechanism = CKM_SHA256_HMAC;
310  break;
312  aMechanism = CKM_SHA512_HMAC;
313  break;
314  }
315 
316  mSlot = PK11_GetBestSlot(aMechanism, nullptr);
317 
318  if (!mSlot)
319  throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference<css::uno::XInterface>());
320 
321  SECItem aKeyItem;
322  aKeyItem.data = rKey.data();
323  aKeyItem.len = rKey.size();
324 
325  mSymKey = ImportSymKey(aMechanism, CKA_SIGN, &aKeyItem);
326  if (!mSymKey)
327  throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference<css::uno::XInterface>());
328 
329  SECItem param;
330  param.data = nullptr;
331  param.len = 0;
332  mContext = PK11_CreateContextBySymKey(aMechanism, CKA_SIGN, mSymKey, &param);
333  }
334 };
335 #else
337 {};
338 #endif
339 
341  : mpImpl(std::make_unique<CryptoImpl>())
342 {
343 }
344 
346 {
347 }
348 
349 // DECRYPT
350 
351 Decrypt::Decrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type)
352  : Crypto()
353 {
354 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
355  (void)key;
356  (void)iv;
357  (void)type;
358 #endif
359 
360 #if USE_TLS_OPENSSL
361  mpImpl->setupDecryptContext(key, iv, type);
362 #endif
363 
364 #if USE_TLS_NSS
365  mpImpl->setupCryptoContext(key, iv, type, CKA_DECRYPT);
366 #endif // USE_TLS_NSS
367 }
368 
369 sal_uInt32 Decrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, sal_uInt32 inputLength)
370 {
371  int outputLength = 0;
372 
373 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
374  sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength;
375 #else
376  (void)output;
377  (void)input;
378  (void)inputLength;
379 #endif
380 
381 #if USE_TLS_OPENSSL
382  (void)EVP_DecryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), actualInputLength);
383 #endif // USE_TLS_OPENSSL
384 
385 #if USE_TLS_NSS
386  if (!mpImpl->mContext)
387  return 0;
388  (void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength);
389 #endif // USE_TLS_NSS
390 
391  return static_cast<sal_uInt32>(outputLength);
392 }
393 
394 sal_uInt32 Decrypt::aes128ecb(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, std::vector<sal_uInt8>& key)
395 {
396  sal_uInt32 outputLength = 0;
397  std::vector<sal_uInt8> iv;
399  outputLength = crypto.update(output, input);
400  return outputLength;
401 }
402 
403 // ENCRYPT
404 
405 Encrypt::Encrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type)
406  : Crypto()
407 {
408 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
409  (void)key;
410  (void)iv;
411  (void)type;
412 #endif
413 
414 #if USE_TLS_OPENSSL
415  mpImpl->setupEncryptContext(key, iv, type);
416 #elif USE_TLS_NSS
417  mpImpl->setupCryptoContext(key, iv, type, CKA_ENCRYPT);
418 #endif // USE_TLS_NSS
419 }
420 
421 sal_uInt32 Encrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, sal_uInt32 inputLength)
422 {
423  int outputLength = 0;
424 
425 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
426  sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength;
427 #else
428  (void)output;
429  (void)input;
430  (void)inputLength;
431 #endif
432 
433 #if USE_TLS_OPENSSL
434  (void)EVP_EncryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), actualInputLength);
435 #endif // USE_TLS_OPENSSL
436 
437 #if USE_TLS_NSS
438  (void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength);
439 #endif // USE_TLS_NSS
440 
441  return static_cast<sal_uInt32>(outputLength);
442 }
443 
444 // CryptoHash - HMAC
445 
446 namespace
447 {
448 
449 sal_Int32 getSizeForHashType(CryptoHashType eType)
450 {
451  switch (eType)
452  {
453  case CryptoHashType::SHA1: return 20;
454  case CryptoHashType::SHA256: return 32;
455  case CryptoHashType::SHA512: return 64;
456  }
457  return 0;
458 }
459 
460 } // end anonymous namespace
461 
462 CryptoHash::CryptoHash(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
463  : Crypto()
464  , mnHashSize(getSizeForHashType(eType))
465 {
466 #if USE_TLS_OPENSSL
467  mpImpl->setupCryptoHashContext(rKey, eType);
468 #elif USE_TLS_NSS
469  mpImpl->setupCryptoHashContext(rKey, eType);
470  PK11_DigestBegin(mpImpl->mContext);
471 #else
472  (void)rKey;
473 #endif
474 }
475 
476 bool CryptoHash::update(std::vector<sal_uInt8>& rInput, sal_uInt32 nInputLength)
477 {
478 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
479  sal_uInt32 nActualInputLength = (nInputLength == 0 || nInputLength > rInput.size()) ? rInput.size() : nInputLength;
480 #else
481  (void)rInput;
482  (void)nInputLength;
483 #endif
484 
485 #if USE_TLS_OPENSSL
486  return HMAC_Update(mpImpl->mpHmacContext.get(), rInput.data(), nActualInputLength) != 0;
487 #elif USE_TLS_NSS
488  return PK11_DigestOp(mpImpl->mContext, rInput.data(), nActualInputLength) == SECSuccess;
489 #else
490  return false; // ???
491 #endif
492 }
493 
494 std::vector<sal_uInt8> CryptoHash::finalize()
495 {
496  std::vector<sal_uInt8> aHash(mnHashSize, 0);
497  unsigned int nSizeWritten;
498 #if USE_TLS_OPENSSL
499  (void) HMAC_Final(mpImpl->mpHmacContext.get(), aHash.data(), &nSizeWritten);
500 #elif USE_TLS_NSS
501  PK11_DigestFinal(mpImpl->mContext, aHash.data(), &nSizeWritten, aHash.size());
502 #endif
503  (void)nSizeWritten;
504 
505  return aHash;
506 }
507 
508 } // namespace oox::crypto
509 
510 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt32 update(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, sal_uInt32 inputLength=0)
Definition: CryptTools.cxx:421
CryptoHash(std::vector< sal_uInt8 > &rKey, CryptoHashType eType)
Definition: CryptTools.cxx:462
sal_uInt32 update(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, sal_uInt32 inputLength=0)
Definition: CryptTools.cxx:369
ParserContextSharedPtr mpContext
std::unique_ptr< CryptoImpl > mpImpl
Definition: CryptTools.hxx:68
bool update(std::vector< sal_uInt8 > &rInput, sal_uInt32 nInputLength=0)
Definition: CryptTools.cxx:476
exports com.sun.star.xml. crypto
Encrypt(std::vector< sal_uInt8 > &key, std::vector< sal_uInt8 > &iv, CryptoType type)
Definition: CryptTools.cxx:405
Decrypt(std::vector< sal_uInt8 > &key, std::vector< sal_uInt8 > &iv, CryptoType type)
Definition: CryptTools.cxx:351
std::vector< sal_uInt8 > finalize()
Definition: CryptTools.cxx:494
static sal_uInt32 aes128ecb(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, std::vector< sal_uInt8 > &key)
Definition: CryptTools.cxx:394
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo