comphelper/Library_comphelper.mk | 5 comphelper/source/crypto/Crypto.cxx | 482 ---------------------------- comphelper/source/crypto/Crypto_NSS.cxx | 295 +++++++++++++++++ comphelper/source/crypto/Crypto_None.cxx | 67 +++ comphelper/source/crypto/Crypto_OpenSSL.cxx | 208 ++++++++++++ include/comphelper/crypto/Crypto.hxx | 27 + 6 files changed, 604 insertions(+), 480 deletions(-)
New commits: commit 676524f200b114e5bb097812321c86516cbbdda0 Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Tue Nov 26 15:22:13 2024 +0900 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Fri Nov 29 13:28:12 2024 +0100 comphelper: put each Crypto impl. in own file - minimize ifdefs This puts NSS and OpenSSL implementations into own classes and adds an interface to make this possible. The implementation is now determined in Library_comphelper. Change-Id: I4918f19bca0adfd2f9180fb418d1bd57bb83982d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/177308 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> diff --git a/comphelper/Library_comphelper.mk b/comphelper/Library_comphelper.mk index c8eabde5065c..f9bd120fd960 100644 --- a/comphelper/Library_comphelper.mk +++ b/comphelper/Library_comphelper.mk @@ -70,12 +70,15 @@ $(eval $(call gb_Library_add_exception_objects,comphelper,\ comphelper/source/container/NamedPropertyValuesContainer \ comphelper/source/container/container \ comphelper/source/container/containermultiplexer \ - comphelper/source/container/interfacecontainer2 \ + comphelper/source/container/interfacecontainer2 \ comphelper/source/container/embeddedobjectcontainer \ comphelper/source/container/enumerablemap \ comphelper/source/container/enumhelper \ comphelper/source/container/namecontainer \ comphelper/source/crypto/Crypto \ + $(if $(filter NSS,$(TLS)), comphelper/source/crypto/Crypto_NSS) \ + $(if $(filter OPENSSL,$(TLS)), comphelper/source/crypto/Crypto_OpenSSL) \ + $(if $(filter NSS OPENSSL,$(TLS)),,comphelper/source/crypto/Crypto_None) \ comphelper/source/eventattachermgr/eventattachermgr \ comphelper/source/misc/accessiblecomponenthelper \ comphelper/source/misc/accessibleeventnotifier \ diff --git a/comphelper/source/crypto/Crypto.cxx b/comphelper/source/crypto/Crypto.cxx index a65718ae20f1..faf1da7307f9 100644 --- a/comphelper/source/crypto/Crypto.cxx +++ b/comphelper/source/crypto/Crypto.cxx @@ -5,447 +5,34 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * */ #include <comphelper/crypto/Crypto.hxx> #include <com/sun/star/uno/RuntimeException.hpp> #include <sal/types.h> - #include <config_oox.h> -#if USE_TLS_OPENSSL -#include <openssl/evp.h> -#include <openssl/sha.h> -#include <openssl/hmac.h> -#endif // USE_TLS_OPENSSL - -#if USE_TLS_NSS -#include <nss.h> -#include <nspr.h> -#include <pk11pub.h> -#endif // USE_TLS_NSS - namespace comphelper { -#if USE_TLS_OPENSSL - -#if (OPENSSL_VERSION_NUMBER < 0x10100000L) - -static HMAC_CTX* HMAC_CTX_new(void) -{ - HMAC_CTX* pContext = new HMAC_CTX; - HMAC_CTX_init(pContext); - return pContext; -} - -static void HMAC_CTX_free(HMAC_CTX* pContext) -{ - HMAC_CTX_cleanup(pContext); - delete pContext; -} -#endif - -namespace -{ -struct cipher_delete -{ - void operator()(EVP_CIPHER_CTX* p) { EVP_CIPHER_CTX_free(p); } -}; - -struct hmac_delete -{ - SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_CTX_free' is deprecated - void - operator()(HMAC_CTX* p) - { - HMAC_CTX_free(p); - } - SAL_WNODEPRECATED_DECLARATIONS_POP -}; -} - -struct CryptoImpl -{ - std::unique_ptr<EVP_CIPHER_CTX, cipher_delete> mpContext; - std::unique_ptr<HMAC_CTX, hmac_delete> mpHmacContext; - - CryptoImpl() = default; - - void setupEncryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, - CryptoType eType) - { - mpContext.reset(EVP_CIPHER_CTX_new()); - EVP_CIPHER_CTX_init(mpContext.get()); - - const EVP_CIPHER* cipher = getCipher(eType); - if (cipher == nullptr) - return; - - if (iv.empty()) - EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), nullptr); - else - EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), iv.data()); - EVP_CIPHER_CTX_set_padding(mpContext.get(), 0); - } - - void setupDecryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, - CryptoType eType) - { - mpContext.reset(EVP_CIPHER_CTX_new()); - EVP_CIPHER_CTX_init(mpContext.get()); - - const EVP_CIPHER* pCipher = getCipher(eType); - if (pCipher == nullptr) - return; - - const size_t nMinKeySize = EVP_CIPHER_key_length(pCipher); - if (key.size() < nMinKeySize) - key.resize(nMinKeySize, 0); - - if (iv.empty()) - EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), nullptr); - else - { - const size_t nMinIVSize = EVP_CIPHER_iv_length(pCipher); - if (iv.size() < nMinIVSize) - iv.resize(nMinIVSize, 0); - - EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), iv.data()); - } - EVP_CIPHER_CTX_set_padding(mpContext.get(), 0); - } - - void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType) - { - SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_CTX_new' is deprecated - mpHmacContext.reset(HMAC_CTX_new()); - SAL_WNODEPRECATED_DECLARATIONS_POP - const EVP_MD* aEvpMd = nullptr; - switch (eType) - { - case CryptoHashType::SHA1: - aEvpMd = EVP_sha1(); - break; - case CryptoHashType::SHA256: - aEvpMd = EVP_sha256(); - break; - case CryptoHashType::SHA384: - aEvpMd = EVP_sha384(); - break; - case CryptoHashType::SHA512: - aEvpMd = EVP_sha512(); - break; - } - SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_Init_ex' is deprecated - HMAC_Init_ex(mpHmacContext.get(), rKey.data(), rKey.size(), aEvpMd, nullptr); - SAL_WNODEPRECATED_DECLARATIONS_POP - } - - ~CryptoImpl() - { - if (mpContext) - EVP_CIPHER_CTX_cleanup(mpContext.get()); - } - - static const EVP_CIPHER* getCipher(CryptoType type) - { - switch (type) - { - case CryptoType::AES_128_ECB: - return EVP_aes_128_ecb(); - case CryptoType::AES_256_ECB: - return EVP_aes_256_ecb(); - case CryptoType::AES_128_CBC: - return EVP_aes_128_cbc(); - case CryptoType::AES_256_CBC: - return EVP_aes_256_cbc(); - default: - break; - } - return nullptr; - } -}; - -#elif USE_TLS_NSS - -#define MAX_WRAPPED_KEY_LEN 128 - -struct CryptoImpl -{ - PK11SlotInfo* mSlot; - PK11Context* mContext; - SECItem* mSecParam; - PK11SymKey* mSymKey; - PK11Context* mWrapKeyContext; - PK11SymKey* mWrapKey; - - CryptoImpl() - : mSlot(nullptr) - , mContext(nullptr) - , mSecParam(nullptr) - , mSymKey(nullptr) - , mWrapKeyContext(nullptr) - , mWrapKey(nullptr) - { - // Initialize NSS, database functions are not needed - if (!NSS_IsInitialized()) - { - auto const e = NSS_NoDB_Init(nullptr); - if (e != SECSuccess) - { - PRErrorCode error = PR_GetError(); - const char* errorText = PR_ErrorToName(error); - throw css::uno::RuntimeException( - "NSS_NoDB_Init failed with " - + OUString(errorText, strlen(errorText), RTL_TEXTENCODING_UTF8) + " (" - + OUString::number(static_cast<int>(error)) + ")"); - } - } - } - - ~CryptoImpl() - { - if (mContext) - PK11_DestroyContext(mContext, PR_TRUE); - if (mSecParam) - SECITEM_FreeItem(mSecParam, PR_TRUE); - if (mSymKey) - PK11_FreeSymKey(mSymKey); - if (mWrapKeyContext) - PK11_DestroyContext(mWrapKeyContext, PR_TRUE); - if (mWrapKey) - PK11_FreeSymKey(mWrapKey); - if (mSlot) - PK11_FreeSlot(mSlot); - } - - PK11SymKey* ImportSymKey(CK_MECHANISM_TYPE mechanism, CK_ATTRIBUTE_TYPE operation, SECItem* key) - { - mSymKey = PK11_ImportSymKey(mSlot, mechanism, PK11_OriginUnwrap, operation, key, nullptr); - if (!mSymKey) //rhbz#1614419 maybe failed due to FIPS, use rhbz#1461450 style workaround - { - /* - * Without FIPS it would be possible to just use - * mSymKey = PK11_ImportSymKey( mSlot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr ); - * with FIPS NSS Level 2 certification has to be "workarounded" (so it becomes Level 1) by using - * following method: - * 1. Generate wrap key - * 2. Encrypt authkey with wrap key - * 3. Unwrap encrypted authkey using wrap key - */ - - /* - * Generate wrapping key - */ - CK_MECHANISM_TYPE wrap_mechanism = PK11_GetBestWrapMechanism(mSlot); - int wrap_key_len = PK11_GetBestKeyLength(mSlot, wrap_mechanism); - mWrapKey = PK11_KeyGen(mSlot, wrap_mechanism, nullptr, wrap_key_len, nullptr); - if (!mWrapKey) - throw css::uno::RuntimeException("PK11_KeyGen SymKey failure", - css::uno::Reference<css::uno::XInterface>()); - - /* - * Encrypt authkey with wrapping key - */ - - /* - * Initialization of IV is not needed because PK11_GetBestWrapMechanism should return ECB mode - */ - SECItem tmp_sec_item = {}; - mWrapKeyContext - = PK11_CreateContextBySymKey(wrap_mechanism, CKA_ENCRYPT, mWrapKey, &tmp_sec_item); - if (!mWrapKeyContext) - throw css::uno::RuntimeException("PK11_CreateContextBySymKey failure", - css::uno::Reference<css::uno::XInterface>()); - - unsigned char wrapped_key_data[MAX_WRAPPED_KEY_LEN]; - int wrapped_key_len = sizeof(wrapped_key_data); - - if (PK11_CipherOp(mWrapKeyContext, wrapped_key_data, &wrapped_key_len, - sizeof(wrapped_key_data), key->data, key->len) - != SECSuccess) - { - throw css::uno::RuntimeException("PK11_CipherOp failure", - css::uno::Reference<css::uno::XInterface>()); - } - - if (PK11_Finalize(mWrapKeyContext) != SECSuccess) - throw css::uno::RuntimeException("PK11_Finalize failure", - css::uno::Reference<css::uno::XInterface>()); - - /* - * Finally unwrap sym key - */ - SECItem wrapped_key = {}; - wrapped_key.data = wrapped_key_data; - wrapped_key.len = wrapped_key_len; - - mSymKey = PK11_UnwrapSymKey(mWrapKey, wrap_mechanism, &tmp_sec_item, &wrapped_key, - mechanism, operation, key->len); - } - return mSymKey; - } - - void setupEncryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, - CryptoType type) - { - setupCryptoContext(key, iv, type, CKA_ENCRYPT); - } - - void setupDecryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, - CryptoType type) - { - setupCryptoContext(key, iv, type, CKA_DECRYPT); - } - - void setupCryptoContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, - CryptoType type, CK_ATTRIBUTE_TYPE operation) - { - CK_MECHANISM_TYPE mechanism = static_cast<CK_ULONG>(-1); - - SECItem ivItem; - ivItem.type = siBuffer; - if (iv.empty()) - ivItem.data = nullptr; - else - ivItem.data = iv.data(); - ivItem.len = iv.size(); - - SECItem* pIvItem = nullptr; - - switch (type) - { - case CryptoType::AES_128_ECB: - case CryptoType::AES_256_ECB: - mechanism = CKM_AES_ECB; - break; - case CryptoType::AES_128_CBC: - mechanism = CKM_AES_CBC; - pIvItem = &ivItem; - break; - case CryptoType::AES_256_CBC: - mechanism = CKM_AES_CBC; - pIvItem = &ivItem; - break; - default: - break; - } - - mSlot = PK11_GetBestSlot(mechanism, nullptr); - - if (!mSlot) - throw css::uno::RuntimeException("NSS Slot failure", - css::uno::Reference<css::uno::XInterface>()); - - SECItem keyItem; - keyItem.type = siBuffer; - keyItem.data = key.data(); - keyItem.len = key.size(); - - mSymKey = ImportSymKey(mechanism, CKA_ENCRYPT, &keyItem); - if (!mSymKey) - throw css::uno::RuntimeException("NSS SymKey failure", - css::uno::Reference<css::uno::XInterface>()); - - mSecParam = PK11_ParamFromIV(mechanism, pIvItem); - mContext = PK11_CreateContextBySymKey(mechanism, operation, mSymKey, mSecParam); - } - - void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType) - { - CK_MECHANISM_TYPE aMechanism = static_cast<CK_ULONG>(-1); - - switch (eType) - { - case CryptoHashType::SHA1: - aMechanism = CKM_SHA_1_HMAC; - break; - case CryptoHashType::SHA256: - aMechanism = CKM_SHA256_HMAC; - break; - case CryptoHashType::SHA384: - aMechanism = CKM_SHA384_HMAC; - break; - case CryptoHashType::SHA512: - aMechanism = CKM_SHA512_HMAC; - break; - } - - mSlot = PK11_GetBestSlot(aMechanism, nullptr); - - if (!mSlot) - throw css::uno::RuntimeException("NSS Slot failure", - css::uno::Reference<css::uno::XInterface>()); - - SECItem aKeyItem; - aKeyItem.data = rKey.data(); - aKeyItem.len = rKey.size(); - - mSymKey = ImportSymKey(aMechanism, CKA_SIGN, &aKeyItem); - if (!mSymKey) - throw css::uno::RuntimeException("NSS SymKey failure", - css::uno::Reference<css::uno::XInterface>()); - - SECItem param; - param.data = nullptr; - param.len = 0; - mContext = PK11_CreateContextBySymKey(aMechanism, CKA_SIGN, mSymKey, ¶m); - } -}; -#else -struct CryptoImpl -{ -}; -#endif - Crypto::Crypto() - : mpImpl(std::make_unique<CryptoImpl>()) + : mpImpl(ICryptoImplementation::createInstance()) { } - -Crypto::~Crypto() {} +Crypto::~Crypto() = default; // DECRYPT Decrypt::Decrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type) { -#if USE_TLS_OPENSSL + USE_TLS_NSS == 0 - (void)key; - (void)iv; - (void)type; -#else mpImpl->setupDecryptContext(key, iv, type); -#endif } sal_uInt32 Decrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, sal_uInt32 inputLength) { - int outputLength = 0; - -#if USE_TLS_OPENSSL + USE_TLS_NSS > 0 sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength; -#else - (void)output; - (void)input; - (void)inputLength; -#endif - -#if USE_TLS_OPENSSL - (void)EVP_DecryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), - actualInputLength); -#endif // USE_TLS_OPENSSL - -#if USE_TLS_NSS - if (!mpImpl->mContext) - return 0; - (void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, - input.data(), actualInputLength); -#endif // USE_TLS_NSS - - return static_cast<sal_uInt32>(outputLength); + return mpImpl->decryptUpdate(output, input, actualInputLength); } sal_uInt32 Decrypt::aes128ecb(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, @@ -462,40 +49,15 @@ sal_uInt32 Decrypt::aes128ecb(std::vector<sal_uInt8>& output, std::vector<sal_uI Encrypt::Encrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type) { -#if USE_TLS_OPENSSL + USE_TLS_NSS == 0 - (void)key; - (void)iv; - (void)type; -#else mpImpl->setupEncryptContext(key, iv, type); -#endif } sal_uInt32 Encrypt::update(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, sal_uInt32 inputLength) { - int outputLength = 0; - -#if USE_TLS_OPENSSL + USE_TLS_NSS > 0 sal_uInt32 actualInputLength = inputLength == 0 || inputLength > input.size() ? input.size() : inputLength; -#else - (void)output; - (void)input; - (void)inputLength; -#endif - -#if USE_TLS_OPENSSL - (void)EVP_EncryptUpdate(mpImpl->mpContext.get(), output.data(), &outputLength, input.data(), - actualInputLength); -#endif // USE_TLS_OPENSSL - -#if USE_TLS_NSS - (void)PK11_CipherOp(mpImpl->mContext, output.data(), &outputLength, actualInputLength, - input.data(), actualInputLength); -#endif // USE_TLS_NSS - - return static_cast<sal_uInt32>(outputLength); + return mpImpl->encryptUpdate(output, input, actualInputLength); } // CryptoHash - HMAC @@ -523,52 +85,20 @@ sal_Int32 getSizeForHashType(CryptoHashType eType) CryptoHash::CryptoHash(std::vector<sal_uInt8>& rKey, CryptoHashType eType) : mnHashSize(getSizeForHashType(eType)) { -#if USE_TLS_OPENSSL + USE_TLS_NSS > 0 mpImpl->setupCryptoHashContext(rKey, eType); - -#if USE_TLS_NSS - PK11_DigestBegin(mpImpl->mContext); -#endif - -#else - (void)rKey; -#endif } bool CryptoHash::update(std::vector<sal_uInt8>& rInput, sal_uInt32 nInputLength) { -#if USE_TLS_OPENSSL + USE_TLS_NSS > 0 sal_uInt32 nActualInputLength = (nInputLength == 0 || nInputLength > rInput.size()) ? rInput.size() : nInputLength; -#else - (void)rInput; - (void)nInputLength; -#endif - -#if USE_TLS_OPENSSL - SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_Update' is deprecated - return HMAC_Update(mpImpl->mpHmacContext.get(), rInput.data(), nActualInputLength) - != 0; - SAL_WNODEPRECATED_DECLARATIONS_POP -#elif USE_TLS_NSS - return PK11_DigestOp(mpImpl->mContext, rInput.data(), nActualInputLength) == SECSuccess; -#else - return false; // ??? -#endif + return mpImpl->cryptoHashUpdate(rInput, nActualInputLength); } std::vector<sal_uInt8> CryptoHash::finalize() { std::vector<sal_uInt8> aHash(mnHashSize, 0); -#if USE_TLS_OPENSSL - unsigned int nSizeWritten = 0; - SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_Final' is deprecated - (void) HMAC_Final(mpImpl->mpHmacContext.get(), aHash.data(), &nSizeWritten); - SAL_WNODEPRECATED_DECLARATIONS_POP -#elif USE_TLS_NSS - unsigned int nSizeWritten = 0; - PK11_DigestFinal(mpImpl->mContext, aHash.data(), &nSizeWritten, aHash.size()); -#endif + mpImpl->cryptoHashFinalize(aHash); return aHash; } diff --git a/comphelper/source/crypto/Crypto_NSS.cxx b/comphelper/source/crypto/Crypto_NSS.cxx new file mode 100644 index 000000000000..4291961c801d --- /dev/null +++ b/comphelper/source/crypto/Crypto_NSS.cxx @@ -0,0 +1,295 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <comphelper/crypto/Crypto.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <sal/types.h> +#include <config_oox.h> + +#include <nss.h> +#include <nspr.h> +#include <pk11pub.h> + +namespace comphelper +{ +namespace +{ +#define MAX_WRAPPED_KEY_LEN 128 + +class CryptoImplementationNSS : public ICryptoImplementation +{ + PK11SlotInfo* mSlot; + PK11Context* mContext; + SECItem* mSecParam; + PK11SymKey* mSymKey; + PK11Context* mWrapKeyContext; + PK11SymKey* mWrapKey; + +public: + CryptoImplementationNSS() + : mSlot(nullptr) + , mContext(nullptr) + , mSecParam(nullptr) + , mSymKey(nullptr) + , mWrapKeyContext(nullptr) + , mWrapKey(nullptr) + { + // Initialize NSS, database functions are not needed + if (!NSS_IsInitialized()) + { + auto const e = NSS_NoDB_Init(nullptr); + if (e != SECSuccess) + { + PRErrorCode error = PR_GetError(); + const char* errorText = PR_ErrorToName(error); + throw css::uno::RuntimeException( + "NSS_NoDB_Init failed with " + + OUString(errorText, strlen(errorText), RTL_TEXTENCODING_UTF8) + " (" + + OUString::number(static_cast<int>(error)) + ")"); + } + } + } + + virtual ~CryptoImplementationNSS() + { + if (mContext) + PK11_DestroyContext(mContext, PR_TRUE); + if (mSecParam) + SECITEM_FreeItem(mSecParam, PR_TRUE); + if (mSymKey) + PK11_FreeSymKey(mSymKey); + if (mWrapKeyContext) + PK11_DestroyContext(mWrapKeyContext, PR_TRUE); + if (mWrapKey) + PK11_FreeSymKey(mWrapKey); + if (mSlot) + PK11_FreeSlot(mSlot); + } + + PK11SymKey* ImportSymKey(CK_MECHANISM_TYPE mechanism, CK_ATTRIBUTE_TYPE operation, SECItem* key) + { + mSymKey = PK11_ImportSymKey(mSlot, mechanism, PK11_OriginUnwrap, operation, key, nullptr); + if (!mSymKey) //rhbz#1614419 maybe failed due to FIPS, use rhbz#1461450 style workaround + { + /* + * Without FIPS it would be possible to just use + * mSymKey = PK11_ImportSymKey( mSlot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr ); + * with FIPS NSS Level 2 certification has to be "workarounded" (so it becomes Level 1) by using + * following method: + * 1. Generate wrap key + * 2. Encrypt authkey with wrap key + * 3. Unwrap encrypted authkey using wrap key + */ + + /* + * Generate wrapping key + */ + CK_MECHANISM_TYPE wrap_mechanism = PK11_GetBestWrapMechanism(mSlot); + int wrap_key_len = PK11_GetBestKeyLength(mSlot, wrap_mechanism); + mWrapKey = PK11_KeyGen(mSlot, wrap_mechanism, nullptr, wrap_key_len, nullptr); + if (!mWrapKey) + throw css::uno::RuntimeException("PK11_KeyGen SymKey failure", + css::uno::Reference<css::uno::XInterface>()); + + /* + * Encrypt authkey with wrapping key + */ + + /* + * Initialization of IV is not needed because PK11_GetBestWrapMechanism should return ECB mode + */ + SECItem tmp_sec_item = {}; + mWrapKeyContext + = PK11_CreateContextBySymKey(wrap_mechanism, CKA_ENCRYPT, mWrapKey, &tmp_sec_item); + if (!mWrapKeyContext) + throw css::uno::RuntimeException("PK11_CreateContextBySymKey failure", + css::uno::Reference<css::uno::XInterface>()); + + unsigned char wrapped_key_data[MAX_WRAPPED_KEY_LEN]; + int wrapped_key_len = sizeof(wrapped_key_data); + + if (PK11_CipherOp(mWrapKeyContext, wrapped_key_data, &wrapped_key_len, + sizeof(wrapped_key_data), key->data, key->len) + != SECSuccess) + { + throw css::uno::RuntimeException("PK11_CipherOp failure", + css::uno::Reference<css::uno::XInterface>()); + } + + if (PK11_Finalize(mWrapKeyContext) != SECSuccess) + throw css::uno::RuntimeException("PK11_Finalize failure", + css::uno::Reference<css::uno::XInterface>()); + + /* + * Finally unwrap sym key + */ + SECItem wrapped_key = {}; + wrapped_key.data = wrapped_key_data; + wrapped_key.len = wrapped_key_len; + + mSymKey = PK11_UnwrapSymKey(mWrapKey, wrap_mechanism, &tmp_sec_item, &wrapped_key, + mechanism, operation, key->len); + } + return mSymKey; + } + + void setupEncryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, + CryptoType type) override + { + setupCryptoContext(key, iv, type, CKA_ENCRYPT); + } + + void setupDecryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, + CryptoType type) override + { + setupCryptoContext(key, iv, type, CKA_DECRYPT); + } + + void setupCryptoContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, + CryptoType type, CK_ATTRIBUTE_TYPE operation) + { + CK_MECHANISM_TYPE mechanism = static_cast<CK_ULONG>(-1); + + SECItem ivItem; + ivItem.type = siBuffer; + if (iv.empty()) + ivItem.data = nullptr; + else + ivItem.data = iv.data(); + ivItem.len = iv.size(); + + SECItem* pIvItem = nullptr; + + switch (type) + { + case CryptoType::AES_128_ECB: + case CryptoType::AES_256_ECB: + mechanism = CKM_AES_ECB; + break; + case CryptoType::AES_128_CBC: + mechanism = CKM_AES_CBC; + pIvItem = &ivItem; + break; + case CryptoType::AES_256_CBC: + mechanism = CKM_AES_CBC; + pIvItem = &ivItem; + break; + default: + break; + } + + mSlot = PK11_GetBestSlot(mechanism, nullptr); + + if (!mSlot) + throw css::uno::RuntimeException("NSS Slot failure", + css::uno::Reference<css::uno::XInterface>()); + + SECItem keyItem; + keyItem.type = siBuffer; + keyItem.data = key.data(); + keyItem.len = key.size(); + + mSymKey = ImportSymKey(mechanism, CKA_ENCRYPT, &keyItem); + if (!mSymKey) + throw css::uno::RuntimeException("NSS SymKey failure", + css::uno::Reference<css::uno::XInterface>()); + + mSecParam = PK11_ParamFromIV(mechanism, pIvItem); + mContext = PK11_CreateContextBySymKey(mechanism, operation, mSymKey, mSecParam); + } + + void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType) override + { + CK_MECHANISM_TYPE aMechanism = static_cast<CK_ULONG>(-1); + + switch (eType) + { + case CryptoHashType::SHA1: + aMechanism = CKM_SHA_1_HMAC; + break; + case CryptoHashType::SHA256: + aMechanism = CKM_SHA256_HMAC; + break; + case CryptoHashType::SHA384: + aMechanism = CKM_SHA384_HMAC; + break; + case CryptoHashType::SHA512: + aMechanism = CKM_SHA512_HMAC; + break; + } + + mSlot = PK11_GetBestSlot(aMechanism, nullptr); + + if (!mSlot) + throw css::uno::RuntimeException("NSS Slot failure", + css::uno::Reference<css::uno::XInterface>()); + + SECItem aKeyItem; + aKeyItem.data = rKey.data(); + aKeyItem.len = rKey.size(); + + mSymKey = ImportSymKey(aMechanism, CKA_SIGN, &aKeyItem); + if (!mSymKey) + throw css::uno::RuntimeException("NSS SymKey failure", + css::uno::Reference<css::uno::XInterface>()); + + SECItem param; + param.data = nullptr; + param.len = 0; + mContext = PK11_CreateContextBySymKey(aMechanism, CKA_SIGN, mSymKey, ¶m); + + // Also call digest to begin + PK11_DigestBegin(mContext); + } + + sal_uInt32 decryptUpdate(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, + sal_uInt32 inputLength) override + { + if (!mContext) + return 0; + int outputLength = 0; + (void)PK11_CipherOp(mContext, output.data(), &outputLength, inputLength, input.data(), + inputLength); + return outputLength; + } + + sal_uInt32 encryptUpdate(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, + sal_uInt32 inputLength) override + { + if (!mContext) + return 0; + int outputLength = 0; + (void)PK11_CipherOp(mContext, output.data(), &outputLength, inputLength, input.data(), + inputLength); + return outputLength; + } + + bool cryptoHashUpdate(std::vector<sal_uInt8>& rInput, sal_uInt32 nInputLength) override + { + return PK11_DigestOp(mContext, rInput.data(), nInputLength) == SECSuccess; + } + + bool cryptoHashFinalize(std::vector<sal_uInt8>& rHash) override + { + unsigned int nSizeWritten = 0; + PK11_DigestFinal(mContext, rHash.data(), &nSizeWritten, rHash.size()); + return nSizeWritten == rHash.size(); + } +}; + +} // anonymous namespace + +std::shared_ptr<ICryptoImplementation> ICryptoImplementation::createInstance() +{ + return std::shared_ptr<ICryptoImplementation>(new CryptoImplementationNSS); +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/crypto/Crypto_None.cxx b/comphelper/source/crypto/Crypto_None.cxx new file mode 100644 index 000000000000..b1c2d9712e51 --- /dev/null +++ b/comphelper/source/crypto/Crypto_None.cxx @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <comphelper/crypto/Crypto.hxx> +#include <sal/types.h> +#include <config_oox.h> + +namespace comphelper +{ +namespace +{ +class CryptoImplementationNone : public ICryptoImplementation +{ +public: + CryptoImplementationNone() = default; + + virtual ~CryptoImplementationNone() {} + + void setupEncryptContext(std::vector<sal_uInt8>& /*key*/, std::vector<sal_uInt8>& /*iv*/, + CryptoType /*eType*/) override + { + } + + void setupDecryptContext(std::vector<sal_uInt8>& /*key*/, std::vector<sal_uInt8>& /*iv*/, + CryptoType /*eType*/) override + { + } + + void setupCryptoHashContext(std::vector<sal_uInt8>& /*rKey*/, CryptoHashType /*eType*/) override + { + } + + sal_uInt32 decryptUpdate(std::vector<sal_uInt8>& /*output*/, std::vector<sal_uInt8>& /*input*/, + sal_uInt32 /*inputLength*/) override + { + return 0; + } + + sal_uInt32 encryptUpdate(std::vector<sal_uInt8>& /*output*/, std::vector<sal_uInt8>& /*input*/, + sal_uInt32 /*inputLength*/) override + { + return 0; + } + + bool cryptoHashUpdate(std::vector<sal_uInt8>& /*rInput*/, sal_uInt32 /*nInputLength*/) override + { + return false; + } + + bool cryptoHashFinalize(std::vector<sal_uInt8>& /*rHash*/) override { return false; } +}; +} // anonymous namespace + +std::shared_ptr<ICryptoImplementation> ICryptoImplementation::createInstance() +{ + return std::shared_ptr<ICryptoImplementation>(new CryptoImplementationNone); +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/crypto/Crypto_OpenSSL.cxx b/comphelper/source/crypto/Crypto_OpenSSL.cxx new file mode 100644 index 000000000000..37c5ff44e222 --- /dev/null +++ b/comphelper/source/crypto/Crypto_OpenSSL.cxx @@ -0,0 +1,208 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <comphelper/crypto/Crypto.hxx> +#include <sal/types.h> +#include <config_oox.h> + +#include <openssl/evp.h> +#include <openssl/sha.h> +#include <openssl/hmac.h> + +namespace comphelper +{ +namespace +{ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + +static HMAC_CTX* HMAC_CTX_new(void) +{ + HMAC_CTX* pContext = new HMAC_CTX; + HMAC_CTX_init(pContext); + return pContext; +} + +static void HMAC_CTX_free(HMAC_CTX* pContext) +{ + HMAC_CTX_cleanup(pContext); + delete pContext; +} +#endif + +namespace +{ +struct cipher_delete +{ + void operator()(EVP_CIPHER_CTX* p) { EVP_CIPHER_CTX_free(p); } +}; + +struct hmac_delete +{ + SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_CTX_free' is deprecated + void + operator()(HMAC_CTX* p) + { + HMAC_CTX_free(p); + } + SAL_WNODEPRECATED_DECLARATIONS_POP +}; +} + +class CryptoImplementationOpenSSL : public ICryptoImplementation +{ + std::unique_ptr<EVP_CIPHER_CTX, cipher_delete> mpContext; + std::unique_ptr<HMAC_CTX, hmac_delete> mpHmacContext; + +public: + CryptoImplementationOpenSSL() = default; + + virtual ~CryptoImplementationOpenSSL() + { + if (mpContext) + EVP_CIPHER_CTX_cleanup(mpContext.get()); + } + + void setupEncryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, + CryptoType eType) override + { + mpContext.reset(EVP_CIPHER_CTX_new()); + EVP_CIPHER_CTX_init(mpContext.get()); + + const EVP_CIPHER* cipher = getCipher(eType); + if (cipher == nullptr) + return; + + if (iv.empty()) + EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), nullptr); + else + EVP_EncryptInit_ex(mpContext.get(), cipher, nullptr, key.data(), iv.data()); + EVP_CIPHER_CTX_set_padding(mpContext.get(), 0); + } + + void setupDecryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, + CryptoType eType) override + { + mpContext.reset(EVP_CIPHER_CTX_new()); + EVP_CIPHER_CTX_init(mpContext.get()); + + const EVP_CIPHER* pCipher = getCipher(eType); + if (pCipher == nullptr) + return; + + const size_t nMinKeySize = EVP_CIPHER_key_length(pCipher); + if (key.size() < nMinKeySize) + key.resize(nMinKeySize, 0); + + if (iv.empty()) + EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), nullptr); + else + { + const size_t nMinIVSize = EVP_CIPHER_iv_length(pCipher); + if (iv.size() < nMinIVSize) + iv.resize(nMinIVSize, 0); + + EVP_DecryptInit_ex(mpContext.get(), pCipher, nullptr, key.data(), iv.data()); + } + EVP_CIPHER_CTX_set_padding(mpContext.get(), 0); + } + + void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType) override + { + SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_CTX_new' is deprecated + mpHmacContext.reset(HMAC_CTX_new()); + SAL_WNODEPRECATED_DECLARATIONS_POP + const EVP_MD* aEvpMd = nullptr; + switch (eType) + { + case CryptoHashType::SHA1: + aEvpMd = EVP_sha1(); + break; + case CryptoHashType::SHA256: + aEvpMd = EVP_sha256(); + break; + case CryptoHashType::SHA384: + aEvpMd = EVP_sha384(); + break; + case CryptoHashType::SHA512: + aEvpMd = EVP_sha512(); + break; + } + SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_Init_ex' is deprecated + HMAC_Init_ex(mpHmacContext.get(), rKey.data(), rKey.size(), aEvpMd, nullptr); + SAL_WNODEPRECATED_DECLARATIONS_POP + } + + static const EVP_CIPHER* getCipher(CryptoType type) + { + switch (type) + { + case CryptoType::AES_128_ECB: + return EVP_aes_128_ecb(); + case CryptoType::AES_256_ECB: + return EVP_aes_256_ecb(); + case CryptoType::AES_128_CBC: + return EVP_aes_128_cbc(); + case CryptoType::AES_256_CBC: + return EVP_aes_256_cbc(); + default: + break; + } + return nullptr; + } + + sal_uInt32 decryptUpdate(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, + sal_uInt32 inputLength) override + { + if (!mpContext) + return 0; + int outputLength = 0; + (void)EVP_DecryptUpdate(mpContext.get(), output.data(), &outputLength, input.data(), + inputLength); + return outputLength; + } + + sal_uInt32 encryptUpdate(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, + sal_uInt32 inputLength) override + { + if (!mpContext) + return 0; + int outputLength = 0; + (void)EVP_EncryptUpdate(mpContext.get(), output.data(), &outputLength, input.data(), + inputLength); + return sal_uInt32(outputLength); + } + + bool cryptoHashUpdate(std::vector<sal_uInt8>& rInput, sal_uInt32 nInputLength) override + { + SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_Update' is deprecated + return HMAC_Update(mpHmacContext.get(), rInput.data(), nInputLength) + != 0; + SAL_WNODEPRECATED_DECLARATIONS_POP + } + + bool cryptoHashFinalize(std::vector<sal_uInt8>& rHash) override + { + unsigned int nSizeWritten = 0; + SAL_WNODEPRECATED_DECLARATIONS_PUSH // 'HMAC_Final' is deprecated + (void) HMAC_Final(mpHmacContext.get(), rHash.data(), &nSizeWritten); + SAL_WNODEPRECATED_DECLARATIONS_POP + return nSizeWritten == rHash.size(); + } +}; + +} // anonymous namespace + +std::shared_ptr<ICryptoImplementation> ICryptoImplementation::createInstance() +{ + return std::shared_ptr<ICryptoImplementation>(new CryptoImplementationOpenSSL); +} + +} // namespace comphelper + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/comphelper/crypto/Crypto.hxx b/include/comphelper/crypto/Crypto.hxx index 151ee9d04565..abebf9440a5a 100644 --- a/include/comphelper/crypto/Crypto.hxx +++ b/include/comphelper/crypto/Crypto.hxx @@ -50,20 +50,41 @@ enum class CryptoType AES_256_CBC, }; -struct CryptoImpl; +class ICryptoImplementation +{ +public: + static std::shared_ptr<ICryptoImplementation> createInstance(); + + virtual void setupDecryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, + CryptoType eType) + = 0; + virtual void setupEncryptContext(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, + CryptoType type) + = 0; + virtual void setupCryptoHashContext(std::vector<sal_uInt8>& rKey, CryptoHashType eType) = 0; + + virtual sal_uInt32 decryptUpdate(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, + sal_uInt32 inputLength) + = 0; + virtual sal_uInt32 encryptUpdate(std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input, + sal_uInt32 inputLength) + = 0; + virtual bool cryptoHashUpdate(std::vector<sal_uInt8>& rInput, sal_uInt32 nInputLength) = 0; + virtual bool cryptoHashFinalize(std::vector<sal_uInt8>& rHash) = 0; +}; class COMPHELPER_DLLPUBLIC Crypto { protected: - std::unique_ptr<CryptoImpl> mpImpl; + std::shared_ptr<ICryptoImplementation> mpImpl; -protected: Crypto(); public: virtual ~Crypto(); }; +/** Decrypt vector of bytes with AES encryption */ class COMPHELPER_DLLPUBLIC Decrypt final : public Crypto { public: