1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
11 #include <oox/crypto/CryptTools.hxx>
12 #include <com/sun/star/uno/RuntimeException.hpp>
13 #include <sal/types.h>
15 #include <config_oox.h>
18 #include <openssl/evp.h>
19 #include <openssl/sha.h>
20 #include <openssl/hmac.h>
21 #endif // USE_TLS_OPENSSL
29 namespace oox::crypto
{
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
);
42 static void HMAC_CTX_free(HMAC_CTX
*pContext
)
44 HMAC_CTX_cleanup(pContext
);
53 void operator()(EVP_CIPHER_CTX
* p
) { EVP_CIPHER_CTX_free(p
); }
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
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)
81 EVP_EncryptInit_ex(mpContext
.get(), cipher
, nullptr, key
.data(), nullptr);
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)
96 const size_t nMinKeySize
= EVP_CIPHER_key_length(pCipher
);
97 if (key
.size() < nMinKeySize
)
98 key
.resize(nMinKeySize
, 0);
101 EVP_DecryptInit_ex(mpContext
.get(), pCipher
, nullptr, key
.data(), nullptr);
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;
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
138 EVP_CIPHER_CTX_cleanup(mpContext
.get());
141 static const EVP_CIPHER
* getCipher(Crypto::CryptoType 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();
160 #define MAX_WRAPPED_KEY_LEN 128
165 PK11Context
* mContext
;
168 PK11Context
* mWrapKeyContext
;
169 PK11SymKey
* mWrapKey
;
176 , mWrapKeyContext(nullptr)
179 // Initialize NSS, database functions are not needed
180 if (!NSS_IsInitialized())
182 auto const e
= NSS_NoDB_Init(nullptr);
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
)) + ")");
195 PK11_DestroyContext(mContext
, PR_TRUE
);
197 SECITEM_FreeItem(mSecParam
, PR_TRUE
);
199 PK11_FreeSymKey(mSymKey
);
201 PK11_DestroyContext(mWrapKeyContext
, PR_TRUE
);
203 PK11_FreeSymKey(mWrapKey
);
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
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);
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
);
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);
274 ivItem
.type
= siBuffer
;
276 ivItem
.data
= nullptr;
278 ivItem
.data
= iv
.data();
279 ivItem
.len
= iv
.size();
281 SECItem
* pIvItem
= nullptr;
285 case Crypto::CryptoType::AES_128_ECB
:
286 mechanism
= CKM_AES_ECB
;
288 case Crypto::CryptoType::AES_128_CBC
:
289 mechanism
= CKM_AES_CBC
;
292 case Crypto::CryptoType::AES_256_CBC
:
293 mechanism
= CKM_AES_CBC
;
300 mSlot
= PK11_GetBestSlot(mechanism
, nullptr);
303 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference
<css::uno::XInterface
>());
306 keyItem
.type
= siBuffer
;
307 keyItem
.data
= key
.data();
308 keyItem
.len
= key
.size();
310 mSymKey
= ImportSymKey(mechanism
, CKA_ENCRYPT
, &keyItem
);
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);
324 case CryptoHashType::SHA1
:
325 aMechanism
= CKM_SHA_1_HMAC
;
327 case CryptoHashType::SHA256
:
328 aMechanism
= CKM_SHA256_HMAC
;
330 case CryptoHashType::SHA384
:
331 aMechanism
= CKM_SHA384_HMAC
;
333 case CryptoHashType::SHA512
:
334 aMechanism
= CKM_SHA512_HMAC
;
338 mSlot
= PK11_GetBestSlot(aMechanism
, nullptr);
341 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference
<css::uno::XInterface
>());
344 aKeyItem
.data
= rKey
.data();
345 aKeyItem
.len
= rKey
.size();
347 mSymKey
= ImportSymKey(aMechanism
, CKA_SIGN
, &aKeyItem
);
349 throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference
<css::uno::XInterface
>());
352 param
.data
= nullptr;
354 mContext
= PK11_CreateContextBySymKey(aMechanism
, CKA_SIGN
, mSymKey
, ¶m
);
363 : mpImpl(std::make_unique
<CryptoImpl
>())
373 Decrypt::Decrypt(std::vector
<sal_uInt8
>& key
, std::vector
<sal_uInt8
>& iv
, CryptoType type
)
375 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
382 mpImpl
->setupDecryptContext(key
, iv
, type
);
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
;
403 (void)EVP_DecryptUpdate(mpImpl
->mpContext
.get(), output
.data(), &outputLength
, input
.data(), actualInputLength
);
404 #endif // USE_TLS_OPENSSL
407 if (!mpImpl
->mContext
)
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
);
426 Encrypt::Encrypt(std::vector
<sal_uInt8
>& key
, std::vector
<sal_uInt8
>& iv
, CryptoType type
)
428 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
435 mpImpl
->setupEncryptContext(key
, iv
, type
);
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
;
454 (void)EVP_EncryptUpdate(mpImpl
->mpContext
.get(), output
.data(), &outputLength
, input
.data(), actualInputLength
);
455 #endif // USE_TLS_OPENSSL
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
);
469 sal_Int32
getSizeForHashType(CryptoHashType 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;
481 } // end anonymous namespace
483 CryptoHash::CryptoHash(std::vector
<sal_uInt8
>& rKey
, CryptoHashType eType
)
484 : mnHashSize(getSizeForHashType(eType
))
487 mpImpl
->setupCryptoHashContext(rKey
, eType
);
489 mpImpl
->setupCryptoHashContext(rKey
, eType
);
490 PK11_DigestBegin(mpImpl
->mContext
);
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
;
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
510 return PK11_DigestOp(mpImpl
->mContext
, rInput
.data(), nActualInputLength
) == SECSuccess
;
516 std::vector
<sal_uInt8
> CryptoHash::finalize()
518 std::vector
<sal_uInt8
> aHash(mnHashSize
, 0);
519 unsigned int nSizeWritten
;
521 SAL_WNODEPRECATED_DECLARATIONS_PUSH
// 'HMAC_Final' is deprecated
522 (void) HMAC_Final(mpImpl
->mpHmacContext
.get(), aHash
.data(), &nSizeWritten
);
523 SAL_WNODEPRECATED_DECLARATIONS_POP
525 PK11_DigestFinal(mpImpl
->mContext
, aHash
.data(), &nSizeWritten
, aHash
.size());
532 } // namespace oox::crypto
534 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */