diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /security/nss/gtests/pk11_gtest/pk11_import_unittest.cc | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | security/nss/gtests/pk11_gtest/pk11_import_unittest.cc | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/security/nss/gtests/pk11_gtest/pk11_import_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_import_unittest.cc new file mode 100644 index 0000000000..7fcc1cc4d7 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_import_unittest.cc @@ -0,0 +1,283 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <memory> +#include "nss.h" +#include "pk11pub.h" +#include "pk11pqg.h" +#include "prerror.h" +#include "secoid.h" + +#include "cpputil.h" +#include "nss_scoped_ptrs.h" +#include "gtest/gtest.h" +#include "databuffer.h" +#include "pk11_keygen.h" + +namespace nss_test { + +// This deleter deletes a set of objects, unlike the deleter on +// ScopedPK11GenericObject, which only deletes one. +struct PK11GenericObjectsDeleter { + void operator()(PK11GenericObject* objs) { + if (objs) { + PK11_DestroyGenericObjects(objs); + } + } +}; + +class Pk11KeyImportTestBase : public ::testing::Test { + public: + Pk11KeyImportTestBase() = default; + virtual ~Pk11KeyImportTestBase() = default; + + void SetUp() override { + slot_.reset(PK11_GetInternalKeySlot()); + ASSERT_TRUE(slot_); + + static const uint8_t pw[] = "pw"; + SECItem pwItem = {siBuffer, toUcharPtr(pw), sizeof(pw)}; + password_.reset(SECITEM_DupItem(&pwItem)); + } + + void Test(const Pkcs11KeyPairGenerator& generator) { + // Generate a key and export it. + KeyType key_type = nullKey; + ScopedSECKEYEncryptedPrivateKeyInfo key_info; + ScopedSECItem public_value; + GenerateAndExport(generator, &key_type, &key_info, &public_value); + + // Note: NSS is currently unable export wrapped DH keys, so this doesn't + // test those beyond generate and verify. + if (key_type == dhKey) { + return; + } + ASSERT_NE(nullptr, public_value); + ASSERT_NE(nullptr, key_info); + + // Now import the encrypted key. + static const uint8_t nick[] = "nick"; + SECItem nickname = {siBuffer, toUcharPtr(nick), sizeof(nick)}; + SECKEYPrivateKey* priv_tmp; + SECStatus rv = PK11_ImportEncryptedPrivateKeyInfoAndReturnKey( + slot_.get(), key_info.get(), password_.get(), &nickname, + public_value.get(), PR_TRUE, PR_TRUE, key_type, 0, &priv_tmp, NULL); + ASSERT_EQ(SECSuccess, rv) << "PK11_ImportEncryptedPrivateKeyInfo failed " + << PORT_ErrorToName(PORT_GetError()); + ScopedSECKEYPrivateKey priv_key(priv_tmp); + ASSERT_NE(nullptr, priv_key); + + CheckForPublicKey(priv_key, public_value.get()); + } + + private: + SECItem GetPublicComponent(ScopedSECKEYPublicKey& pub_key) { + SECItem null = {siBuffer, NULL, 0}; + switch (SECKEY_GetPublicKeyType(pub_key.get())) { + case rsaKey: + case rsaPssKey: + case rsaOaepKey: + return pub_key->u.rsa.modulus; + case keaKey: + return pub_key->u.kea.publicValue; + case dsaKey: + return pub_key->u.dsa.publicValue; + case dhKey: + return pub_key->u.dh.publicValue; + case ecKey: + return pub_key->u.ec.publicValue; + case kyberKey: + return pub_key->u.kyber.publicValue; + case fortezzaKey: /* depricated */ + case nullKey: + /* didn't use default here so we can catch new key types at compile time + */ + break; + } + return null; + } + void CheckForPublicKey(const ScopedSECKEYPrivateKey& priv_key, + const SECItem* expected_public) { + // Verify the public key exists. + StackSECItem priv_id; + KeyType type = SECKEY_GetPrivateKeyType(priv_key.get()); + SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, priv_key.get(), + CKA_ID, &priv_id); + ASSERT_EQ(SECSuccess, rv) << "Couldn't read CKA_ID from private key: " + << PORT_ErrorToName(PORT_GetError()); + + CK_ATTRIBUTE_TYPE value_type = CKA_VALUE; + switch (type) { + case rsaKey: + value_type = CKA_MODULUS; + break; + + case dhKey: + case dsaKey: + value_type = CKA_VALUE; + break; + + case ecKey: + value_type = CKA_EC_POINT; + break; + + default: + FAIL() << "unknown key type"; + } + + // Scan public key objects until we find one with the same CKA_ID as + // priv_key + std::unique_ptr<PK11GenericObject, PK11GenericObjectsDeleter> objs( + PK11_FindGenericObjects(slot_.get(), CKO_PUBLIC_KEY)); + ASSERT_NE(nullptr, objs); + for (PK11GenericObject* obj = objs.get(); obj != nullptr; + obj = PK11_GetNextGenericObject(obj)) { + StackSECItem pub_id; + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_ID, &pub_id); + if (rv != SECSuccess) { + // Can't read CKA_ID from object. + continue; + } + if (!SECITEM_ItemsAreEqual(&priv_id, &pub_id)) { + // This isn't the object we're looking for. + continue; + } + + StackSECItem token; + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_TOKEN, &token); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(1U, token.len); + ASSERT_NE(0, token.data[0]); + + StackSECItem raw_value; + SECItem decoded_value; + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, value_type, &raw_value); + ASSERT_EQ(SECSuccess, rv); + SECItem value = raw_value; + + // Decode the EC_POINT and check the output against expected. + // CKA_EC_POINT isn't stable, see Bug 1520649. + ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + ASSERT_TRUE(arena); + if (value_type == CKA_EC_POINT) { + // If this fails due to the noted inconsistency, we may need to + // check the whole raw_value, or remove a leading UNCOMPRESSED_POINT tag + rv = SEC_QuickDERDecodeItem(arena.get(), &decoded_value, + SEC_ASN1_GET(SEC_OctetStringTemplate), + &raw_value); + ASSERT_EQ(SECSuccess, rv); + value = decoded_value; + } + ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &value)) + << "expected: " + << DataBuffer(expected_public->data, expected_public->len) + << std::endl + << "actual: " << DataBuffer(value.data, value.len) << std::endl; + + // Finally, convert the private to public and ensure it matches. + ScopedSECKEYPublicKey pub_key(SECKEY_ConvertToPublicKey(priv_key.get())); + ASSERT_TRUE(pub_key); + SECItem converted_public = GetPublicComponent(pub_key); + ASSERT_TRUE(converted_public.len != 0); + + ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &converted_public)) + << "expected: " + << DataBuffer(expected_public->data, expected_public->len) + << std::endl + << "actual: " + << DataBuffer(converted_public.data, converted_public.len) + << std::endl; + } + } + + void GenerateAndExport(const Pkcs11KeyPairGenerator& generator, + KeyType* key_type, + ScopedSECKEYEncryptedPrivateKeyInfo* key_info, + ScopedSECItem* public_value) { + ScopedSECKEYPrivateKey priv_key; + ScopedSECKEYPublicKey pub_key; + generator.GenerateKey(&priv_key, &pub_key); + ASSERT_TRUE(priv_key); + + // Save the public value, which we will need on import */ + SECItem* pub_val; + KeyType t = SECKEY_GetPublicKeyType(pub_key.get()); + switch (t) { + case rsaKey: + pub_val = &pub_key->u.rsa.modulus; + break; + case dhKey: + pub_val = &pub_key->u.dh.publicValue; + break; + case dsaKey: + pub_val = &pub_key->u.dsa.publicValue; + break; + case ecKey: + pub_val = &pub_key->u.ec.publicValue; + break; + default: + FAIL() << "Unknown key type"; + } + + CheckForPublicKey(priv_key, pub_val); + + *key_type = t; + // Note: NSS is currently unable export wrapped DH keys, so this doesn't + // test those beyond generate and verify. + if (t == dhKey) { + return; + } + public_value->reset(SECITEM_DupItem(pub_val)); + + // Wrap and export the key. + ScopedSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo( + slot_.get(), SEC_OID_AES_256_CBC, password_.get(), priv_key.get(), 1, + nullptr)); + ASSERT_NE(nullptr, epki) << "PK11_ExportEncryptedPrivKeyInfo failed: " + << PORT_ErrorToName(PORT_GetError()); + + key_info->swap(epki); + } + + ScopedPK11SlotInfo slot_; + ScopedSECItem password_; +}; + +class Pk11KeyImportTest + : public Pk11KeyImportTestBase, + public ::testing::WithParamInterface<CK_MECHANISM_TYPE> { + public: + Pk11KeyImportTest() = default; + virtual ~Pk11KeyImportTest() = default; +}; + +TEST_P(Pk11KeyImportTest, GenerateExportImport) { + Test(Pkcs11KeyPairGenerator(GetParam())); +} + +INSTANTIATE_TEST_SUITE_P(Pk11KeyImportTest, Pk11KeyImportTest, + ::testing::Values(CKM_RSA_PKCS_KEY_PAIR_GEN, + CKM_DSA_KEY_PAIR_GEN, + CKM_DH_PKCS_KEY_PAIR_GEN)); + +class Pk11KeyImportTestEC : public Pk11KeyImportTestBase, + public ::testing::WithParamInterface<SECOidTag> { + public: + Pk11KeyImportTestEC() = default; + virtual ~Pk11KeyImportTestEC() = default; +}; + +TEST_P(Pk11KeyImportTestEC, GenerateExportImport) { + Test(Pkcs11KeyPairGenerator(CKM_EC_KEY_PAIR_GEN, GetParam())); +} + +INSTANTIATE_TEST_SUITE_P(Pk11KeyImportTestEC, Pk11KeyImportTestEC, + ::testing::Values(SEC_OID_SECG_EC_SECP256R1, + SEC_OID_SECG_EC_SECP384R1, + SEC_OID_SECG_EC_SECP521R1, + SEC_OID_CURVE25519)); + +} // namespace nss_test |