Bump version to 24.04.3.4
[LibreOffice.git] / oox / source / crypto / CryptTools.cxx
blob17729828ac428c28e11f7e0c3913113dd02a1ade
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
9 */
11 #include <oox/crypto/CryptTools.hxx>
12 #include <com/sun/star/uno/RuntimeException.hpp>
13 #include <sal/types.h>
15 #include <config_oox.h>
17 #if USE_TLS_OPENSSL
18 #include <openssl/evp.h>
19 #include <openssl/sha.h>
20 #include <openssl/hmac.h>
21 #endif // USE_TLS_OPENSSL
23 #if USE_TLS_NSS
24 #include <nss.h>
25 #include <nspr.h>
26 #include <pk11pub.h>
27 #endif // USE_TLS_NSS
29 namespace oox::crypto {
31 #if USE_TLS_OPENSSL
33 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
35 static HMAC_CTX *HMAC_CTX_new(void)
37 HMAC_CTX *pContext = new HMAC_CTX;
38 HMAC_CTX_init(pContext);
39 return pContext;
42 static void HMAC_CTX_free(HMAC_CTX *pContext)
44 HMAC_CTX_cleanup(pContext);
45 delete pContext;
47 #endif
49 namespace
51 struct cipher_delete
53 void operator()(EVP_CIPHER_CTX* p) { EVP_CIPHER_CTX_free(p); }
56 struct hmac_delete
58 SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_CTX_free' is deprecated
59 void operator()(HMAC_CTX* p) { HMAC_CTX_free(p); }
60 SAL_WNODEPRECATED_DECLARATIONS_POP
64 struct CryptoImpl
66 std::unique_ptr<EVP_CIPHER_CTX, cipher_delete> mpContext;
67 std::unique_ptr<HMAC_CTX, hmac_delete> mpHmacContext;
69 CryptoImpl() = default;
71 void setupEncryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType eType)
73 mpContext.reset(EVP_CIPHER_CTX_new());
74 EVP_CIPHER_CTX_init(mpContext.get());
76 const EVP_CIPHER* cipher = getCipher(eType);
77 if (cipher == nullptr)
78 return;
80 if (iv.empty())
81 EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), nullptr);
82 else
83 EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), iv.data());
84 EVP_CIPHER_CTX_set_padding(mpContext.get(), 0);
87 void setupDecryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType eType)
89 mpContext.reset(EVP_CIPHER_CTX_new());
90 EVP_CIPHER_CTX_init(mpContext.get());
92 const EVP_CIPHER* pCipher = getCipher(eType);
93 if (pCipher == nullptr)
94 return;
96 const size_t nMinKeySize = EVP_CIPHER_key_length(pCipher);
97 if (key.size() < nMinKeySize)
98 key.resize(nMinKeySize, 0);
100 if (iv.empty())
101 EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), nullptr);
102 else
104 const size_t nMinIVSize = EVP_CIPHER_iv_length(pCipher);
105 if (iv.size() < nMinIVSize)
106 iv.resize(nMinIVSize, 0);
108 EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), iv.data());
110 EVP_CIPHER_CTX_set_padding(mpContext.get(), 0);
113 void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
115 SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_CTX_new' is deprecated
116 mpHmacContext.reset(HMAC_CTX_new());
117 SAL_WNODEPRECATED_DECLARATIONS_POP
118 const EVP_MD* aEvpMd = nullptr;
119 switch (eType)
121 case CryptoHashType::SHA1:
122 aEvpMd = EVP_sha1(); break;
123 case CryptoHashType::SHA256:
124 aEvpMd = EVP_sha256(); break;
125 case CryptoHashType::SHA384:
126 aEvpMd = EVP_sha384(); break;
127 case CryptoHashType::SHA512:
128 aEvpMd = EVP_sha512(); break;
130 SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_Init_ex' is deprecated
131 HMAC_Init_ex(mpHmacContext.get(), rKey.data(), rKey.size(), aEvpMd, nullptr);
132 SAL_WNODEPRECATED_DECLARATIONS_POP
135 ~CryptoImpl()
137 if (mpContext)
138 EVP_CIPHER_CTX_cleanup(mpContext.get());
141 static const EVP_CIPHER* getCipher(Crypto::CryptoType type)
143 switch(type)
145 case Crypto::CryptoType::AES_128_ECB:
146 return EVP_aes_128_ecb();
147 case Crypto::CryptoType::AES_128_CBC:
148 return EVP_aes_128_cbc();
149 case Crypto::CryptoType::AES_256_CBC:
150 return EVP_aes_256_cbc();
151 default:
152 break;
154 return nullptr;
158 #elif USE_TLS_NSS
160 #define MAX_WRAPPED_KEY_LEN 128
162 struct CryptoImpl
164 PK11SlotInfo* mSlot;
165 PK11Context* mContext;
166 SECItem* mSecParam;
167 PK11SymKey* mSymKey;
168 PK11Context* mWrapKeyContext;
169 PK11SymKey* mWrapKey;
171 CryptoImpl()
172 : mSlot(nullptr)
173 , mContext(nullptr)
174 , mSecParam(nullptr)
175 , mSymKey(nullptr)
176 , mWrapKeyContext(nullptr)
177 , mWrapKey(nullptr)
179 // Initialize NSS, database functions are not needed
180 if (!NSS_IsInitialized())
182 auto const e = NSS_NoDB_Init(nullptr);
183 if (e != SECSuccess)
185 PRErrorCode error = PR_GetError();
186 const char* errorText = PR_ErrorToName(error);
187 throw css::uno::RuntimeException("NSS_NoDB_Init failed with " + OUString(errorText, strlen(errorText), RTL_TEXTENCODING_UTF8) + " (" + OUString::number(static_cast<int>(error)) + ")");
192 ~CryptoImpl()
194 if (mContext)
195 PK11_DestroyContext(mContext, PR_TRUE);
196 if (mSecParam)
197 SECITEM_FreeItem(mSecParam, PR_TRUE);
198 if (mSymKey)
199 PK11_FreeSymKey(mSymKey);
200 if (mWrapKeyContext)
201 PK11_DestroyContext(mWrapKeyContext, PR_TRUE);
202 if (mWrapKey)
203 PK11_FreeSymKey(mWrapKey);
204 if (mSlot)
205 PK11_FreeSlot(mSlot);
208 PK11SymKey* ImportSymKey(CK_MECHANISM_TYPE mechanism, CK_ATTRIBUTE_TYPE operation, SECItem* key)
210 mSymKey = PK11_ImportSymKey(mSlot, mechanism, PK11_OriginUnwrap, operation, key, nullptr);
211 if (!mSymKey) //rhbz#1614419 maybe failed due to FIPS, use rhbz#1461450 style workaround
214 * Without FIPS it would be possible to just use
215 * mSymKey = PK11_ImportSymKey( mSlot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr );
216 * with FIPS NSS Level 2 certification has to be "workarounded" (so it becomes Level 1) by using
217 * following method:
218 * 1. Generate wrap key
219 * 2. Encrypt authkey with wrap key
220 * 3. Unwrap encrypted authkey using wrap key
224 * Generate wrapping key
226 CK_MECHANISM_TYPE wrap_mechanism = PK11_GetBestWrapMechanism(mSlot);
227 int wrap_key_len = PK11_GetBestKeyLength(mSlot, wrap_mechanism);
228 mWrapKey = PK11_KeyGen(mSlot, wrap_mechanism, nullptr, wrap_key_len, nullptr);
229 if (!mWrapKey)
230 throw css::uno::RuntimeException("PK11_KeyGen SymKey failure", css::uno::Reference<css::uno::XInterface>());
233 * Encrypt authkey with wrapping key
237 * Initialization of IV is not needed because PK11_GetBestWrapMechanism should return ECB mode
239 SECItem tmp_sec_item = {};
240 mWrapKeyContext = PK11_CreateContextBySymKey(wrap_mechanism, CKA_ENCRYPT, mWrapKey, &tmp_sec_item);
241 if (!mWrapKeyContext)
242 throw css::uno::RuntimeException("PK11_CreateContextBySymKey failure", css::uno::Reference<css::uno::XInterface>());
244 unsigned char wrapped_key_data[MAX_WRAPPED_KEY_LEN];
245 int wrapped_key_len = sizeof(wrapped_key_data);
247 if (PK11_CipherOp(mWrapKeyContext, wrapped_key_data, &wrapped_key_len,
248 sizeof(wrapped_key_data), key->data, key->len) != SECSuccess)
250 throw css::uno::RuntimeException("PK11_CipherOp failure", css::uno::Reference<css::uno::XInterface>());
253 if (PK11_Finalize(mWrapKeyContext) != SECSuccess)
254 throw css::uno::RuntimeException("PK11_Finalize failure", css::uno::Reference<css::uno::XInterface>());
257 * Finally unwrap sym key
259 SECItem wrapped_key = {};
260 wrapped_key.data = wrapped_key_data;
261 wrapped_key.len = wrapped_key_len;
263 mSymKey = PK11_UnwrapSymKey(mWrapKey, wrap_mechanism, &tmp_sec_item, &wrapped_key,
264 mechanism, operation, key->len);
266 return mSymKey;
269 void setupCryptoContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType type, CK_ATTRIBUTE_TYPE operation)
271 CK_MECHANISM_TYPE mechanism = static_cast<CK_ULONG>(-1);
273 SECItem ivItem;
274 ivItem.type = siBuffer;
275 if(iv.empty())
276 ivItem.data = nullptr;
277 else
278 ivItem.data = iv.data();
279 ivItem.len = iv.size();
281 SECItem* pIvItem = nullptr;
283 switch(type)
285 case Crypto::CryptoType::AES_128_ECB:
286 mechanism = CKM_AES_ECB;
287 break;
288 case Crypto::CryptoType::AES_128_CBC:
289 mechanism = CKM_AES_CBC;
290 pIvItem = &ivItem;
291 break;
292 case Crypto::CryptoType::AES_256_CBC:
293 mechanism = CKM_AES_CBC;
294 pIvItem = &ivItem;
295 break;
296 default:
297 break;
300 mSlot = PK11_GetBestSlot(mechanism, nullptr);
302 if (!mSlot)
303 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference<css::uno::XInterface>());
305 SECItem keyItem;
306 keyItem.type = siBuffer;
307 keyItem.data = key.data();
308 keyItem.len = key.size();
310 mSymKey = ImportSymKey(mechanism, CKA_ENCRYPT, &keyItem);
311 if (!mSymKey)
312 throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference<css::uno::XInterface>());
314 mSecParam = PK11_ParamFromIV(mechanism, pIvItem);
315 mContext = PK11_CreateContextBySymKey(mechanism, operation, mSymKey, mSecParam);
318 void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
320 CK_MECHANISM_TYPE aMechanism = static_cast<CK_ULONG>(-1);
322 switch(eType)
324 case CryptoHashType::SHA1:
325 aMechanism = CKM_SHA_1_HMAC;
326 break;
327 case CryptoHashType::SHA256:
328 aMechanism = CKM_SHA256_HMAC;
329 break;
330 case CryptoHashType::SHA384:
331 aMechanism = CKM_SHA384_HMAC;
332 break;
333 case CryptoHashType::SHA512:
334 aMechanism = CKM_SHA512_HMAC;
335 break;
338 mSlot = PK11_GetBestSlot(aMechanism, nullptr);
340 if (!mSlot)
341 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference<css::uno::XInterface>());
343 SECItem aKeyItem;
344 aKeyItem.data = rKey.data();
345 aKeyItem.len = rKey.size();
347 mSymKey = ImportSymKey(aMechanism, CKA_SIGN, &aKeyItem);
348 if (!mSymKey)
349 throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference<css::uno::XInterface>());
351 SECItem param;
352 param.data = nullptr;
353 param.len = 0;
354 mContext = PK11_CreateContextBySymKey(aMechanism, CKA_SIGN, mSymKey, &param);
357 #else
358 struct CryptoImpl
360 #endif
362 Crypto::Crypto()
363 : mpImpl(std::make_unique<CryptoImpl>())
367 Crypto::~Crypto()
371 // DECRYPT
373 Decrypt::Decrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type)
375 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
376 (void)key;
377 (void)iv;
378 (void)type;
379 #endif
381 #if USE_TLS_OPENSSL
382 mpImpl->setupDecryptContext(key, iv, type);
383 #endif
385 #if USE_TLS_NSS
386 mpImpl->setupCryptoContext(key, iv, type, CKA_DECRYPT);
387 #endif // USE_TLS_NSS
390 sal_uInt32 Decrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, sal_uInt32 inputLength)
392 int outputLength = 0;
394 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
395 sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength;
396 #else
397 (void)output;
398 (void)input;
399 (void)inputLength;
400 #endif
402 #if USE_TLS_OPENSSL
403 (void)EVP_DecryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), actualInputLength);
404 #endif // USE_TLS_OPENSSL
406 #if USE_TLS_NSS
407 if (!mpImpl->mContext)
408 return 0;
409 (void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength);
410 #endif // USE_TLS_NSS
412 return static_cast<sal_uInt32>(outputLength);
415 sal_uInt32 Decrypt::aes128ecb(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, std::vector<sal_uInt8>& key)
417 sal_uInt32 outputLength = 0;
418 std::vector<sal_uInt8> iv;
419 Decrypt crypto(key, iv, Crypto::AES_128_ECB);
420 outputLength = crypto.update(output, input);
421 return outputLength;
424 // ENCRYPT
426 Encrypt::Encrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type)
428 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
429 (void)key;
430 (void)iv;
431 (void)type;
432 #endif
434 #if USE_TLS_OPENSSL
435 mpImpl->setupEncryptContext(key, iv, type);
436 #elif USE_TLS_NSS
437 mpImpl->setupCryptoContext(key, iv, type, CKA_ENCRYPT);
438 #endif // USE_TLS_NSS
441 sal_uInt32 Encrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, sal_uInt32 inputLength)
443 int outputLength = 0;
445 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
446 sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength;
447 #else
448 (void)output;
449 (void)input;
450 (void)inputLength;
451 #endif
453 #if USE_TLS_OPENSSL
454 (void)EVP_EncryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), actualInputLength);
455 #endif // USE_TLS_OPENSSL
457 #if USE_TLS_NSS
458 (void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength);
459 #endif // USE_TLS_NSS
461 return static_cast<sal_uInt32>(outputLength);
464 // CryptoHash - HMAC
466 namespace
469 sal_Int32 getSizeForHashType(CryptoHashType eType)
471 switch (eType)
473 case CryptoHashType::SHA1: return 20;
474 case CryptoHashType::SHA256: return 32;
475 case CryptoHashType::SHA384: return 48;
476 case CryptoHashType::SHA512: return 64;
478 return 0;
481 } // end anonymous namespace
483 CryptoHash::CryptoHash(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
484 : mnHashSize(getSizeForHashType(eType))
486 #if USE_TLS_OPENSSL
487 mpImpl->setupCryptoHashContext(rKey, eType);
488 #elif USE_TLS_NSS
489 mpImpl->setupCryptoHashContext(rKey, eType);
490 PK11_DigestBegin(mpImpl->mContext);
491 #else
492 (void)rKey;
493 #endif
496 bool CryptoHash::update(std::vector<sal_uInt8>& rInput, sal_uInt32 nInputLength)
498 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
499 sal_uInt32 nActualInputLength = (nInputLength == 0 || nInputLength > rInput.size()) ? rInput.size() : nInputLength;
500 #else
501 (void)rInput;
502 (void)nInputLength;
503 #endif
505 #if USE_TLS_OPENSSL
506 SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_Update' is deprecated
507 return HMAC_Update(mpImpl->mpHmacContext.get(), rInput.data(), nActualInputLength) != 0;
508 SAL_WNODEPRECATED_DECLARATIONS_POP
509 #elif USE_TLS_NSS
510 return PK11_DigestOp(mpImpl->mContext, rInput.data(), nActualInputLength) == SECSuccess;
511 #else
512 return false; // ???
513 #endif
516 std::vector<sal_uInt8> CryptoHash::finalize()
518 std::vector<sal_uInt8> aHash(mnHashSize, 0);
519 unsigned int nSizeWritten;
520 #if USE_TLS_OPENSSL
521 SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_Final' is deprecated
522 (void) HMAC_Final(mpImpl->mpHmacContext.get(), aHash.data(), &nSizeWritten);
523 SAL_WNODEPRECATED_DECLARATIONS_POP
524 #elif USE_TLS_NSS
525 PK11_DigestFinal(mpImpl->mContext, aHash.data(), &nSizeWritten, aHash.size());
526 #endif
527 (void)nSizeWritten;
529 return aHash;
532 } // namespace oox::crypto
534 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */