Blob Blame History Raw

# HG changeset patch
# User Robert Relyea <rrelyea@redhat.com>
# Date 1603492441 25200
# Node ID 33f920fcd1753d2b8f4a5e4f31e317c102d8cbfe
# Parent  e3bd9c2f925932b301440fb07ea1228f2d4e39ac
Bug 1666891 - Add PK11_Pub{Wrap,Unwrap}SymKeyWithMechanism r=mt,rrelyea

Summary

This is useful for RSA-OAEP support.

The CKM_RSA_PKCS_OAEP mechanism requires a CK_RSA_PKCS_OAEP_PARAMS
be present for PKCS#11 calls. This provides required context for OAEP.
However, PK11_PubWrapSymKey lacks a way of providing this context and
historically silently converted CKM_RSA_PKCS_OAEP to CKM_RSA_PKCS when
a RSA key is provided. Introducing a new call will let us indicate
parameters and potentially support other mechanisms in the future.
This call mirrors the earlier calls introduced for RSA-PSS:
PK11_SignWithMechanism and PK11_VerifyWithMechanism.

The CKM_RSA_PKCS_OAEP mechanism requires a CK_RSA_PKCS_OAEP_PARAMS
be present for PKCS#11 calls. This provides required context for OAEP.
However, PK11_PubUnwrapSymKey lacks a way of providing this context,
and additionally lacked a way of indicating which mechanism type to use
for the unwrap operation (instead detecting it by key type). Introducing
a new call will let us indicate parameters and potentially support other
mechanisms in the future.

Signed-off-by: Alexander Scheel <ascheel@redhat.com>

Differential Revision: https://phabricator.services.mozilla.com/D93424

diff --git a/gtests/pk11_gtest/pk11_rsaoaep_unittest.cc b/gtests/pk11_gtest/pk11_rsaoaep_unittest.cc
--- a/gtests/pk11_gtest/pk11_rsaoaep_unittest.cc
+++ b/gtests/pk11_gtest/pk11_rsaoaep_unittest.cc
@@ -111,9 +111,76 @@ INSTANTIATE_TEST_CASE_P(
 
 INSTANTIATE_TEST_CASE_P(
     WycheproofOaep2048Sha512Sha1Test, RsaOaepWycheproofTest,
     ::testing::ValuesIn(kRsaOaep2048Sha512Mgf1Sha1WycheproofVectors));
 
 INSTANTIATE_TEST_CASE_P(
     WycheproofOaep2048Sha512Sha512Test, RsaOaepWycheproofTest,
     ::testing::ValuesIn(kRsaOaep2048Sha512Mgf1Sha512WycheproofVectors));
+
+TEST(Pkcs11RsaOaepTest, TestOaepWrapUnwrap) {
+  const size_t kRsaKeyBits = 2048;
+  const size_t kwrappedBufLen = 4096;
+
+  SECStatus rv = SECFailure;
+
+  ScopedSECKEYPrivateKey priv;
+  ScopedSECKEYPublicKey pub;
+  PK11RSAGenParams rsa_params;
+  rsa_params.keySizeInBits = kRsaKeyBits;
+  rsa_params.pe = 65537;
+
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  ASSERT_NE(slot, nullptr);
+
+  SECKEYPublicKey* p_pub_tmp = nullptr;
+  priv.reset(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
+                                  &rsa_params, &p_pub_tmp, false, false,
+                                  nullptr));
+  pub.reset(p_pub_tmp);
+
+  ASSERT_NE(priv.get(), nullptr);
+  ASSERT_NE(pub.get(), nullptr);
+
+  ScopedPK11SymKey to_wrap(
+      PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
+
+  CK_RSA_PKCS_OAEP_PARAMS oaep_params = {CKM_SHA256, CKG_MGF1_SHA256,
+                                         CKZ_DATA_SPECIFIED, NULL, 0};
+
+  SECItem param = {siBuffer, (unsigned char*)&oaep_params, sizeof(oaep_params)};
+
+  ScopedSECItem wrapped(SECITEM_AllocItem(nullptr, nullptr, kwrappedBufLen));
+  rv = PK11_PubWrapSymKeyWithMechanism(pub.get(), CKM_RSA_PKCS_OAEP, &param,
+                                       to_wrap.get(), wrapped.get());
+  ASSERT_EQ(rv, SECSuccess);
+
+  PK11SymKey* p_unwrapped_tmp = nullptr;
+
+  // This fails because this method is broken and assumes CKM_RSA_PKCS and
+  // doesn't understand OAEP.
+  p_unwrapped_tmp = PK11_PubUnwrapSymKey(priv.get(), wrapped.get(), CKM_AES_CBC,
+                                         CKA_DECRYPT, 16);
+  ASSERT_EQ(p_unwrapped_tmp, nullptr);
+
+  ScopedPK11SymKey unwrapped;
+  p_unwrapped_tmp = PK11_PubUnwrapSymKeyWithMechanism(
+      priv.get(), CKM_RSA_PKCS_OAEP, &param, wrapped.get(), CKM_AES_CBC,
+      CKA_DECRYPT, 16);
+  ASSERT_NE(p_unwrapped_tmp, nullptr);
+
+  unwrapped.reset(p_unwrapped_tmp);
+
+  // Extract key's value in order to validate decryption worked.
+  rv = PK11_ExtractKeyValue(to_wrap.get());
+  ASSERT_EQ(rv, SECSuccess);
+
+  rv = PK11_ExtractKeyValue(unwrapped.get());
+  ASSERT_EQ(rv, SECSuccess);
+
+  // References owned by PKCS#11 layer; no need to scope and free.
+  SECItem* expectedItem = PK11_GetKeyData(to_wrap.get());
+  SECItem* actualItem = PK11_GetKeyData(unwrapped.get());
+
+  ASSERT_EQ(SECITEM_CompareItem(actualItem, expectedItem), 0);
+}
 }  // namespace nss_test
diff --git a/lib/nss/nss.def b/lib/nss/nss.def
--- a/lib/nss/nss.def
+++ b/lib/nss/nss.def
@@ -1181,3 +1181,10 @@ SECMOD_GetSystemFIPSEnabled;
 ;+    local:
 ;+       *;
 ;+};
+;+NSS_3.59 {   # NSS 3.59 release
+;+    global:
+PK11_PubWrapSymKeyWithMechanism;
+PK11_PubUnwrapSymKeyWithMechanism;
+;+    local:
+;+       *;
+;+};
diff --git a/lib/pk11wrap/pk11pub.h b/lib/pk11wrap/pk11pub.h
--- a/lib/pk11wrap/pk11pub.h
+++ b/lib/pk11wrap/pk11pub.h
@@ -352,16 +352,21 @@ void PK11_SetSymKeyUserData(PK11SymKey *
  * will return NULL. Returned data is still owned and managed by the SymKey,
  * the caller should not free the data.
  *
  */
 void *PK11_GetSymKeyUserData(PK11SymKey *symKey);
 
 SECStatus PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey,
                              PK11SymKey *symKey, SECItem *wrappedKey);
+SECStatus PK11_PubWrapSymKeyWithMechanism(SECKEYPublicKey *pubKey,
+                                          CK_MECHANISM_TYPE mechType,
+                                          SECItem *param,
+                                          PK11SymKey *symKey,
+                                          SECItem *wrappedKey);
 SECStatus PK11_WrapSymKey(CK_MECHANISM_TYPE type, SECItem *params,
                           PK11SymKey *wrappingKey, PK11SymKey *symKey, SECItem *wrappedKey);
 /* move a key to 'slot' optionally set the key attributes according to either
  * operation or the  flags and making the key permanent at the same time.
  * If the key is moved to the same slot, operation and flags values are
  * currently ignored */
 PK11SymKey *PK11_MoveSymKey(PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation,
                             CK_FLAGS flags, PRBool perm, PK11SymKey *symKey);
