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 <filter/msfilter/mscodec.hxx>
13 #include <com/sun/star/uno/RuntimeException.hpp>
16 #include <openssl/evp.h>
17 #include <openssl/sha.h>
18 #include <openssl/hmac.h>
19 #endif // USE_TLS_OPENSSL
33 std::unique_ptr
<EVP_CIPHER_CTX
> mpContext
;
34 std::unique_ptr
<HMAC_CTX
> mpHmacContext
;
36 CryptoImpl() = default;
38 void setupEncryptContext(std::vector
<sal_uInt8
>& key
, std::vector
<sal_uInt8
>& iv
, Crypto::CryptoType eType
)
40 mpContext
.reset(new EVP_CIPHER_CTX
);
41 EVP_CIPHER_CTX_init(mpContext
.get());
43 const EVP_CIPHER
* cipher
= getCipher(eType
);
44 if (cipher
== nullptr)
48 EVP_EncryptInit_ex(mpContext
.get(), cipher
, nullptr, key
.data(), 0);
50 EVP_EncryptInit_ex(mpContext
.get(), cipher
, nullptr, key
.data(), iv
.data());
51 EVP_CIPHER_CTX_set_padding(mpContext
.get(), 0);
54 void setupDecryptContext(std::vector
<sal_uInt8
>& key
, std::vector
<sal_uInt8
>& iv
, Crypto::CryptoType eType
)
56 mpContext
.reset(new EVP_CIPHER_CTX
);
57 EVP_CIPHER_CTX_init(mpContext
.get());
59 const EVP_CIPHER
* pCipher
= getCipher(eType
);
60 if (pCipher
== nullptr)
63 const size_t nMinKeySize
= EVP_CIPHER_key_length(pCipher
);
64 if (key
.size() < nMinKeySize
)
65 key
.resize(nMinKeySize
, 0);
68 EVP_DecryptInit_ex(mpContext
.get(), pCipher
, nullptr, key
.data(), 0);
71 const size_t nMinIVSize
= EVP_CIPHER_iv_length(pCipher
);
72 if (iv
.size() < nMinIVSize
)
73 iv
.resize(nMinIVSize
, 0);
75 EVP_DecryptInit_ex(mpContext
.get(), pCipher
, nullptr, key
.data(), iv
.data());
77 EVP_CIPHER_CTX_set_padding(mpContext
.get(), 0);
80 void setupCryptoHashContext(std::vector
<sal_uInt8
>& rKey
, CryptoHashType eType
)
82 mpHmacContext
.reset(new HMAC_CTX
);
83 HMAC_CTX_init(mpHmacContext
.get());
87 case CryptoHashType::SHA1
:
88 aEvpMd
= EVP_sha1(); break;
89 case CryptoHashType::SHA256
:
90 aEvpMd
= EVP_sha256(); break;
91 case CryptoHashType::SHA512
:
92 aEvpMd
= EVP_sha512(); break;
94 HMAC_Init(mpHmacContext
.get(), rKey
.data(), rKey
.size(), aEvpMd
);
100 EVP_CIPHER_CTX_cleanup(mpContext
.get());
102 HMAC_CTX_cleanup(mpHmacContext
.get());
105 const EVP_CIPHER
* getCipher(Crypto::CryptoType type
)
109 case Crypto::CryptoType::AES_128_ECB
:
110 return EVP_aes_128_ecb();
111 case Crypto::CryptoType::AES_128_CBC
:
112 return EVP_aes_128_cbc();
113 case Crypto::CryptoType::AES_256_CBC
:
114 return EVP_aes_256_cbc();
124 #define MAX_WRAPPED_KEY_LEN 128
129 PK11Context
* mContext
;
132 PK11Context
* mWrapKeyContext
;
133 PK11SymKey
* mWrapKey
;
140 , mWrapKeyContext(nullptr)
143 // Initialize NSS, database functions are not needed
144 NSS_NoDB_Init(nullptr);
150 PK11_DestroyContext(mContext
, PR_TRUE
);
152 SECITEM_FreeItem(mSecParam
, PR_TRUE
);
154 PK11_FreeSymKey(mSymKey
);
156 PK11_DestroyContext(mWrapKeyContext
, PR_TRUE
);
158 PK11_FreeSymKey(mWrapKey
);
160 PK11_FreeSlot(mSlot
);
163 PK11SymKey
* ImportSymKey(CK_MECHANISM_TYPE mechanism
, CK_ATTRIBUTE_TYPE operation
, SECItem
* key
)
165 mSymKey
= PK11_ImportSymKey(mSlot
, mechanism
, PK11_OriginUnwrap
, operation
, key
, nullptr);
166 if (!mSymKey
) //rhbz#1614419 maybe failed due to FIPS, use rhbz#1461450 style workaround
169 * Without FIPS it would be possible to just use
170 * mSymKey = PK11_ImportSymKey( mSlot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr );
171 * with FIPS NSS Level 2 certification has to be "workarounded" (so it becomes Level 1) by using
173 * 1. Generate wrap key
174 * 2. Encrypt authkey with wrap key
175 * 3. Unwrap encrypted authkey using wrap key
179 * Generate wrapping key
181 CK_MECHANISM_TYPE wrap_mechanism
= PK11_GetBestWrapMechanism(mSlot
);
182 int wrap_key_len
= PK11_GetBestKeyLength(mSlot
, wrap_mechanism
);
183 mWrapKey
= PK11_KeyGen(mSlot
, wrap_mechanism
, nullptr, wrap_key_len
, nullptr);
185 throw css::uno::RuntimeException("PK11_KeyGen SymKey failure", css::uno::Reference
<css::uno::XInterface
>());
188 * Encrypt authkey with wrapping key
192 * Initialization of IV is not needed because PK11_GetBestWrapMechanism should return ECB mode
194 SECItem tmp_sec_item
;
195 memset(&tmp_sec_item
, 0, sizeof(tmp_sec_item
));
196 mWrapKeyContext
= PK11_CreateContextBySymKey(wrap_mechanism
, CKA_ENCRYPT
, mWrapKey
, &tmp_sec_item
);
197 if (!mWrapKeyContext
)
198 throw css::uno::RuntimeException("PK11_CreateContextBySymKey failure", css::uno::Reference
<css::uno::XInterface
>());
200 unsigned char wrapped_key_data
[MAX_WRAPPED_KEY_LEN
];
201 int wrapped_key_len
= sizeof(wrapped_key_data
);
203 if (PK11_CipherOp(mWrapKeyContext
, wrapped_key_data
, &wrapped_key_len
,
204 sizeof(wrapped_key_data
), key
->data
, key
->len
) != SECSuccess
)
206 throw css::uno::RuntimeException("PK11_CipherOp failure", css::uno::Reference
<css::uno::XInterface
>());
209 if (PK11_Finalize(mWrapKeyContext
) != SECSuccess
)
210 throw css::uno::RuntimeException("PK11_Finalize failure", css::uno::Reference
<css::uno::XInterface
>());
213 * Finally unwrap sym key
216 memset(&tmp_sec_item
, 0, sizeof(tmp_sec_item
));
217 wrapped_key
.data
= wrapped_key_data
;
218 wrapped_key
.len
= wrapped_key_len
;
220 mSymKey
= PK11_UnwrapSymKey(mWrapKey
, wrap_mechanism
, &tmp_sec_item
, &wrapped_key
,
221 mechanism
, operation
, key
->len
);
226 void setupCryptoContext(std::vector
<sal_uInt8
>& key
, std::vector
<sal_uInt8
>& iv
, Crypto::CryptoType type
, CK_ATTRIBUTE_TYPE operation
)
228 CK_MECHANISM_TYPE mechanism
= static_cast<CK_ULONG
>(-1);
231 ivItem
.type
= siBuffer
;
233 ivItem
.data
= nullptr;
235 ivItem
.data
= iv
.data();
236 ivItem
.len
= iv
.size();
238 SECItem
* pIvItem
= nullptr;
242 case Crypto::CryptoType::AES_128_ECB
:
243 mechanism
= CKM_AES_ECB
;
245 case Crypto::CryptoType::AES_128_CBC
:
246 mechanism
= CKM_AES_CBC
;
249 case Crypto::CryptoType::AES_256_CBC
:
250 mechanism
= CKM_AES_CBC
;
257 mSlot
= PK11_GetBestSlot(mechanism
, nullptr);
260 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference
<css::uno::XInterface
>());
263 keyItem
.type
= siBuffer
;
264 keyItem
.data
= key
.data();
265 keyItem
.len
= key
.size();
267 mSymKey
= ImportSymKey(mechanism
, CKA_ENCRYPT
, &keyItem
);
269 throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference
<css::uno::XInterface
>());
271 mSecParam
= PK11_ParamFromIV(mechanism
, pIvItem
);
272 mContext
= PK11_CreateContextBySymKey(mechanism
, operation
, mSymKey
, mSecParam
);
275 void setupCryptoHashContext(std::vector
<sal_uInt8
>& rKey
, CryptoHashType eType
)
277 CK_MECHANISM_TYPE aMechanism
= static_cast<CK_ULONG
>(-1);
281 case CryptoHashType::SHA1
:
282 aMechanism
= CKM_SHA_1_HMAC
;
284 case CryptoHashType::SHA256
:
285 aMechanism
= CKM_SHA256_HMAC
;
287 case CryptoHashType::SHA512
:
288 aMechanism
= CKM_SHA512_HMAC
;
292 mSlot
= PK11_GetBestSlot(aMechanism
, nullptr);
295 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference
<css::uno::XInterface
>());
298 aKeyItem
.data
= rKey
.data();
299 aKeyItem
.len
= rKey
.size();
301 mSymKey
= ImportSymKey(aMechanism
, CKA_SIGN
, &aKeyItem
);
303 throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference
<css::uno::XInterface
>());
306 param
.data
= nullptr;
308 mContext
= PK11_CreateContextBySymKey(aMechanism
, CKA_SIGN
, mSymKey
, ¶m
);
317 : mpImpl(std::make_unique
<CryptoImpl
>())
327 Decrypt::Decrypt(std::vector
<sal_uInt8
>& key
, std::vector
<sal_uInt8
>& iv
, CryptoType type
)
330 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
337 mpImpl
->setupDecryptContext(key
, iv
, type
);
341 mpImpl
->setupCryptoContext(key
, iv
, type
, CKA_DECRYPT
);
342 #endif // USE_TLS_NSS
345 sal_uInt32
Decrypt::update(std::vector
<sal_uInt8
>& output
, std::vector
<sal_uInt8
>& input
, sal_uInt32 inputLength
)
347 int outputLength
= 0;
349 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
350 sal_uInt32 actualInputLength
= inputLength
== 0 || inputLength
> input
.size() ? input
.size() : inputLength
;
358 (void)EVP_DecryptUpdate(mpImpl
->mpContext
.get(), output
.data(), &outputLength
, input
.data(), actualInputLength
);
359 #endif // USE_TLS_OPENSSL
362 if (!mpImpl
->mContext
)
364 (void)PK11_CipherOp(mpImpl
->mContext
, output
.data(), &outputLength
, actualInputLength
, input
.data(), actualInputLength
);
365 #endif // USE_TLS_NSS
367 return static_cast<sal_uInt32
>(outputLength
);
370 sal_uInt32
Decrypt::aes128ecb(std::vector
<sal_uInt8
>& output
, std::vector
<sal_uInt8
>& input
, std::vector
<sal_uInt8
>& key
)
372 sal_uInt32 outputLength
= 0;
373 std::vector
<sal_uInt8
> iv
;
374 Decrypt
crypto(key
, iv
, Crypto::AES_128_ECB
);
375 outputLength
= crypto
.update(output
, input
);
381 Encrypt::Encrypt(std::vector
<sal_uInt8
>& key
, std::vector
<sal_uInt8
>& iv
, CryptoType type
)
384 #if USE_TLS_OPENSSL + USE_TLS_NSS == 0
391 mpImpl
->setupEncryptContext(key
, iv
, type
);
393 mpImpl
->setupCryptoContext(key
, iv
, type
, CKA_ENCRYPT
);
394 #endif // USE_TLS_NSS
397 sal_uInt32
Encrypt::update(std::vector
<sal_uInt8
>& output
, std::vector
<sal_uInt8
>& input
, sal_uInt32 inputLength
)
399 int outputLength
= 0;
401 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
402 sal_uInt32 actualInputLength
= inputLength
== 0 || inputLength
> input
.size() ? input
.size() : inputLength
;
410 (void)EVP_EncryptUpdate(mpImpl
->mpContext
.get(), output
.data(), &outputLength
, input
.data(), actualInputLength
);
411 #endif // USE_TLS_OPENSSL
414 (void)PK11_CipherOp(mpImpl
->mContext
, output
.data(), &outputLength
, actualInputLength
, input
.data(), actualInputLength
);
415 #endif // USE_TLS_NSS
417 return static_cast<sal_uInt32
>(outputLength
);
425 sal_Int32
getSizeForHashType(CryptoHashType eType
)
429 case CryptoHashType::SHA1
: return 20;
430 case CryptoHashType::SHA256
: return 32;
431 case CryptoHashType::SHA512
: return 64;
436 } // end anonymous namespace
438 CryptoHash::CryptoHash(std::vector
<sal_uInt8
>& rKey
, CryptoHashType eType
)
440 , mnHashSize(getSizeForHashType(eType
))
443 mpImpl
->setupCryptoHashContext(rKey
, eType
);
445 mpImpl
->setupCryptoHashContext(rKey
, eType
);
446 PK11_DigestBegin(mpImpl
->mContext
);
452 bool CryptoHash::update(std::vector
<sal_uInt8
>& rInput
, sal_uInt32 nInputLength
)
454 #if USE_TLS_OPENSSL + USE_TLS_NSS > 0
455 sal_uInt32 nActualInputLength
= (nInputLength
== 0 || nInputLength
> rInput
.size()) ? rInput
.size() : nInputLength
;
462 return HMAC_Update(mpImpl
->mpHmacContext
.get(), rInput
.data(), nActualInputLength
) != 0;
464 return PK11_DigestOp(mpImpl
->mContext
, rInput
.data(), nActualInputLength
) == SECSuccess
;
470 std::vector
<sal_uInt8
> CryptoHash::finalize()
472 std::vector
<sal_uInt8
> aHash(mnHashSize
, 0);
473 unsigned int nSizeWritten
;
475 (void) HMAC_Final(mpImpl
->mpHmacContext
.get(), aHash
.data(), &nSizeWritten
);
477 PK11_DigestFinal(mpImpl
->mContext
, aHash
.data(), &nSizeWritten
, aHash
.size());
487 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */