]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
crypto: bring PK11_ImportSymKey_FIPS doing PK11_UnwrapSymKey.
authorRadoslaw Zarzynski <rzarzyns@redhat.com>
Tue, 12 Mar 2019 01:23:53 +0000 (02:23 +0100)
committerRadoslaw Zarzynski <rzarzyns@redhat.com>
Wed, 27 Mar 2019 21:20:08 +0000 (17:20 -0400)
Signed-off-by: Radoslaw Zarzynski <rzarzyns@redhat.com>
src/common/ceph_crypto.cc
src/common/ceph_crypto.h

index a0aa8767e428f42b1df583aa1e7a26fce7311777..77454fb605b02eeedc255fb5c7a801cc0692f7a5 100644 (file)
@@ -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<unsigned char> 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<int>(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();
index 9c302392923df326673ad36df4246637f6e9118e..c0900b7248ef620f1b08c5d7323e58a0803e3b99 100644 (file)
@@ -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();