# HG changeset patch # User Robert Relyea # 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 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, ¶m, + 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, ¶m, 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)