@@ -446,16 +451,23 @@ PK11SymKey *PK11_UnwrapSymKeyWithFlagsPe
  *  PK11_PubUnwrap returns a key which can do exactly one operation, and is
  * ephemeral (session key).
  *  PK11_PubUnwrapWithFlagsPerm is the same as PK11_PubUnwrap except you can
  * use * CKF_ flags to enable more than one operation, and optionally make
  * the key permanent (token key).
  */
 PK11SymKey *PK11_PubUnwrapSymKey(SECKEYPrivateKey *key, SECItem *wrapppedKey,
                                  CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize);
+PK11SymKey *PK11_PubUnwrapSymKeyWithMechanism(SECKEYPrivateKey *key,
+                                              CK_MECHANISM_TYPE mechType,
+                                              SECItem *param,
+                                              SECItem *wrapppedKey,
+                                              CK_MECHANISM_TYPE target,
+                                              CK_ATTRIBUTE_TYPE operation,
+                                              int keySize);
 PK11SymKey *PK11_PubUnwrapSymKeyWithFlagsPerm(SECKEYPrivateKey *wrappingKey,
                                               SECItem *wrappedKey, CK_MECHANISM_TYPE target,
                                               CK_ATTRIBUTE_TYPE operation, int keySize,
                                               CK_FLAGS flags, PRBool isPerm);
 PK11SymKey *PK11_FindFixedKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
                               SECItem *keyID, void *wincx);
 SECStatus PK11_DeleteTokenPrivateKey(SECKEYPrivateKey *privKey, PRBool force);
 SECStatus PK11_DeleteTokenPublicKey(SECKEYPublicKey *pubKey);
diff --git a/lib/pk11wrap/pk11skey.c b/lib/pk11wrap/pk11skey.c
--- a/lib/pk11wrap/pk11skey.c
+++ b/lib/pk11wrap/pk11skey.c
@@ -1270,53 +1270,69 @@ PK11_ConvertSessionSymKeyToTokenSymKey(P
         PORT_SetError(PK11_MapError(crv));
         return NULL;
     }
 
     return PK11_SymKeyFromHandle(slot, NULL /*parent*/, symk->origin,
                                  symk->type, newKeyID, PR_FALSE /*owner*/, NULL /*wincx*/);
 }
 
