LibreOffice Module oox (master) 1
CryptTools.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 */
10
12#include <com/sun/star/uno/RuntimeException.hpp>
13
14#include <config_oox.h>
15
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
21
22#if USE_TLS_NSS
23#include <nss.h>
24#include <nspr.h>
25#include <pk11pub.h>
26#endif // USE_TLS_NSS
27
28namespace oox::crypto {
29
30#if USE_TLS_OPENSSL
31
32#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
33
34static HMAC_CTX *HMAC_CTX_new(void)
35{
36 HMAC_CTX *pContext = new HMAC_CTX;
37 HMAC_CTX_init(pContext);
38 return pContext;
39}
40
41static void HMAC_CTX_free(HMAC_CTX *pContext)
42{
43 HMAC_CTX_cleanup(pContext);
44 delete pContext;
45}
46#endif
47
48namespace
49{
50 struct cipher_delete
51 {
52 void operator()(EVP_CIPHER_CTX* p) { EVP_CIPHER_CTX_free(p); }
53 };
54
55 struct hmac_delete
56 {
57 void operator()(HMAC_CTX* p) { HMAC_CTX_free(p); }
58 };
59}
60
61struct CryptoImpl
62{
63 std::unique_ptr<EVP_CIPHER_CTX, cipher_delete> mpContext;
64 std::unique_ptr<HMAC_CTX, hmac_delete> mpHmacContext;
65
66 CryptoImpl() = default;
67
68 void setupEncryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType eType)
69 {
70 mpContext.reset(EVP_CIPHER_CTX_new());
71 EVP_CIPHER_CTX_init(mpContext.get());
72
73 const EVP_CIPHER* cipher = getCipher(eType);
74 if (cipher == nullptr)
75 return;
76
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);
82 }
83
84 void setupDecryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType eType)
85 {
86 mpContext.reset(EVP_CIPHER_CTX_new());
87 EVP_CIPHER_CTX_init(mpContext.get());
88
89 const EVP_CIPHER* pCipher = getCipher(eType);
90 if (pCipher == nullptr)
91 return;
92
93 const size_t nMinKeySize = EVP_CIPHER_key_length(pCipher);
94 if (key.size() < nMinKeySize)
95 key.resize(nMinKeySize, 0);
96
97 if (iv.empty())
98 EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), nullptr);
99 else
100 {
101 const size_t nMinIVSize = EVP_CIPHER_iv_length(pCipher);
102 if (iv.size() < nMinIVSize)
103 iv.resize(nMinIVSize, 0);
104
105 EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), iv.data());
106 }
107 EVP_CIPHER_CTX_set_padding(mpContext.get(), 0);
108 }
109
110 void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
111 {
112 mpHmacContext.reset(HMAC_CTX_new());
113 const EVP_MD* aEvpMd = nullptr;
114 switch (eType)
115 {
117 aEvpMd = EVP_sha1(); break;
119 aEvpMd = EVP_sha256(); break;
121 aEvpMd = EVP_sha512(); break;
122 }
123 HMAC_Init_ex(mpHmacContext.get(), rKey.data(), rKey.size(), aEvpMd, nullptr);
124 }
125
126 ~CryptoImpl()
127 {
128 if (mpContext)
129 EVP_CIPHER_CTX_cleanup(mpContext.get());
130 }
131
132 static const EVP_CIPHER* getCipher(Crypto::CryptoType type)
133 {
134 switch(type)
135 {
136 case Crypto::CryptoType::AES_128_ECB:
137 return EVP_aes_128_ecb();
138 case Crypto::CryptoType::AES_128_CBC:
139 return EVP_aes_128_cbc();
140 case Crypto::CryptoType::AES_256_CBC:
141 return EVP_aes_256_cbc();
142 default:
143 break;
144 }
145 return nullptr;
146 }
147};
148
149#elif USE_TLS_NSS
150
151#define MAX_WRAPPED_KEY_LEN 128
152
153struct CryptoImpl
154{
155 PK11SlotInfo* mSlot;
156 PK11Context* mContext;
157 SECItem* mSecParam;
158 PK11SymKey* mSymKey;
159 PK11Context* mWrapKeyContext;
160 PK11SymKey* mWrapKey;
161
162 CryptoImpl()
163 : mSlot(nullptr)
164 , mContext(nullptr)
165 , mSecParam(nullptr)
166 , mSymKey(nullptr)
167 , mWrapKeyContext(nullptr)
168 , mWrapKey(nullptr)
169 {
170 // Initialize NSS, database functions are not needed
171 if (!NSS_IsInitialized())
172 {
173 auto const e = NSS_NoDB_Init(nullptr);
174 if (e != SECSuccess)
175 {
176 PRErrorCode error = PR_GetError();
177 const char* errorText = PR_ErrorToName(error);
178 throw css::uno::RuntimeException("NSS_NoDB_Init failed with " + OUString(errorText, strlen(errorText), RTL_TEXTENCODING_UTF8) + " (" + OUString::number(static_cast<int>(error)) + ")");
179 }
180 }
181 }
182
183 ~CryptoImpl()
184 {
185 if (mContext)
186 PK11_DestroyContext(mContext, PR_TRUE);
187 if (mSecParam)
188 SECITEM_FreeItem(mSecParam, PR_TRUE);
189 if (mSymKey)
190 PK11_FreeSymKey(mSymKey);
191 if (mWrapKeyContext)
192 PK11_DestroyContext(mWrapKeyContext, PR_TRUE);
193 if (mWrapKey)
194 PK11_FreeSymKey(mWrapKey);
195 if (mSlot)
196 PK11_FreeSlot(mSlot);
197 }
198
199 PK11SymKey* ImportSymKey(CK_MECHANISM_TYPE mechanism, CK_ATTRIBUTE_TYPE operation, SECItem* key)
200 {
201 mSymKey = PK11_ImportSymKey(mSlot, mechanism, PK11_OriginUnwrap, operation, key, nullptr);
202 if (!mSymKey) //rhbz#1614419 maybe failed due to FIPS, use rhbz#1461450 style workaround
203 {
204 /*
205 * Without FIPS it would be possible to just use
206 * mSymKey = PK11_ImportSymKey( mSlot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr );
207 * with FIPS NSS Level 2 certification has to be "workarounded" (so it becomes Level 1) by using
208 * following method:
209 * 1. Generate wrap key
210 * 2. Encrypt authkey with wrap key
211 * 3. Unwrap encrypted authkey using wrap key
212 */
213
214 /*
215 * Generate wrapping key
216 */
217 CK_MECHANISM_TYPE wrap_mechanism = PK11_GetBestWrapMechanism(mSlot);
218 int wrap_key_len = PK11_GetBestKeyLength(mSlot, wrap_mechanism);
219 mWrapKey = PK11_KeyGen(mSlot, wrap_mechanism, nullptr, wrap_key_len, nullptr);
220 if (!mWrapKey)
221 throw css::uno::RuntimeException("PK11_KeyGen SymKey failure", css::uno::Reference<css::uno::XInterface>());
222
223 /*
224 * Encrypt authkey with wrapping key
225 */
226
227 /*
228 * Initialization of IV is not needed because PK11_GetBestWrapMechanism should return ECB mode
229 */
230 SECItem tmp_sec_item = {};
231 mWrapKeyContext = PK11_CreateContextBySymKey(wrap_mechanism, CKA_ENCRYPT, mWrapKey, &tmp_sec_item);
232 if (!mWrapKeyContext)
233 throw css::uno::RuntimeException("PK11_CreateContextBySymKey failure", css::uno::Reference<css::uno::XInterface>());
234
235 unsigned char wrapped_key_data[MAX_WRAPPED_KEY_LEN];
236 int wrapped_key_len = sizeof(wrapped_key_data);
237
238 if (PK11_CipherOp(mWrapKeyContext, wrapped_key_data, &wrapped_key_len,
239 sizeof(wrapped_key_data), key->data, key->len) != SECSuccess)
240 {
241 throw css::uno::RuntimeException("PK11_CipherOp failure", css::uno::Reference<css::uno::XInterface>());
242 }
243
244 if (PK11_Finalize(mWrapKeyContext) != SECSuccess)
245 throw css::uno::RuntimeException("PK11_Finalize failure", css::uno::Reference<css::uno::XInterface>());
246
247 /*
248 * Finally unwrap sym key
249 */
250 SECItem wrapped_key = {};
251 wrapped_key.data = wrapped_key_data;
252 wrapped_key.len = wrapped_key_len;
253
254 mSymKey = PK11_UnwrapSymKey(mWrapKey, wrap_mechanism, &tmp_sec_item, &wrapped_key,
255 mechanism, operation, key->len);
256 }
257 return mSymKey;
258 }
259
260 void setupCryptoContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, Crypto::CryptoType type, CK_ATTRIBUTE_TYPE operation)
261 {
262 CK_MECHANISM_TYPE mechanism = static_cast<CK_ULONG>(-1);
263
264 SECItem ivItem;
265 ivItem.type = siBuffer;
266 if(iv.empty())
267 ivItem.data = nullptr;
268 else
269 ivItem.data = iv.data();
270 ivItem.len = iv.size();
271
272 SECItem* pIvItem = nullptr;
273
274 switch(type)
275 {
276 case Crypto::CryptoType::AES_128_ECB:
277 mechanism = CKM_AES_ECB;
278 break;
279 case Crypto::CryptoType::AES_128_CBC:
280 mechanism = CKM_AES_CBC;
281 pIvItem = &ivItem;
282 break;
283 case Crypto::CryptoType::AES_256_CBC:
284 mechanism = CKM_AES_CBC;
285 pIvItem = &ivItem;
286 break;
287 default:
288 break;
289 }
290
291 mSlot = PK11_GetBestSlot(mechanism, nullptr);
292
293 if (!mSlot)
294 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference<css::uno::XInterface>());
295
296 SECItem keyItem;
297 keyItem.type = siBuffer;
298 keyItem.data = key.data();
299 keyItem.len = key.size();
300
301 mSymKey = ImportSymKey(mechanism, CKA_ENCRYPT, &keyItem);
302 if (!mSymKey)
303 throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference<css::uno::XInterface>());
304
305 mSecParam = PK11_ParamFromIV(mechanism, pIvItem);
306 mContext = PK11_CreateContextBySymKey(mechanism, operation, mSymKey, mSecParam);
307 }
308
309 void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
310 {
311 CK_MECHANISM_TYPE aMechanism = static_cast<CK_ULONG>(-1);
312
313 switch(eType)
314 {
316 aMechanism = CKM_SHA_1_HMAC;
317 break;
319 aMechanism = CKM_SHA256_HMAC;
320 break;
322 aMechanism = CKM_SHA512_HMAC;
323 break;
324 }
325
326 mSlot = PK11_GetBestSlot(aMechanism, nullptr);
327
328 if (!mSlot)
329 throw css::uno::RuntimeException("NSS Slot failure", css::uno::Reference<css::uno::XInterface>());
330
331 SECItem aKeyItem;
332 aKeyItem.data = rKey.data();
333 aKeyItem.len = rKey.size();
334
335 mSymKey = ImportSymKey(aMechanism, CKA_SIGN, &aKeyItem);
336 if (!mSymKey)
337 throw css::uno::RuntimeException("NSS SymKey failure", css::uno::Reference<css::uno::XInterface>());
338
339 SECItem param;
340 param.data = nullptr;
341 param.len = 0;
342 mContext = PK11_CreateContextBySymKey(aMechanism, CKA_SIGN, mSymKey, &param);
343 }
344};
345#else
347{};
348#endif
349
351 : mpImpl(std::make_unique<CryptoImpl>())
352{
353}
354
356{
357}
358
359// DECRYPT
360
361Decrypt::Decrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type)
362{
363#if USE_TLS_OPENSSL + USE_TLS_NSS == 0
364 (void)key;
365 (void)iv;
366 (void)type;
367#endif
368
369#if USE_TLS_OPENSSL
370 mpImpl->setupDecryptContext(key, iv, type);
371#endif
372
373#if USE_TLS_NSS
374 mpImpl->setupCryptoContext(key, iv, type, CKA_DECRYPT);
375#endif // USE_TLS_NSS
376}
377
378sal_uInt32 Decrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, sal_uInt32 inputLength)
379{
380 int outputLength = 0;
381
382#if USE_TLS_OPENSSL + USE_TLS_NSS > 0
383 sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength;
384#else
385 (void)output;
386 (void)input;
387 (void)inputLength;
388#endif
389
390#if USE_TLS_OPENSSL
391 (void)EVP_DecryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), actualInputLength);
392#endif // USE_TLS_OPENSSL
393
394#if USE_TLS_NSS
395 if (!mpImpl->mContext)
396 return 0;
397 (void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength);
398#endif // USE_TLS_NSS
399
400 return static_cast<sal_uInt32>(outputLength);
401}
402
403sal_uInt32 Decrypt::aes128ecb(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, std::vector<sal_uInt8>& key)
404{
405 sal_uInt32 outputLength = 0;
406 std::vector<sal_uInt8> iv;
407 Decrypt crypto(key, iv, Crypto::AES_128_ECB);
408 outputLength = crypto.update(output, input);
409 return outputLength;
410}
411
412// ENCRYPT
413
414Encrypt::Encrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type)
415{
416#if USE_TLS_OPENSSL + USE_TLS_NSS == 0
417 (void)key;
418 (void)iv;
419 (void)type;
420#endif
421
422#if USE_TLS_OPENSSL
423 mpImpl->setupEncryptContext(key, iv, type);
424#elif USE_TLS_NSS
425 mpImpl->setupCryptoContext(key, iv, type, CKA_ENCRYPT);
426#endif // USE_TLS_NSS
427}
428
429sal_uInt32 Encrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, sal_uInt32 inputLength)
430{
431 int outputLength = 0;
432
433#if USE_TLS_OPENSSL + USE_TLS_NSS > 0
434 sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength;
435#else
436 (void)output;
437 (void)input;
438 (void)inputLength;
439#endif
440
441#if USE_TLS_OPENSSL
442 (void)EVP_EncryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), actualInputLength);
443#endif // USE_TLS_OPENSSL
444
445#if USE_TLS_NSS
446 (void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, input.data(), actualInputLength);
447#endif // USE_TLS_NSS
448
449 return static_cast<sal_uInt32>(outputLength);
450}
451
452// CryptoHash - HMAC
453
454namespace
455{
456
457sal_Int32 getSizeForHashType(CryptoHashType eType)
458{
459 switch (eType)
460 {
461 case CryptoHashType::SHA1: return 20;
462 case CryptoHashType::SHA256: return 32;
463 case CryptoHashType::SHA512: return 64;
464 }
465 return 0;
466}
467
468} // end anonymous namespace
469
470CryptoHash::CryptoHash(std::vector<sal_uInt8>& rKey, CryptoHashType eType)
471 : mnHashSize(getSizeForHashType(eType))
472{
473#if USE_TLS_OPENSSL
474 mpImpl->setupCryptoHashContext(rKey, eType);
475#elif USE_TLS_NSS
476 mpImpl->setupCryptoHashContext(rKey, eType);
477 PK11_DigestBegin(mpImpl->mContext);
478#else
479 (void)rKey;
480#endif
481}
482
483bool CryptoHash::update(std::vector<sal_uInt8>& rInput, sal_uInt32 nInputLength)
484{
485#if USE_TLS_OPENSSL + USE_TLS_NSS > 0
486 sal_uInt32 nActualInputLength = (nInputLength == 0 || nInputLength > rInput.size()) ? rInput.size() : nInputLength;
487#else
488 (void)rInput;
489 (void)nInputLength;
490#endif
491
492#if USE_TLS_OPENSSL
493 return HMAC_Update(mpImpl->mpHmacContext.get(), rInput.data(), nActualInputLength) != 0;
494#elif USE_TLS_NSS
495 return PK11_DigestOp(mpImpl->mContext, rInput.data(), nActualInputLength) == SECSuccess;
496#else
497 return false; // ???
498#endif
499}
500
501std::vector<sal_uInt8> CryptoHash::finalize()
502{
503 std::vector<sal_uInt8> aHash(mnHashSize, 0);
504 unsigned int nSizeWritten;
505#if USE_TLS_OPENSSL
506 (void) HMAC_Final(mpImpl->mpHmacContext.get(), aHash.data(), &nSizeWritten);
507#elif USE_TLS_NSS
508 PK11_DigestFinal(mpImpl->mContext, aHash.data(), &nSizeWritten, aHash.size());
509#endif
510 (void)nSizeWritten;
511
512 return aHash;
513}
514
515} // namespace oox::crypto
516
517/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::vector< sal_uInt8 > finalize()
Definition: CryptTools.cxx:501
bool update(std::vector< sal_uInt8 > &rInput, sal_uInt32 nInputLength=0)
Definition: CryptTools.cxx:483
CryptoHash(std::vector< sal_uInt8 > &rKey, CryptoHashType eType)
Definition: CryptTools.cxx:470
std::unique_ptr< CryptoImpl > mpImpl
Definition: CryptTools.hxx:68
Decrypt(std::vector< sal_uInt8 > &key, std::vector< sal_uInt8 > &iv, CryptoType type)
Definition: CryptTools.cxx:361
sal_uInt32 update(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, sal_uInt32 inputLength=0)
Definition: CryptTools.cxx:378
static sal_uInt32 aes128ecb(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, std::vector< sal_uInt8 > &key)
Definition: CryptTools.cxx:403
sal_uInt32 update(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, sal_uInt32 inputLength=0)
Definition: CryptTools.cxx:429
Encrypt(std::vector< sal_uInt8 > &key, std::vector< sal_uInt8 > &iv, CryptoType type)
Definition: CryptTools.cxx:414
DocumentType eType
ParserContextSharedPtr mpContext
ResultType type