From c302dceb99413c241bcff5f5f8b943c30ad5c1b8 Mon Sep 17 00:00:00 2001 From: Radoslaw Zarzynski Date: Tue, 12 Mar 2019 02:23:53 +0100 Subject: [PATCH] crypto: bring PK11_ImportSymKey_FIPS doing PK11_UnwrapSymKey. Signed-off-by: Radoslaw Zarzynski --- src/common/ceph_crypto.cc | 119 ++++++++++++++++++++++++++++++++++++++ src/common/ceph_crypto.h | 14 +++++ 2 files changed, 133 insertions(+) diff --git a/src/common/ceph_crypto.cc b/src/common/ceph_crypto.cc index a0aa8767e428f..77454fb605b02 100644 --- a/src/common/ceph_crypto.cc +++ b/src/common/ceph_crypto.cc @@ -14,6 +14,7 @@ #include "common/config.h" #include "ceph_crypto.h" +#include "include/scope_guard.h" #ifdef USE_CRYPTOPP void ceph::crypto::init(CephContext *cct) @@ -44,6 +45,124 @@ static uint32_t crypto_refs = 0; static NSSInitContext *crypto_context = NULL; static pid_t crypto_init_pid = 0; +PK11SymKey *ceph::crypto::PK11_ImportSymKey_FIPS( + PK11SlotInfo * const slot, + const CK_MECHANISM_TYPE type, + const PK11Origin origin, + const CK_ATTRIBUTE_TYPE operation, + SECItem * const raw_key, + void * const wincx) +{ + if (PK11_IsFIPS() == PR_FALSE) { + // This isn't the FIPS mode, and thus PK11_ImportSymKey is available. Let's + // make use of it to avoid overhead related to e.g. creating extra PK11Ctx. + PK11SymKey *ret_key = nullptr; + ret_key = PK11_ImportSymKey(slot, type, origin, operation, raw_key, wincx); + + return ret_key; + } + + ceph_assert_always(wincx == nullptr); + + std::vector wrapped_key; + + // getting 306 on my system which is CKM_DES3_ECB. + const CK_MECHANISM_TYPE wrap_mechanism = PK11_GetBestWrapMechanism(slot); + + // Generate a wrapping key. It will be used exactly twice over the scope: + // * to encrypt raw_key giving wrapped_key, + // * to decrypt wrapped_key in the internals of PK11_UnwrapSymKey(). + PK11SymKey * const wrapping_key = PK11_KeyGen( + slot, + wrap_mechanism, + nullptr, + PK11_GetBestKeyLength(slot, wrap_mechanism), + nullptr); + if (wrapping_key == nullptr) { + return nullptr; + } + auto wk_guard = make_scope_guard([wrapping_key] { + PK11_FreeSymKey(wrapping_key); + }); + + // Prepare a PK11 context for the raw_key -> wrapped_key encryption. + SECItem tmp_sec_item; + ::memset(&tmp_sec_item, 0, sizeof(tmp_sec_item)); + PK11Context * const wrap_key_crypt_context = PK11_CreateContextBySymKey( + wrap_mechanism, + CKA_ENCRYPT, + wrapping_key, + &tmp_sec_item); + if (wrap_key_crypt_context == nullptr) { + return nullptr; + } + auto wkcc_guard = make_scope_guard([wrap_key_crypt_context] { + PK11_DestroyContext(wrap_key_crypt_context, PR_TRUE); + }); + + + // Finally wrap the key. Important note is that the wrapping mechanism + // selection (read: just grabbing a cipher) offers, at least in my NSS + // copy, mostly CKM_*_ECB ciphers (with 3DES as the leading one, see + // wrapMechanismList[] in pk11mech.c). There is no CKM_*_*_PAD variant + // which means that plaintext we are providing to PK11_CipherOp() must + // be aligned to cipher's block size. For 3DES it's 64 bits. + { + const auto block_size = PK11_GetBlockSize(wrap_mechanism, nullptr); + SECItem * const raw_key_aligned = PK11_BlockData(raw_key, block_size); + if (raw_key_aligned == nullptr) { + return nullptr; + } + auto rka_guard = make_scope_guard([raw_key_aligned] { + SECITEM_FreeItem(raw_key_aligned, PR_TRUE); + }); + + // PARANOIA: always add space for one extra cipher's block. This seems + // unnecessary at the moment as padding is never used (see the comment + // above) but let's assume it can change in the future. Just in case. + wrapped_key.resize(raw_key_aligned->len + block_size, 0x0); + int out_len = 0; + + int ret = PK11_CipherOp( + wrap_key_crypt_context, + wrapped_key.data(), + &out_len, + wrapped_key.size(), // max space + raw_key_aligned->data, + raw_key_aligned->len); + if (ret != SECSuccess) { + return nullptr; + } + + ret = PK11_Finalize(wrap_key_crypt_context); + if (ret != SECSuccess) { + return nullptr; + } + + ceph_assert(out_len <= static_cast(wrapped_key.size())); + wrapped_key.resize(out_len); + } + + // Key is wrapped now so we can acquire the ultimate PK11SymKey through + // unwrapping it. Of course these two opposite operations form NOP with + // a side effect: FIPS level 1 compatibility. + ::memset(&tmp_sec_item, 0, sizeof(tmp_sec_item)); + + SECItem wrapped_key_item; + ::memset(&wrapped_key_item, 0, sizeof(wrapped_key_item)); + wrapped_key_item.data = wrapped_key.data(); + wrapped_key_item.len = wrapped_key.size(); + + return PK11_UnwrapSymKey( + wrapping_key, + wrap_mechanism, + &tmp_sec_item, + &wrapped_key_item, + type, + operation, + raw_key->len); +} + void ceph::crypto::init(CephContext *cct) { pid_t pid = getpid(); diff --git a/src/common/ceph_crypto.h b/src/common/ceph_crypto.h index 9c302392923df..c0900b7248ef6 100644 --- a/src/common/ceph_crypto.h +++ b/src/common/ceph_crypto.h @@ -67,6 +67,20 @@ namespace ceph { // ugly bit of CryptoPP that we have to emulate here :( typedef unsigned char byte; +namespace ceph { + namespace crypto { + // workaround for no PK11_ImportSymKey in FIPS mode + PK11SymKey *PK11_ImportSymKey_FIPS( + PK11SlotInfo *slot, + CK_MECHANISM_TYPE type, + PK11Origin origin, + CK_ATTRIBUTE_TYPE operation, + SECItem *key, + void *wincx); + } // namespace crypto +} // namespace + + namespace ceph { namespace crypto { void assert_init(); -- 2.39.5