-/*
- * This function does a straight public key wrap (which only RSA can do).
- * Use PK11_PubGenKey and PK11_WrapSymKey to implement the FORTEZZA and
- * Diffie-Hellman Ciphers. */
+/* This function does a straight public key wrap with the CKM_RSA_PKCS
+ * mechanism. */
 SECStatus
 PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey,
                    PK11SymKey *symKey, SECItem *wrappedKey)
 {
+    CK_MECHANISM_TYPE inferred = pk11_mapWrapKeyType(pubKey->keyType);
+    return PK11_PubWrapSymKeyWithMechanism(pubKey, inferred, NULL, symKey,
+                                           wrappedKey);
+}
+
+/* This function wraps a symmetric key with a public key, such as with the
+ * CKM_RSA_PKCS and CKM_RSA_PKCS_OAEP mechanisms. */
+SECStatus
+PK11_PubWrapSymKeyWithMechanism(SECKEYPublicKey *pubKey,
+                                CK_MECHANISM_TYPE mechType, SECItem *param,
+                                PK11SymKey *symKey, SECItem *wrappedKey)
+{
     PK11SlotInfo *slot;
     CK_ULONG len = wrappedKey->len;
     PK11SymKey *newKey = NULL;
     CK_OBJECT_HANDLE id;
     CK_MECHANISM mechanism;
     PRBool owner = PR_TRUE;
     CK_SESSION_HANDLE session;
     CK_RV crv;
 
     if (symKey == NULL) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
 
     /* if this slot doesn't support the mechanism, go to a slot that does */
-    newKey = pk11_ForceSlot(symKey, type, CKA_ENCRYPT);
+    newKey = pk11_ForceSlot(symKey, mechType, CKA_ENCRYPT);
     if (newKey != NULL) {
         symKey = newKey;
     }
 
     if (symKey->slot == NULL) {
         PORT_SetError(SEC_ERROR_NO_MODULE);
         return SECFailure;
     }
 
     slot = symKey->slot;
-    mechanism.mechanism = pk11_mapWrapKeyType(pubKey->keyType);
-    mechanism.pParameter = NULL;
-    mechanism.ulParameterLen = 0;
+
+    mechanism.mechanism = mechType;
+    if (param == NULL) {
+        mechanism.pParameter = NULL;
+        mechanism.ulParameterLen = 0;
+    } else {
+        mechanism.pParameter = param->data;
+        mechanism.ulParameterLen = param->len;
+    }
 
     id = PK11_ImportPublicKey(slot, pubKey, PR_FALSE);
     if (id == CK_INVALID_HANDLE) {
         if (newKey) {
             PK11_FreeSymKey(newKey);
         }
         return SECFailure; /* Error code has been set. */
     }
@@ -2878,30 +2894,43 @@ PK11_UnwrapSymKeyWithFlagsPerm(PK11SymKe
     templateCount = attrs - keyTemplate;
     templateCount += pk11_OpFlagsToAttributes(flags, attrs, &cktrue);
 
     return pk11_AnyUnwrapKey(wrappingKey->slot, wrappingKey->objectID,
                              wrapType, param, wrappedKey, target, operation, keySize,
                              wrappingKey->cx, keyTemplate, templateCount, isPerm);
 }
 
-/* unwrap a symetric key with a private key. */
+/* unwrap a symmetric key with a private key. Only supports CKM_RSA_PKCS. */
 PK11SymKey *
 PK11_PubUnwrapSymKey(SECKEYPrivateKey *wrappingKey, SECItem *wrappedKey,
                      CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize)
 {
     CK_MECHANISM_TYPE wrapType = pk11_mapWrapKeyType(wrappingKey->keyType);
+
+    return PK11_PubUnwrapSymKeyWithMechanism(wrappingKey, wrapType, NULL,
+                                             wrappedKey, target, operation,
+                                             keySize);
+}
+
+/* unwrap a symmetric key with a private key with the given parameters. */
+PK11SymKey *
+PK11_PubUnwrapSymKeyWithMechanism(SECKEYPrivateKey *wrappingKey,
+                                  CK_MECHANISM_TYPE mechType, SECItem *param,
+                                  SECItem *wrappedKey, CK_MECHANISM_TYPE target,
+                                  CK_ATTRIBUTE_TYPE operation, int keySize)
+{
     PK11SlotInfo *slot = wrappingKey->pkcs11Slot;
 
     if (SECKEY_HAS_ATTRIBUTE_SET(wrappingKey, CKA_PRIVATE)) {
         PK11_HandlePasswordCheck(slot, wrappingKey->wincx);
     }
 
-    return pk11_AnyUnwrapKey(slot, wrappingKey->pkcs11ID,
-                             wrapType, NULL, wrappedKey, target, operation, keySize,
+    return pk11_AnyUnwrapKey(slot, wrappingKey->pkcs11ID, mechType, param,
+                             wrappedKey, target, operation, keySize,
                              wrappingKey->wincx, NULL, 0, PR_FALSE);
 }
 
 /* unwrap a symetric key with a private key. */
 PK11SymKey *
 PK11_PubUnwrapSymKeyWithFlags(SECKEYPrivateKey *wrappingKey,
                               SECItem *wrappedKey, CK_MECHANISM_TYPE target,
                               CK_ATTRIBUTE_TYPE operation, int keySize, CK_FLAGS flags)