/* -*- 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 #include #include #include #include #include #include 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(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(u"PK11_KeyGen SymKey failure"_ustr, css::uno::Reference()); /* * 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(u"PK11_CreateContextBySymKey failure"_ustr, css::uno::Reference()); 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(u"PK11_CipherOp failure"_ustr, css::uno::Reference()); } if (PK11_Finalize(mWrapKeyContext) != SECSuccess) throw css::uno::RuntimeException(u"PK11_Finalize failure"_ustr, css::uno::Reference()); /* * 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& key, std::vector& iv, CryptoType type) override { setupCryptoContext(key, iv, type, CKA_ENCRYPT); } void setupDecryptContext(std::vector& key, std::vector& iv, CryptoType type) override { setupCryptoContext(key, iv, type, CKA_DECRYPT); } void setupCryptoContext(std::vector& key, std::vector& iv, CryptoType type, CK_ATTRIBUTE_TYPE operation) { CK_MECHANISM_TYPE mechanism = static_cast(-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(u"NSS Slot failure"_ustr, css::uno::Reference()); 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(u"NSS SymKey failure"_ustr, css::uno::Reference()); mSecParam = PK11_ParamFromIV(mechanism, pIvItem); mContext = PK11_CreateContextBySymKey(mechanism, operation, mSymKey, mSecParam); } void setupCryptoHashContext(std::vector& rKey, CryptoHashType eType) override { CK_MECHANISM_TYPE aMechanism = static_cast(-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(u"NSS Slot failure"_ustr, css::uno::Reference()); SECItem aKeyItem; aKeyItem.data = rKey.data(); aKeyItem.len = rKey.size(); mSymKey = ImportSymKey(aMechanism, CKA_SIGN, &aKeyItem); if (!mSymKey) throw css::uno::RuntimeException(u"NSS SymKey failure"_ustr, css::uno::Reference()); 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& output, std::vector& 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& output, std::vector& 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& rInput, sal_uInt32 nInputLength) override { return PK11_DigestOp(mContext, rInput.data(), nInputLength) == SECSuccess; } bool cryptoHashFinalize(std::vector& rHash) override { unsigned int nSizeWritten = 0; PK11_DigestFinal(mContext, rHash.data(), &nSizeWritten, rHash.size()); return nSizeWritten == rHash.size(); } }; } // anonymous namespace std::shared_ptr ICryptoImplementation::createInstance() { return std::shared_ptr(new CryptoImplementationNSS); } } // namespace comphelper /* vim:set shiftwidth=4 softtabstop=4 expandtab: */