Skip to content

Commit

Permalink
Bug 1667153 - Add PK11_ImportDataKey API. r=rrelyea
Browse files Browse the repository at this point in the history
This patch adds and exports `PK11_ImportDataKey`, and refactors the null PSK TLS 1.3 code to use it.

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

--HG--
extra : moz-landing-system : lando
  • Loading branch information
Kevin Jacobs committed Sep 24, 2020
1 parent 33c0a6a commit 1f3746f
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 75 deletions.
4 changes: 4 additions & 0 deletions automation/abi-check/expected-report-libnss3.so.txt
@@ -0,0 +1,4 @@

1 Added function:

[A] 'function PK11SymKey* PK11_ImportDataKey(PK11SlotInfo*, CK_MECHANISM_TYPE, PK11Origin, CK_ATTRIBUTE_TYPE, SECItem*, void*)' {PK11_ImportDataKey@@NSS_3.58}
88 changes: 48 additions & 40 deletions gtests/pk11_gtest/pk11_hkdf_unittest.cc
Expand Up @@ -26,7 +26,12 @@ const int kPkcs11HkdfSaltDerive = 3;
const int kPkcs11HkdfData = 4;
const int kPKCS11NumTypes = 5;

class Pkcs11HkdfTest : public ::testing::TestWithParam<hkdf_vector> {
enum class Pk11ImportType { data = 0, key = 1 };
static const Pk11ImportType kImportTypesAll[] = {Pk11ImportType::data,
Pk11ImportType::key};

class Pkcs11HkdfTest
: public ::testing::TestWithParam<std::tuple<hkdf_vector, Pk11ImportType>> {
protected:
CK_MECHANISM_TYPE Pk11HkdfToHash(CK_MECHANISM_TYPE nssHkdfMech) {
switch (nssHkdfMech) {
Expand All @@ -45,14 +50,15 @@ class Pkcs11HkdfTest : public ::testing::TestWithParam<hkdf_vector> {
return CKM_INVALID_MECHANISM;
}

ScopedPK11SymKey ImportKey(CK_KEY_TYPE keyType, SECItem *ikm_item) {
ScopedPK11SymKey ImportKey(CK_KEY_TYPE key_type, SECItem *ikm_item,
Pk11ImportType import_type) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
CK_MECHANISM_TYPE mech = CKM_HKDF_KEY_GEN;
if (!slot) {
ADD_FAILURE() << "Can't get slot";
return nullptr;
}
switch (keyType) {
switch (key_type) {
case CKK_GENERIC_SECRET:
mech = CKM_GENERIC_SECRET_KEY_GEN;
break;
Expand All @@ -61,13 +67,21 @@ class Pkcs11HkdfTest : public ::testing::TestWithParam<hkdf_vector> {
break;
}

ScopedPK11SymKey ikm(PK11_ImportSymKey(slot.get(), mech, PK11_OriginUnwrap,
CKA_SIGN, ikm_item, nullptr));
ScopedPK11SymKey ikm;

if (import_type == Pk11ImportType::key) {
ikm.reset(PK11_ImportSymKey(slot.get(), mech, PK11_OriginUnwrap, CKA_SIGN,
ikm_item, nullptr));
} else {
ikm.reset(PK11_ImportDataKey(slot.get(), mech, PK11_OriginUnwrap,
CKA_SIGN, ikm_item, nullptr));
}

return ikm;
}

void RunTest(hkdf_vector vec, HkdfTestType type, CK_KEY_TYPE keyType) {
void RunTest(hkdf_vector vec, HkdfTestType test_type, CK_KEY_TYPE key_type,
Pk11ImportType import_type) {
SECItem ikm_item = {siBuffer, vec.ikm.data(),
static_cast<unsigned int>(vec.ikm.size())};
SECItem salt_item = {siBuffer, vec.salt.data(),
Expand All @@ -88,50 +102,40 @@ class Pkcs11HkdfTest : public ::testing::TestWithParam<hkdf_vector> {
SECItem params_item = {siBuffer, (unsigned char *)&nss_hkdf_params,
sizeof(nss_hkdf_params)};

ScopedPK11SymKey ikm = ImportKey(keyType, &ikm_item);
ScopedPK11SymKey ikm = ImportKey(key_type, &ikm_item, import_type);
ScopedPK11SymKey salt_key = NULL;
ASSERT_NE(nullptr, ikm.get());

switch (type) {
switch (test_type) {
case kNSSHkdfLegacy:
printf("kNSSHkdfLegacy\n");
break; /* already set up */
case kPkcs11HkdfDeriveDataKey: {
ScopedPK11SlotInfo slot(PK11_GetSlotFromKey(ikm.get()));
CK_OBJECT_CLASS ckoData = CKO_DATA;
CK_OBJECT_HANDLE handle;
CK_ATTRIBUTE pk11template[2] = {
{CKA_CLASS, (CK_BYTE_PTR)&ckoData, sizeof(ckoData)},
{CKA_VALUE, vec.ikm.data(), static_cast<CK_ULONG>(vec.ikm.size())}};

ScopedPK11GenericObject dataKey(PK11_CreateGenericObject(
slot.get(), pk11template, PR_ARRAY_SIZE(pk11template), PR_FALSE));
ASSERT_NE(nullptr, dataKey.get());
handle = PK11_GetObjectHandle(PK11_TypeGeneric, dataKey.get(), NULL);
ASSERT_NE((CK_ULONG)CK_INVALID_HANDLE, (CK_ULONG)handle);
/* replaces old key with our new data key */
ikm = ScopedPK11SymKey(
PK11_SymKeyFromHandle(slot.get(), NULL, PK11_OriginUnwrap,
CKM_HKDF_DERIVE, handle, PR_TRUE, NULL));
SECItem data_item = {siBuffer, vec.ikm.data(),
static_cast<unsigned int>(vec.ikm.size())};
ikm = ScopedPK11SymKey(PK11_ImportDataKey(slot.get(), CKM_HKDF_DERIVE,
PK11_OriginUnwrap, CKA_DERIVE,
&data_item, NULL));
ASSERT_NE(nullptr, ikm.get());
/* generic object is freed, ikm owns the handle */
}
/* fall through */
case kPkcs11HkdfSaltDerive:
case kPkcs11HkdfDerive:
if (hkdf_params.ulSaltLen == 0) {
hkdf_params.ulSaltType = CKF_HKDF_SALT_NULL;
printf("kPkcs11HkdfNullSaltDerive\n");
} else if (type == kPkcs11HkdfSaltDerive) {
salt_key = ImportKey(keyType, &salt_item);
} else if (test_type == kPkcs11HkdfSaltDerive) {
salt_key = ImportKey(key_type, &salt_item, import_type);
hkdf_params.ulSaltType = CKF_HKDF_SALT_KEY;
hkdf_params.ulSaltLen = 0;
hkdf_params.pSalt = NULL;
hkdf_params.hSaltKey = PK11_GetSymKeyHandle(salt_key.get());
printf("kPkcs11HkdfSaltDerive\n");
} else {
printf("kPkcs11HkdfDerive%s\n",
(type == kPkcs11HkdfDeriveDataKey) ? "DataKey" : "");
(test_type == kPkcs11HkdfDeriveDataKey) ? "DataKey" : "");
}
hkdf_params.prfHashMechanism = Pk11HkdfToHash(vec.mech);
params_item.data = (unsigned char *)&hkdf_params;
Expand Down Expand Up @@ -166,23 +170,27 @@ class Pkcs11HkdfTest : public ::testing::TestWithParam<hkdf_vector> {
ASSERT_EQ(nullptr, okm.get());
}
}
void RunTest(hkdf_vector vec) {
void RunTest(hkdf_vector vec, Pk11ImportType import_type) {
HkdfTestType test_type;

for (test_type = kNSSHkdfLegacy; test_type < kPKCS11NumTypes; test_type++) {
RunTest(vec, test_type, CKK_GENERIC_SECRET);
RunTest(vec, test_type, CKK_GENERIC_SECRET, import_type);
if (test_type == kPkcs11HkdfDeriveDataKey) {
continue;
}
RunTest(vec, test_type, CKK_HKDF);
RunTest(vec, test_type, CKK_HKDF, import_type);
}
}
};

TEST_P(Pkcs11HkdfTest, TestVectors) { RunTest(GetParam()); }
TEST_P(Pkcs11HkdfTest, TestVectors) {
RunTest(std::get<0>(GetParam()), std::get<1>(GetParam()));
}

INSTANTIATE_TEST_CASE_P(Pkcs11HkdfTests, Pkcs11HkdfTest,
::testing::ValuesIn(kHkdfTestVectors));
INSTANTIATE_TEST_CASE_P(
Pkcs11HkdfTests, Pkcs11HkdfTest,
::testing::Combine(::testing::ValuesIn(kHkdfTestVectors),
::testing::ValuesIn(kImportTypesAll)));

TEST_F(Pkcs11HkdfTest, OkmLimits) {
hkdf_vector vector{
Expand All @@ -198,44 +206,44 @@ TEST_F(Pkcs11HkdfTest, OkmLimits) {
};

// SHA1 limit
RunTest(vector);
RunTest(vector, Pk11ImportType::key);

// SHA1 limit + 1
vector.l += 1;
vector.res.expect_rv = SECFailure;
RunTest(vector);
RunTest(vector, Pk11ImportType::key);

// SHA256 limit
vector.mech = CKM_NSS_HKDF_SHA256;
vector.l = 255 * SHA256_LENGTH; /* per rfc5869 */
vector.res.expect_rv = SECSuccess;
RunTest(vector);
RunTest(vector, Pk11ImportType::data);

// SHA256 limit + 1
vector.l += 1;
vector.res.expect_rv = SECFailure;
RunTest(vector);
RunTest(vector, Pk11ImportType::data);

// SHA384 limit
vector.mech = CKM_NSS_HKDF_SHA384;
vector.l = 255 * SHA384_LENGTH; /* per rfc5869 */
vector.res.expect_rv = SECSuccess;
RunTest(vector);
RunTest(vector, Pk11ImportType::key);

// SHA384 limit + 1
vector.l += 1;
vector.res.expect_rv = SECFailure;
RunTest(vector);
RunTest(vector, Pk11ImportType::key);

// SHA512 limit
vector.mech = CKM_NSS_HKDF_SHA512;
vector.l = 255 * SHA512_LENGTH; /* per rfc5869 */
vector.res.expect_rv = SECSuccess;
RunTest(vector);
RunTest(vector, Pk11ImportType::data);

// SHA512 limit + 1
vector.l += 1;
vector.res.expect_rv = SECFailure;
RunTest(vector);
RunTest(vector, Pk11ImportType::data);
}
} // namespace nss_test
6 changes: 6 additions & 0 deletions lib/nss/nss.def
Expand Up @@ -1187,3 +1187,9 @@ PK11_FindEncodedCertInSlot;
;+ local:
;+ *;
;+};
;+NSS_3.58 { # NSS 3.58 release
;+ global:
PK11_ImportDataKey;
;+ local:
;+ *;
;+};
2 changes: 2 additions & 0 deletions lib/pk11wrap/pk11pub.h
Expand Up @@ -267,6 +267,8 @@ CK_MECHANISM_TYPE PK11_MapSignKeyType(KeyType keyType);
**********************************************************************/
void PK11_FreeSymKey(PK11SymKey *key);
PK11SymKey *PK11_ReferenceSymKey(PK11SymKey *symKey);
PK11SymKey *PK11_ImportDataKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, PK11Origin origin,
CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx);
PK11SymKey *PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx);
PK11SymKey *PK11_ImportSymKeyWithFlags(PK11SlotInfo *slot,
Expand Down
33 changes: 30 additions & 3 deletions lib/pk11wrap/pk11skey.c
Expand Up @@ -506,10 +506,37 @@ PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
keyTemplate, templateCount, key, wincx);
return symKey;
}
/* Import a PKCS #11 data object and return it as a key. This key is
* only useful in a limited number of mechanisms, such as HKDF. */
PK11SymKey *
PK11_ImportDataKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, PK11Origin origin,
CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx)
{
CK_OBJECT_CLASS ckoData = CKO_DATA;
CK_ATTRIBUTE template[2] = { { CKA_CLASS, (CK_BYTE_PTR)&ckoData, sizeof(ckoData) },
{ CKA_VALUE, (CK_BYTE_PTR)key->data, key->len } };
CK_OBJECT_HANDLE handle;
PK11GenericObject *genObject;

/*
* turn key bits into an appropriate key object
*/
genObject = PK11_CreateGenericObject(slot, template, PR_ARRAY_SIZE(template), PR_FALSE);
if (genObject == NULL) {
return NULL;
}
handle = PK11_GetObjectHandle(PK11_TypeGeneric, genObject, NULL);
if (handle == CK_INVALID_HANDLE) {
return NULL;
}
/* A note about ownership of the PKCS #11 handle:
* PK11_CreateGenericObject() will not destroy the object it creates
* on Free, For that you want PK11_CreateManagedGenericObject().
* Below we import the handle into the symKey structure. We pass
* PR_TRUE as the owner so that the symKey will destroy the object
* once it's freed. This is way it's safe to free now. */
PK11_DestroyGenericObject(genObject);
return PK11_SymKeyFromHandle(slot, NULL, origin, type, handle, PR_TRUE, wincx);
}

/* turn key bits into an appropriate key object */
PK11SymKey *
PK11_ImportSymKeyWithFlags(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key,
Expand Down
35 changes: 3 additions & 32 deletions lib/ssl/tls13hkdf.c
Expand Up @@ -38,6 +38,7 @@ tls13_HkdfExtract(PK11SymKey *ikm1, PK11SymKey *ikm2, SSLHashType baseHash,
SECItem paramsi;
PK11SymKey *prk;
static const PRUint8 zeroKeyBuf[HASH_LENGTH_MAX];
SECItem zeroKeyItem = { siBuffer, CONST_CAST(PRUint8, zeroKeyBuf), kTlsHkdfInfo[baseHash].hashSize };
PK11SlotInfo *slot = NULL;
PK11SymKey *newIkm2 = NULL;
PK11SymKey *newIkm1 = NULL;
Expand Down Expand Up @@ -102,44 +103,14 @@ tls13_HkdfExtract(PK11SymKey *ikm1, PK11SymKey *ikm2, SSLHashType baseHash,

/* A zero ikm2 is a key of hash-length 0s. */
if (!ikm2) {
CK_OBJECT_CLASS ckoData = CKO_DATA;
CK_ATTRIBUTE template[2] = {
{ CKA_CLASS, (CK_BYTE_PTR)&ckoData, sizeof(ckoData) },
{ CKA_VALUE, (CK_BYTE_PTR)zeroKeyBuf, kTlsHkdfInfo[baseHash].hashSize }
};
CK_OBJECT_HANDLE handle;
PK11GenericObject *genObject;

/* if we have ikm1, put the zero key in the same slot */
slot = ikm1 ? PK11_GetSlotFromKey(ikm1) : PK11_GetBestSlot(CKM_HKDF_DERIVE, NULL);
if (!slot) {
return SECFailure;
}
genObject = PK11_CreateGenericObject(slot, template,
PR_ARRAY_SIZE(template), PR_FALSE);
if (genObject == NULL) {
return SECFailure;
}
handle = PK11_GetObjectHandle(PK11_TypeGeneric, genObject, NULL);
if (handle == CK_INVALID_HANDLE) {
return SECFailure;
}
/* A note about ownership of the PKCS #11 handle.
* PK11_CreateGenericObject() will not destroy the object it creates
* on Free, For that you want PK11_CreateManagedGenericObject().
* Below we import the handle into the symKey structure. We pass
* PR_TRUE as the owner so that the symKey will destroy the object
* once it's freed. This is way it's safe to free now */
PK11_DestroyGenericObject(genObject);

/* NOTE: we can't both be in this block, and the block above where
* we moved the keys (because this block requires ikm2 to be NULL and
* the other requires ikm2 to be non-NULL. It's therefore safe to
* use newIkm2 in both cases and have a single free at the end for
* both */
PORT_Assert(newIkm2 == NULL); /* verify logic of the above statement */
newIkm2 = PK11_SymKeyFromHandle(slot, NULL, PK11_OriginUnwrap,
CKM_HKDF_DERIVE, handle, PR_TRUE, NULL);
newIkm2 = PK11_ImportDataKey(slot, CKM_HKDF_DERIVE, PK11_OriginUnwrap,
CKA_DERIVE, &zeroKeyItem, NULL);
if (!newIkm2) {
return SECFailure;
}
Expand Down

0 comments on commit 1f3746f

Please sign in to comment.