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