diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc')
-rw-r--r-- | security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc new file mode 100644 index 0000000000..cf35958d92 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc @@ -0,0 +1,329 @@ +/* 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 "sechash.h" +#include "cryptohi.h" + +#include "cpputil.h" +#include "gtest/gtest.h" +#include "json_reader.h" +#include "nss_scoped_ptrs.h" +#include "testvectors/curve25519-vectors.h" + +#include "pk11_ecdsa_vectors.h" +#include "pk11_signature_test.h" +#include "pk11_keygen.h" + +namespace nss_test { + +CK_MECHANISM_TYPE +EcHashToComboMech(SECOidTag hash) { + switch (hash) { + case SEC_OID_SHA1: + return CKM_ECDSA_SHA1; + case SEC_OID_SHA224: + return CKM_ECDSA_SHA224; + case SEC_OID_SHA256: + return CKM_ECDSA_SHA256; + case SEC_OID_SHA384: + return CKM_ECDSA_SHA384; + case SEC_OID_SHA512: + return CKM_ECDSA_SHA512; + default: + break; + } + return CKM_INVALID_MECHANISM; +} + +class Pkcs11EcdsaTestBase : public Pk11SignatureTest { + protected: + Pkcs11EcdsaTestBase(SECOidTag hash_oid) + : Pk11SignatureTest(CKM_ECDSA, hash_oid, EcHashToComboMech(hash_oid)) {} +}; + +struct Pkcs11EcdsaTestParams { + SECOidTag hash_oid_; + Pkcs11SignatureTestParams sig_params_; +}; + +class Pkcs11EcdsaTest + : public Pkcs11EcdsaTestBase, + public ::testing::WithParamInterface<Pkcs11EcdsaTestParams> { + public: + Pkcs11EcdsaTest() : Pkcs11EcdsaTestBase(GetParam().hash_oid_) {} +}; + +TEST_P(Pkcs11EcdsaTest, Verify) { Verify(GetParam().sig_params_); } + +TEST_P(Pkcs11EcdsaTest, SignAndVerify) { + SignAndVerify(GetParam().sig_params_); +} + +TEST_P(Pkcs11EcdsaTest, ImportExport) { + ImportExport(GetParam().sig_params_.pkcs8_); +} + +static const Pkcs11EcdsaTestParams kEcdsaVectors[] = { + {SEC_OID_SHA256, + {DataBuffer(kP256Pkcs8, sizeof(kP256Pkcs8)), + DataBuffer(kP256Spki, sizeof(kP256Spki)), + DataBuffer(kP256Data, sizeof(kP256Data)), + DataBuffer(kP256Signature, sizeof(kP256Signature))}}, + {SEC_OID_SHA256, + {DataBuffer(kP256Pkcs8ZeroPad, sizeof(kP256Pkcs8ZeroPad)), + DataBuffer(kP256SpkiZeroPad, sizeof(kP256SpkiZeroPad)), + DataBuffer(kP256DataZeroPad, sizeof(kP256DataZeroPad)), + DataBuffer(kP256SignatureZeroPad, sizeof(kP256SignatureZeroPad))}}, + {SEC_OID_SHA384, + {DataBuffer(kP384Pkcs8, sizeof(kP384Pkcs8)), + DataBuffer(kP384Spki, sizeof(kP384Spki)), + DataBuffer(kP384Data, sizeof(kP384Data)), + DataBuffer(kP384Signature, sizeof(kP384Signature))}}, + {SEC_OID_SHA512, + {DataBuffer(kP521Pkcs8, sizeof(kP521Pkcs8)), + DataBuffer(kP521Spki, sizeof(kP521Spki)), + DataBuffer(kP521Data, sizeof(kP521Data)), + DataBuffer(kP521Signature, sizeof(kP521Signature))}}}; + +INSTANTIATE_TEST_SUITE_P(EcdsaSignVerify, Pkcs11EcdsaTest, + ::testing::ValuesIn(kEcdsaVectors)); + +class Pkcs11EcdsaSha256Test : public Pkcs11EcdsaTestBase { + public: + Pkcs11EcdsaSha256Test() : Pkcs11EcdsaTestBase(SEC_OID_SHA256) {} +}; + +// Importing a private key in PKCS#8 format must fail when the outer AlgID +// struct contains neither id-ecPublicKey nor a namedCurve parameter. +TEST_F(Pkcs11EcdsaSha256Test, ImportNoCurveOIDOrAlgorithmParams) { + DataBuffer k(kP256Pkcs8NoCurveOIDOrAlgorithmParams, + sizeof(kP256Pkcs8NoCurveOIDOrAlgorithmParams)); + EXPECT_FALSE(ImportPrivateKey(k)); +}; + +// Importing a private key in PKCS#8 format must succeed when only the outer +// AlgID struct contains the namedCurve parameters. +TEST_F(Pkcs11EcdsaSha256Test, ImportOnlyAlgorithmParams) { + DataBuffer k(kP256Pkcs8OnlyAlgorithmParams, + sizeof(kP256Pkcs8OnlyAlgorithmParams)); + DataBuffer data(kP256Data, sizeof(kP256Data)); + DataBuffer sig; + DataBuffer sig2; + EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig, &sig2)); +}; + +// Importing a private key in PKCS#8 format must succeed when the outer AlgID +// struct and the inner ECPrivateKey contain the same namedCurve parameters. +// The inner curveOID is always ignored, so only the outer one will be used. +TEST_F(Pkcs11EcdsaSha256Test, ImportMatchingCurveOIDAndAlgorithmParams) { + DataBuffer k(kP256Pkcs8MatchingCurveOIDAndAlgorithmParams, + sizeof(kP256Pkcs8MatchingCurveOIDAndAlgorithmParams)); + DataBuffer data(kP256Data, sizeof(kP256Data)); + DataBuffer sig; + DataBuffer sig2; + EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig, &sig2)); +}; + +// Importing a private key in PKCS#8 format must succeed when the outer AlgID +// struct and the inner ECPrivateKey contain dissimilar namedCurve parameters. +// The inner curveOID is always ignored, so only the outer one will be used. +TEST_F(Pkcs11EcdsaSha256Test, ImportDissimilarCurveOIDAndAlgorithmParams) { + DataBuffer k(kP256Pkcs8DissimilarCurveOIDAndAlgorithmParams, + sizeof(kP256Pkcs8DissimilarCurveOIDAndAlgorithmParams)); + DataBuffer data(kP256Data, sizeof(kP256Data)); + DataBuffer sig; + DataBuffer sig2; + EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig, &sig2)); +}; + +// Importing a private key in PKCS#8 format must fail when the outer ASN.1 +// AlgorithmID struct contains only id-ecPublicKey but no namedCurve parameter. +TEST_F(Pkcs11EcdsaSha256Test, ImportNoAlgorithmParams) { + DataBuffer k(kP256Pkcs8NoAlgorithmParams, + sizeof(kP256Pkcs8NoAlgorithmParams)); + EXPECT_FALSE(ImportPrivateKey(k)); +}; + +// Importing a private key in PKCS#8 format must fail when id-ecPublicKey is +// given (so we know it's an EC key) but the namedCurve parameter is unknown. +TEST_F(Pkcs11EcdsaSha256Test, ImportInvalidAlgorithmParams) { + DataBuffer k(kP256Pkcs8InvalidAlgorithmParams, + sizeof(kP256Pkcs8InvalidAlgorithmParams)); + EXPECT_FALSE(ImportPrivateKey(k)); +}; + +// Importing a private key in PKCS#8 format with a point not on the curve will +// succeed. Using the contained public key however will fail when trying to +// import it before using it for any operation. +TEST_F(Pkcs11EcdsaSha256Test, ImportPointNotOnCurve) { + DataBuffer k(kP256Pkcs8PointNotOnCurve, sizeof(kP256Pkcs8PointNotOnCurve)); + ScopedSECKEYPrivateKey privKey(ImportPrivateKey(k)); + ASSERT_TRUE(privKey); + + ScopedSECKEYPublicKey pubKey(SECKEY_ConvertToPublicKey(privKey.get())); + ASSERT_TRUE(pubKey); + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_TRUE(slot); + + auto handle = PK11_ImportPublicKey(slot.get(), pubKey.get(), false); + EXPECT_EQ(handle, static_cast<decltype(handle)>(CK_INVALID_HANDLE)); +}; + +// Importing a private key in PKCS#8 format must fail when no point is given. +// PK11 currently offers no APIs to derive raw public keys from private values. +TEST_F(Pkcs11EcdsaSha256Test, ImportNoPublicKey) { + DataBuffer k(kP256Pkcs8NoPublicKey, sizeof(kP256Pkcs8NoPublicKey)); + EXPECT_FALSE(ImportPrivateKey(k)); +}; + +// Importing a public key in SPKI format must fail when id-ecPublicKey is +// given (so we know it's an EC key) but the namedCurve parameter is missing. +TEST_F(Pkcs11EcdsaSha256Test, ImportSpkiNoAlgorithmParams) { + DataBuffer k(kP256SpkiNoAlgorithmParams, sizeof(kP256SpkiNoAlgorithmParams)); + EXPECT_FALSE(ImportPublicKey(k)); +} + +// Importing a public key in SPKI format with a point not on the curve will +// succeed. Using the public key however will fail when trying to import +// it before using it for any operation. +TEST_F(Pkcs11EcdsaSha256Test, ImportSpkiPointNotOnCurve) { + DataBuffer k(kP256SpkiPointNotOnCurve, sizeof(kP256SpkiPointNotOnCurve)); + ScopedSECKEYPublicKey pubKey(ImportPublicKey(k)); + ASSERT_TRUE(pubKey); + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_TRUE(slot); + + auto handle = PK11_ImportPublicKey(slot.get(), pubKey.get(), false); + EXPECT_EQ(handle, static_cast<decltype(handle)>(CK_INVALID_HANDLE)); +} + +class Pkcs11EcdsaWycheproofTest : public ::testing::Test { + protected: + void Run(const std::string& name) { + WycheproofHeader(name, "ECDSA", "ecdsa_verify_schema.json", + [this](JsonReader& r) { RunGroup(r); }); + } + + private: + void RunGroup(JsonReader& r) { + std::vector<EcdsaTestVector> tests; + std::vector<uint8_t> public_key; + SECOidTag hash_oid = SEC_OID_UNKNOWN; + + while (r.NextItem()) { + std::string n = r.ReadLabel(); + if (n == "") { + break; + } + + if (n == "key" || n == "keyPem") { + r.SkipValue(); + } else if (n == "keyDer") { + public_key = r.ReadHex(); + } else if (n == "sha") { + hash_oid = r.ReadHash(); + } else if (n == "type") { + ASSERT_EQ("EcdsaVerify", r.ReadString()); + } else if (n == "tests") { + WycheproofReadTests(r, &tests, ReadTestAttr); + } else { + FAIL() << "unknown label in group: " << n; + } + } + + for (auto& t : tests) { + std::cout << "Running test " << t.id << std::endl; + t.public_key = public_key; + t.hash_oid = hash_oid; + Derive(t); + } + } + + static void ReadTestAttr(EcdsaTestVector& t, const std::string& n, + JsonReader& r) { + if (n == "msg") { + t.msg = r.ReadHex(); + } else if (n == "sig") { + t.sig = r.ReadHex(); + } else { + FAIL() << "unknown test key: " << n; + } + } + + void Derive(const EcdsaTestVector& vec) { + SECItem spki_item = {siBuffer, toUcharPtr(vec.public_key.data()), + static_cast<unsigned int>(vec.public_key.size())}; + SECItem sig_item = {siBuffer, toUcharPtr(vec.sig.data()), + static_cast<unsigned int>(vec.sig.size())}; + + DataBuffer hash; + hash.Allocate(static_cast<size_t>(HASH_ResultLenByOidTag(vec.hash_oid))); + SECStatus rv = PK11_HashBuf(vec.hash_oid, toUcharPtr(hash.data()), + toUcharPtr(vec.msg.data()), vec.msg.size()); + ASSERT_EQ(rv, SECSuccess); + SECItem hash_item = {siBuffer, toUcharPtr(hash.data()), + static_cast<unsigned int>(hash.len())}; + + ScopedCERTSubjectPublicKeyInfo cert_spki( + SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); + ASSERT_TRUE(cert_spki); + ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get())); + ASSERT_TRUE(pub_key); + + rv = VFY_VerifyDigestDirect(&hash_item, pub_key.get(), &sig_item, + SEC_OID_ANSIX962_EC_PUBLIC_KEY, vec.hash_oid, + nullptr); + EXPECT_EQ(rv, vec.valid ? SECSuccess : SECFailure); + }; +}; + +TEST_F(Pkcs11EcdsaWycheproofTest, P256) { Run("ecdsa_secp256r1_sha256"); } +TEST_F(Pkcs11EcdsaWycheproofTest, P256Sha512) { Run("ecdsa_secp256r1_sha512"); } +TEST_F(Pkcs11EcdsaWycheproofTest, P384) { Run("ecdsa_secp384r1_sha384"); } +TEST_F(Pkcs11EcdsaWycheproofTest, P384Sha512) { Run("ecdsa_secp384r1_sha512"); } +TEST_F(Pkcs11EcdsaWycheproofTest, P521) { Run("ecdsa_secp521r1_sha512"); } + +class Pkcs11EcdsaRoundtripTest + : public Pkcs11EcdsaTestBase, + public ::testing::WithParamInterface<SECOidTag> { + public: + Pkcs11EcdsaRoundtripTest() : Pkcs11EcdsaTestBase(SEC_OID_SHA256) {} + + protected: + void GenerateExportImportSignVerify(SECOidTag tag) { + Pkcs11KeyPairGenerator generator(CKM_EC_KEY_PAIR_GEN, tag); + ScopedSECKEYPrivateKey priv; + ScopedSECKEYPublicKey pub; + generator.GenerateKey(&priv, &pub, false); + + DataBuffer exported; + ExportPrivateKey(&priv, exported); + + if (tag != SEC_OID_CURVE25519) { + DataBuffer sig; + DataBuffer sig2; + DataBuffer data(kP256Data, sizeof(kP256Data)); + ASSERT_TRUE( + ImportPrivateKeyAndSignHashedData(exported, data, &sig, &sig2)); + + Verify(pub, data, sig); + } + } +}; + +TEST_P(Pkcs11EcdsaRoundtripTest, GenerateExportImportSignVerify) { + GenerateExportImportSignVerify(GetParam()); +} +INSTANTIATE_TEST_SUITE_P(Pkcs11EcdsaRoundtripTest, Pkcs11EcdsaRoundtripTest, + ::testing::Values(SEC_OID_SECG_EC_SECP256R1, + SEC_OID_SECG_EC_SECP384R1, + SEC_OID_SECG_EC_SECP521R1, + SEC_OID_CURVE25519)); + +} // namespace nss_test |