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>
14 #include <config_oox.h>
17 #include <openssl/evp.h>
18 #include <openssl/sha.h>
19 #include <openssl/hmac.h>
20 #endif // USE_TLS_OPENSSL
28 namespace oox::crypto
{
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
);
41 static void HMAC_CTX_free(HMAC_CTX
*pContext
)
43 HMAC_CTX_cleanup(pContext
);
52 void operator()(EVP_CIPHER_CTX
* p
) { EVP_CIPHER_CTX_free(p
); }
57 void operator()(HMAC_CTX
* p
) { HMAC_CTX_free(p
); }
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)
78 EVP_EncryptInit_ex(mpContext
.get(), cipher
, nullptr, key
.data(), nullptr);
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)
93 const size_t nMinKeySize
= EVP_CIPHER_key_length(pCipher
);
94 if (key
.size() < nMinKeySize
)
95 key
.resize(nMinKeySize
, 0);
98 EVP_DecryptInit_ex(mpContext
.get(), pCipher
, nullptr, key
.data(), nullptr);
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;
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);
131 EVP_CIPHER_CTX_cleanup(mpContext
.get());
134 static const EVP_CIPHER
* getCipher(Crypto::CryptoType 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();
153 #define MAX_WRAPPED_KEY_LEN 128
158 PK11Context
* mContext
;
161 PK11Context
* mWrapKeyContext
;
162 PK11SymKey
* mWrapKey
;
169 , mWrapKeyContext(nullptr)
172 // Initialize NSS, database functions are not needed
173 if (!NSS_IsInitialized())
175 auto const e
= NSS_NoDB_Init(nullptr);
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
)) + ")");
188 PK11_DestroyContext(mContext
, PR_TRUE
);
190 SECITEM_FreeItem(mSecParam
, PR_TRUE
);
192 PK11_FreeSymKey(mSymKey
);
194 PK11_DestroyContext(mWrapKeyContext
, PR_TRUE
);
196 PK11_FreeSymKey(mWrapKey
);
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
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);
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
);
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);
267 ivItem
.type
= siBuffer
;
269 ivItem
.data
= nullptr;
271 ivItem
.data
= iv
.data();
272 ivItem
.len
= iv
.size();
274 SECItem
* pIvItem
= nullptr;
278 case Crypto::CryptoType::AES_128_ECB
:
279 mechanism
= CKM_AES_ECB
;
281 case Crypto::CryptoType::AES_128_CBC
:
282 mechanism
= CKM_AES_CBC
;
285 case Crypto::CryptoType::AES_256_CBC
:
286 mechanism
= CKM_AES_CBC
;
293 mSlot
= PK11_GetBestSlot(mechanism
, nullptr);
296 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference
<css::uno::XInterface
>());
299 keyItem
.type
= siBuffer
;
300 keyItem
.data
= key
.data();
301 keyItem
.len
= key
.size();
303 mSymKey
= ImportSymKey(mechanism
, CKA_ENCRYPT
, &keyItem
);
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);
317 case CryptoHashType::SHA1
:
318 aMechanism
= CKM_SHA_1_HMAC
;
320 case CryptoHashType::SHA256
:
321 aMechanism
= CKM_SHA256_HMAC
;
323 case CryptoHashType::SHA384
:
324 aMechanism
= CKM_SHA384_HMAC
;
326 case CryptoHashType::SHA512
:
327 aMechanism
= CKM_SHA512_HMAC
;
331 mSlot
= PK11_GetBestSlot(aMechanism
, nullptr);
334 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference
<css::uno::XInterface
>());
337 aKeyItem
.data
= rKey
.data();
338 aKeyItem
.len
= rKey
.size();
340 mSymKey
= ImportSymKey(aMechanism
, CKA_SIGN
, &aKeyItem
);
342 throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference
<css::uno::XInterface
>());
345 param
.data
= nullptr;
347 mContext
= PK11_CreateContextBySymKey(aMechanism
, CKA_SIGN
, mSymKey
, ¶m
);
356 : mpImpl(std::make_unique
<CryptoImpl
>())
366 Decrypt::Decrypt(std::vector
<sal_uInt8
>& key
, std::vector
<sal_uInt8
>& iv
, CryptoType type
)
368 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
375 mpImpl
->setupDecryptContext(key
, iv
, type
);
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
;
396 (void)EVP_DecryptUpdate(mpImpl
->mpContext
.get(), output
.data(), &outputLength
, input
.data(), actualInputLength
);
397 #endif // USE_TLS_OPENSSL
400 if (!mpImpl
->mContext
)
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
);
419 Encrypt::Encrypt(std::vector
<sal_uInt8
>& key
, std::vector
<sal_uInt8
>& iv
, CryptoType type
)
421 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
428 mpImpl
->setupEncryptContext(key
, iv
, type
);
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
;
447 (void)EVP_EncryptUpdate(mpImpl
->mpContext
.get(), output
.data(), &outputLength
, input
.data(), actualInputLength
);
448 #endif // USE_TLS_OPENSSL
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
);
462 sal_Int32
getSizeForHashType(CryptoHashType 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;
474 } // end anonymous namespace
476 CryptoHash::CryptoHash(std::vector
<sal_uInt8
>& rKey
, CryptoHashType eType
)
477 : mnHashSize(getSizeForHashType(eType
))
480 mpImpl
->setupCryptoHashContext(rKey
, eType
);
482 mpImpl
->setupCryptoHashContext(rKey
, eType
);
483 PK11_DigestBegin(mpImpl
->mContext
);
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
;
499 return HMAC_Update(mpImpl
->mpHmacContext
.get(), rInput
.data(), nActualInputLength
) != 0;
501 return PK11_DigestOp(mpImpl
->mContext
, rInput
.data(), nActualInputLength
) == SECSuccess
;
507 std::vector
<sal_uInt8
> CryptoHash::finalize()
509 std::vector
<sal_uInt8
> aHash(mnHashSize
, 0);
510 unsigned int nSizeWritten
;
512 (void) HMAC_Final(mpImpl
->mpHmacContext
.get(), aHash
.data(), &nSizeWritten
);
514 PK11_DigestFinal(mpImpl
->mContext
, aHash
.data(), &nSizeWritten
, aHash
.size());
521 } // namespace oox::crypto
523 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */