Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / oox / source / crypto / CryptTools.cxx
blob86d8ab270d194fcb1799bf42a3a8feb4d1d7f16c
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>
14 #include <config_oox.h>
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
22 #if USE_TLS_NSS
23 #include <nss.h>
24 #include <nspr.h>
25 #include <pk11pub.h>
26 #endif // USE_TLS_NSS
28 namespace oox::crypto {
30 #if USE_TLS_OPENSSL
32 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
34 static HMAC_CTX *HMAC_CTX_new(void)
36 HMAC_CTX *pContext = new HMAC_CTX;
37 HMAC_CTX_init(pContext);
38 return pContext;
41 static void HMAC_CTX_free(HMAC_CTX *pContext)
43 HMAC_CTX_cleanup(pContext);
44 delete pContext;
46 #endif
48 namespace
50 struct cipher_delete
52 void operator()(EVP_CIPHER_CTX* p) { EVP_CIPHER_CTX_free(p); }
55 struct hmac_delete
57 void operator()(HMAC_CTX* p) { HMAC_CTX_free(p); }
61 struct CryptoImpl
63 std::unique_ptr<EVP_CIPHER_CTX, cipher_delete> mpContext;
64 std::unique_ptr<HMAC_CTX, hmac_delete> mpHmacContext;
66 CryptoImpl() = default;
68 void setupEncryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType eType)
70 mpContext.reset(EVP_CIPHER_CTX_new());
71 EVP_CIPHER_CTX_init(mpContext.get());
73 const EVP_CIPHER* cipher = getCipher(eType);
74 if (cipher == nullptr)
75 return;
77 if (iv.empty())
78 EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), nullptr);
79 else
80 EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), iv.data());
81 EVP_CIPHER_CTX_set_padding(mpContext.get(), 0);
84 void setupDecryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType eType)
86 mpContext.reset(EVP_CIPHER_CTX_new());
87 EVP_CIPHER_CTX_init(mpContext.get());
89 const EVP_CIPHER* pCipher = getCipher(eType);
90 if (pCipher == nullptr)
91 return;
93 const size_t nMinKeySize = EVP_CIPHER_key_length(pCipher);
94 if (key.size() < nMinKeySize)
95 key.resize(nMinKeySize, 0);
97 if (iv.empty())
98 EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), nullptr);
99 else
101 const size_t nMinIVSize = EVP_CIPHER_iv_length(pCipher);
102 if (iv.size() < nMinIVSize)
103 iv.resize(nMinIVSize, 0);
105 EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), iv.data());
107 EVP_CIPHER_CTX_set_padding(mpContext.get(), 0);
110 void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
112 mpHmacContext.reset(HMAC_CTX_new());
113 const EVP_MD* aEvpMd = nullptr;
114 switch (eType)
116 case CryptoHashType::SHA1:
117 aEvpMd = EVP_sha1(); break;
118 case CryptoHashType::SHA256:
119 aEvpMd = EVP_sha256(); break;
120 case CryptoHashType::SHA384:
121 aEvpMd = EVP_sha384(); break;
122 case CryptoHashType::SHA512:
123 aEvpMd = EVP_sha512(); break;
125 HMAC_Init_ex(mpHmacContext.get(), rKey.data(), rKey.size(), aEvpMd, nullptr);
128 ~CryptoImpl()
130 if (mpContext)
131 EVP_CIPHER_CTX_cleanup(mpContext.get());
134 static const EVP_CIPHER* getCipher(Crypto::CryptoType type)
136 switch(type)
138 case Crypto::CryptoType::AES_128_ECB:
139 return EVP_aes_128_ecb();
140 case Crypto::CryptoType::AES_128_CBC:
141 return EVP_aes_128_cbc();
142 case Crypto::CryptoType::AES_256_CBC:
143 return EVP_aes_256_cbc();
144 default:
145 break;
147 return nullptr;
151 #elif USE_TLS_NSS
153 #define MAX_WRAPPED_KEY_LEN 128
155 struct CryptoImpl
157 PK11SlotInfo* mSlot;
158 PK11Context* mContext;
159 SECItem* mSecParam;
160 PK11SymKey* mSymKey;
161 PK11Context* mWrapKeyContext;
162 PK11SymKey* mWrapKey;
164 CryptoImpl()
165 : mSlot(nullptr)
166 , mContext(nullptr)
167 , mSecParam(nullptr)
168 , mSymKey(nullptr)
169 , mWrapKeyContext(nullptr)
170 , mWrapKey(nullptr)
172 // Initialize NSS, database functions are not needed
173 if (!NSS_IsInitialized())
175 auto const e = NSS_NoDB_Init(nullptr);
176 if (e != SECSuccess)
178 PRErrorCode error = PR_GetError();
179 const char* errorText = PR_ErrorToName(error);
180 throw css::uno::RuntimeException("NSS_NoDB_Init failed with " + OUString(errorText, strlen(errorText), RTL_TEXTENCODING_UTF8) + " (" + OUString::number(static_cast<int>(error)) + ")");
185 ~CryptoImpl()
187 if (mContext)
188 PK11_DestroyContext(mContext, PR_TRUE);
189 if (mSecParam)
190 SECITEM_FreeItem(mSecParam, PR_TRUE);
191 if (mSymKey)
192 PK11_FreeSymKey(mSymKey);
193 if (mWrapKeyContext)
194 PK11_DestroyContext(mWrapKeyContext, PR_TRUE);
195 if (mWrapKey)
196 PK11_FreeSymKey(mWrapKey);
197 if (mSlot)
198 PK11_FreeSlot(mSlot);
201 PK11SymKey* ImportSymKey(CK_MECHANISM_TYPE mechanism, CK_ATTRIBUTE_TYPE operation, SECItem* key)
203 mSymKey = PK11_ImportSymKey(mSlot, mechanism, PK11_OriginUnwrap, operation, key, nullptr);
204 if (!mSymKey) //rhbz#1614419 maybe failed due to FIPS, use rhbz#1461450 style workaround
207 * Without FIPS it would be possible to just use
208 * mSymKey = PK11_ImportSymKey( mSlot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr );
209 * with FIPS NSS Level 2 certification has to be "workarounded" (so it becomes Level 1) by using
210 * following method:
211 * 1. Generate wrap key
212 * 2. Encrypt authkey with wrap key
213 * 3. Unwrap encrypted authkey using wrap key
217 * Generate wrapping key
219 CK_MECHANISM_TYPE wrap_mechanism = PK11_GetBestWrapMechanism(mSlot);
220 int wrap_key_len = PK11_GetBestKeyLength(mSlot, wrap_mechanism);
221 mWrapKey = PK11_KeyGen(mSlot, wrap_mechanism, nullptr, wrap_key_len, nullptr);
222 if (!mWrapKey)
223 throw css::uno::RuntimeException("PK11_KeyGen SymKey failure", css::uno::Reference<css::uno::XInterface>());
226 * Encrypt authkey with wrapping key
230 * Initialization of IV is not needed because PK11_GetBestWrapMechanism should return ECB mode
232 SECItem tmp_sec_item = {};
233 mWrapKeyContext = PK11_CreateContextBySymKey(wrap_mechanism, CKA_ENCRYPT, mWrapKey, &tmp_sec_item);
234 if (!mWrapKeyContext)
235 throw css::uno::RuntimeException("PK11_CreateContextBySymKey failure", css::uno::Reference<css::uno::XInterface>());
237 unsigned char wrapped_key_data[MAX_WRAPPED_KEY_LEN];
238 int wrapped_key_len = sizeof(wrapped_key_data);
240 if (PK11_CipherOp(mWrapKeyContext, wrapped_key_data, &wrapped_key_len,
241 sizeof(wrapped_key_data), key->data, key->len) != SECSuccess)
243 throw css::uno::RuntimeException("PK11_CipherOp failure", css::uno::Reference<css::uno::XInterface>());
246 if (PK11_Finalize(mWrapKeyContext) != SECSuccess)
247 throw css::uno::RuntimeException("PK11_Finalize failure", css::uno::Reference<css::uno::XInterface>());
250 * Finally unwrap sym key
252 SECItem wrapped_key = {};
253 wrapped_key.data = wrapped_key_data;
254 wrapped_key.len = wrapped_key_len;
256 mSymKey = PK11_UnwrapSymKey(mWrapKey, wrap_mechanism, &tmp_sec_item, &wrapped_key,
257 mechanism, operation, key->len);
259 return mSymKey;
262 void setupCryptoContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType type, CK_ATTRIBUTE_TYPE operation)
264 CK_MECHANISM_TYPE mechanism = static_cast<CK_ULONG>(-1);
266 SECItem ivItem;
267 ivItem.type = siBuffer;
268 if(iv.empty())
269 ivItem.data = nullptr;
270 else
271 ivItem.data = iv.data();
272 ivItem.len = iv.size();
274 SECItem* pIvItem = nullptr;
276 switch(type)
278 case Crypto::CryptoType::AES_128_ECB:
279 mechanism = CKM_AES_ECB;
280 break;
281 case Crypto::CryptoType::AES_128_CBC:
282 mechanism = CKM_AES_CBC;
283 pIvItem = &ivItem;
284 break;
285 case Crypto::CryptoType::AES_256_CBC:
286 mechanism = CKM_AES_CBC;
287 pIvItem = &ivItem;
288 break;
289 default:
290 break;
293 mSlot = PK11_GetBestSlot(mechanism, nullptr);
295 if (!mSlot)
296 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference<css::uno::XInterface>());
298 SECItem keyItem;
299 keyItem.type = siBuffer;
300 keyItem.data = key.data();
301 keyItem.len = key.size();
303 mSymKey = ImportSymKey(mechanism, CKA_ENCRYPT, &keyItem);
304 if (!mSymKey)
305 throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference<css::uno::XInterface>());
307 mSecParam = PK11_ParamFromIV(mechanism, pIvItem);
308 mContext = PK11_CreateContextBySymKey(mechanism, operation, mSymKey, mSecParam);
311 void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
313 CK_MECHANISM_TYPE aMechanism = static_cast<CK_ULONG>(-1);
315 switch(eType)
317 case CryptoHashType::SHA1:
318 aMechanism = CKM_SHA_1_HMAC;
319 break;
320 case CryptoHashType::SHA256:
321 aMechanism = CKM_SHA256_HMAC;
322 break;
323 case CryptoHashType::SHA384:
324 aMechanism = CKM_SHA384_HMAC;
325 break;
326 case CryptoHashType::SHA512:
327 aMechanism = CKM_SHA512_HMAC;
328 break;
331 mSlot = PK11_GetBestSlot(aMechanism, nullptr);
333 if (!mSlot)
334 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference<css::uno::XInterface>());
336 SECItem aKeyItem;
337 aKeyItem.data = rKey.data();
338 aKeyItem.len = rKey.size();
340 mSymKey = ImportSymKey(aMechanism, CKA_SIGN, &aKeyItem);
341 if (!mSymKey)
342 throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference<css::uno::XInterface>());
344 SECItem param;
345 param.data = nullptr;
346 param.len = 0;
347 mContext = PK11_CreateContextBySymKey(aMechanism, CKA_SIGN, mSymKey, &param);
350 #else
351 struct CryptoImpl
353 #endif
355 Crypto::Crypto()
356 : mpImpl(std::make_unique<CryptoImpl>())
360 Crypto::~Crypto()
364 // DECRYPT
366 Decrypt::Decrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type)
368 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
369 (void)key;
370 (void)iv;
371 (void)type;
372 #endif
374 #if USE_TLS_OPENSSL
375 mpImpl->setupDecryptContext(key, iv, type);
376 #endif
378 #if USE_TLS_NSS
379 mpImpl->setupCryptoContext(key, iv, type, CKA_DECRYPT);
380 #endif // USE_TLS_NSS
383 sal_uInt32 Decrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, sal_uInt32 inputLength)
385 int outputLength = 0;
387 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
388 sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength;
389 #else
390 (void)output;
391 (void)input;
392 (void)inputLength;
393 #endif
395 #if USE_TLS_OPENSSL
396 (void)EVP_DecryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), actualInputLength);
397 #endif // USE_TLS_OPENSSL
399 #if USE_TLS_NSS
400 if (!mpImpl->mContext)
401 return 0;
402 (void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength);
403 #endif // USE_TLS_NSS
405 return static_cast<sal_uInt32>(outputLength);
408 sal_uInt32 Decrypt::aes128ecb(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, std::vector<sal_uInt8>& key)
410 sal_uInt32 outputLength = 0;
411 std::vector<sal_uInt8> iv;
412 Decrypt crypto(key, iv, Crypto::AES_128_ECB);
413 outputLength = crypto.update(output, input);
414 return outputLength;
417 // ENCRYPT
419 Encrypt::Encrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type)
421 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
422 (void)key;
423 (void)iv;
424 (void)type;
425 #endif
427 #if USE_TLS_OPENSSL
428 mpImpl->setupEncryptContext(key, iv, type);
429 #elif USE_TLS_NSS
430 mpImpl->setupCryptoContext(key, iv, type, CKA_ENCRYPT);
431 #endif // USE_TLS_NSS
434 sal_uInt32 Encrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, sal_uInt32 inputLength)
436 int outputLength = 0;
438 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
439 sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength;
440 #else
441 (void)output;
442 (void)input;
443 (void)inputLength;
444 #endif
446 #if USE_TLS_OPENSSL
447 (void)EVP_EncryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), actualInputLength);
448 #endif // USE_TLS_OPENSSL
450 #if USE_TLS_NSS
451 (void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength);
452 #endif // USE_TLS_NSS
454 return static_cast<sal_uInt32>(outputLength);
457 // CryptoHash - HMAC
459 namespace
462 sal_Int32 getSizeForHashType(CryptoHashType eType)
464 switch (eType)
466 case CryptoHashType::SHA1: return 20;
467 case CryptoHashType::SHA256: return 32;
468 case CryptoHashType::SHA384: return 48;
469 case CryptoHashType::SHA512: return 64;
471 return 0;
474 } // end anonymous namespace
476 CryptoHash::CryptoHash(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
477 : mnHashSize(getSizeForHashType(eType))
479 #if USE_TLS_OPENSSL
480 mpImpl->setupCryptoHashContext(rKey, eType);
481 #elif USE_TLS_NSS
482 mpImpl->setupCryptoHashContext(rKey, eType);
483 PK11_DigestBegin(mpImpl->mContext);
484 #else
485 (void)rKey;
486 #endif
489 bool CryptoHash::update(std::vector<sal_uInt8>& rInput, sal_uInt32 nInputLength)
491 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
492 sal_uInt32 nActualInputLength = (nInputLength == 0 || nInputLength > rInput.size()) ? rInput.size() : nInputLength;
493 #else
494 (void)rInput;
495 (void)nInputLength;
496 #endif
498 #if USE_TLS_OPENSSL
499 return HMAC_Update(mpImpl->mpHmacContext.get(), rInput.data(), nActualInputLength) != 0;
500 #elif USE_TLS_NSS
501 return PK11_DigestOp(mpImpl->mContext, rInput.data(), nActualInputLength) == SECSuccess;
502 #else
503 return false; // ???
504 #endif
507 std::vector<sal_uInt8> CryptoHash::finalize()
509 std::vector<sal_uInt8> aHash(mnHashSize, 0);
510 unsigned int nSizeWritten;
511 #if USE_TLS_OPENSSL
512 (void) HMAC_Final(mpImpl->mpHmacContext.get(), aHash.data(), &nSizeWritten);
513 #elif USE_TLS_NSS
514 PK11_DigestFinal(mpImpl->mContext, aHash.data(), &nSizeWritten, aHash.size());
515 #endif
516 (void)nSizeWritten;
518 return aHash;
521 } // namespace oox::crypto
523 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */