From 029bcfe189eae5eebbaf58ccff4e1200dd78b228 Mon Sep 17 00:00:00 2001 From: Moonchild Date: Wed, 23 Dec 2020 19:02:52 +0000 Subject: Issue #1693 - Update NSS to 3.59.1.1 This updates to MoonchildProductions/NSS@bd49b2b88 in the repo created for our consumption of the library. --- security/nss/gtests/pk11_gtest/manifest.mn | 9 + .../gtests/pk11_gtest/pk11_aes_cmac_unittest.cc | 44 +- .../nss/gtests/pk11_gtest/pk11_aes_gcm_unittest.cc | 286 ++++++++++- .../gtests/pk11_gtest/pk11_aeskeywrap_unittest.cc | 1 + .../pk11_gtest/pk11_aeskeywrapkwp_unittest.cc | 123 +++++ .../pk11_gtest/pk11_aeskeywrappad_unittest.cc | 1 + .../nss/gtests/pk11_gtest/pk11_cbc_unittest.cc | 71 ++- .../pk11_gtest/pk11_chacha20poly1305_unittest.cc | 220 ++++++++- .../gtests/pk11_gtest/pk11_cipherop_unittest.cc | 49 ++ .../gtests/pk11_gtest/pk11_curve25519_unittest.cc | 4 +- .../pk11_der_private_key_import_unittest.cc | 1 + .../nss/gtests/pk11_gtest/pk11_des_unittest.cc | 1 + .../nss/gtests/pk11_gtest/pk11_dsa_unittest.cc | 79 +++ .../nss/gtests/pk11_gtest/pk11_ecdh_unittest.cc | 86 ++++ .../nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc | 48 ++ .../pk11_gtest/pk11_encrypt_derive_unittest.cc | 19 +- .../nss/gtests/pk11_gtest/pk11_export_unittest.cc | 1 + .../gtests/pk11_gtest/pk11_find_certs_unittest.cc | 244 ++++++--- security/nss/gtests/pk11_gtest/pk11_gtest.gyp | 9 + .../nss/gtests/pk11_gtest/pk11_hkdf_unittest.cc | 199 ++++++++ .../nss/gtests/pk11_gtest/pk11_hmac_unittest.cc | 74 +++ .../nss/gtests/pk11_gtest/pk11_hpke_unittest.cc | 547 +++++++++++++++++++++ .../nss/gtests/pk11_gtest/pk11_import_unittest.cc | 1 + security/nss/gtests/pk11_gtest/pk11_kbkdf.cc | 136 +++++ .../nss/gtests/pk11_gtest/pk11_module_unittest.cc | 1 + .../nss/gtests/pk11_gtest/pk11_pbkdf2_unittest.cc | 49 +- .../nss/gtests/pk11_gtest/pk11_prf_unittest.cc | 1 + .../nss/gtests/pk11_gtest/pk11_prng_unittest.cc | 1 + .../gtests/pk11_gtest/pk11_rsaencrypt_unittest.cc | 127 +++++ .../nss/gtests/pk11_gtest/pk11_rsaoaep_unittest.cc | 186 +++++++ .../gtests/pk11_gtest/pk11_rsapkcs1_unittest.cc | 279 ++++++++--- .../nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc | 144 +++++- .../gtests/pk11_gtest/pk11_seed_cbc_unittest.cc | 70 +-- 33 files changed, 2856 insertions(+), 255 deletions(-) create mode 100644 security/nss/gtests/pk11_gtest/pk11_aeskeywrapkwp_unittest.cc create mode 100644 security/nss/gtests/pk11_gtest/pk11_dsa_unittest.cc create mode 100644 security/nss/gtests/pk11_gtest/pk11_ecdh_unittest.cc create mode 100644 security/nss/gtests/pk11_gtest/pk11_hkdf_unittest.cc create mode 100644 security/nss/gtests/pk11_gtest/pk11_hmac_unittest.cc create mode 100644 security/nss/gtests/pk11_gtest/pk11_hpke_unittest.cc create mode 100644 security/nss/gtests/pk11_gtest/pk11_kbkdf.cc create mode 100644 security/nss/gtests/pk11_gtest/pk11_rsaencrypt_unittest.cc create mode 100644 security/nss/gtests/pk11_gtest/pk11_rsaoaep_unittest.cc (limited to 'security/nss/gtests/pk11_gtest') diff --git a/security/nss/gtests/pk11_gtest/manifest.mn b/security/nss/gtests/pk11_gtest/manifest.mn index 1c0ae6921..f560d9eb1 100644 --- a/security/nss/gtests/pk11_gtest/manifest.mn +++ b/security/nss/gtests/pk11_gtest/manifest.mn @@ -9,23 +9,32 @@ MODULE = nss CPPSRCS = \ pk11_aes_gcm_unittest.cc \ pk11_aeskeywrap_unittest.cc \ + pk11_aeskeywrapkwp_unittest.cc \ pk11_aeskeywrappad_unittest.cc \ pk11_cbc_unittest.cc \ pk11_chacha20poly1305_unittest.cc \ pk11_curve25519_unittest.cc \ pk11_der_private_key_import_unittest.cc \ pk11_des_unittest.cc \ + pk11_dsa_unittest.cc \ pk11_ecdsa_unittest.cc \ + pk11_ecdh_unittest.cc \ pk11_encrypt_derive_unittest.cc \ pk11_export_unittest.cc \ pk11_find_certs_unittest.cc \ + pk11_hkdf_unittest.cc \ + pk11_hmac_unittest.cc \ + pk11_hpke_unittest.cc \ pk11_import_unittest.cc \ + pk11_kbkdf.cc \ pk11_keygen.cc \ pk11_key_unittest.cc \ pk11_module_unittest.cc \ pk11_pbkdf2_unittest.cc \ pk11_prf_unittest.cc \ pk11_prng_unittest.cc \ + pk11_rsaencrypt_unittest.cc \ + pk11_rsaoaep_unittest.cc \ pk11_rsapkcs1_unittest.cc \ pk11_rsapss_unittest.cc \ pk11_seed_cbc_unittest.cc \ diff --git a/security/nss/gtests/pk11_gtest/pk11_aes_cmac_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_aes_cmac_unittest.cc index 962423406..45e4cac1a 100644 --- a/security/nss/gtests/pk11_gtest/pk11_aes_cmac_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_aes_cmac_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -13,11 +14,12 @@ #include "gtest/gtest.h" #include "nss_scoped_ptrs.h" +#include "testvectors/cmac-vectors.h" #include "util.h" namespace nss_test { -class Pkcs11AesCmacTest : public ::testing::Test { +class Pkcs11AesCmacTest : public ::testing::TestWithParam { protected: ScopedPK11SymKey ImportKey(CK_MECHANISM_TYPE mech, SECItem *key_item) { ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); @@ -53,8 +55,46 @@ class Pkcs11AesCmacTest : public ::testing::Test { ASSERT_EQ(SECSuccess, ret); ASSERT_EQ(0, SECITEM_CompareItem(&output_item, &expected_item)); } + + void RunTestVector(const AesCmacTestVector vec) { + bool valid = !vec.invalid; + std::string err = "Test #" + std::to_string(vec.id) + " failed"; + std::vector key = hex_string_to_bytes(vec.key); + std::vector tag = hex_string_to_bytes(vec.tag); + std::vector msg = hex_string_to_bytes(vec.msg); + + std::vector output(AES_BLOCK_SIZE); + // Don't provide a null pointer, even if the input is empty. + uint8_t tmp; + SECItem key_item = {siBuffer, key.data() ? key.data() : &tmp, + static_cast(key.size())}; + SECItem tag_item = {siBuffer, tag.data() ? tag.data() : &tmp, + static_cast(tag.size())}; + SECItem msg_item = {siBuffer, msg.data() ? msg.data() : &tmp, + static_cast(msg.size())}; + SECItem out_item = {siBuffer, output.data() ? output.data() : &tmp, + static_cast(output.size())}; + + ScopedPK11SymKey p11_key = ImportKey(CKM_AES_CMAC_GENERAL, &key_item); + if (vec.comment == "invalid key size") { + ASSERT_EQ(nullptr, p11_key.get()) << err; + return; + } + + ASSERT_NE(nullptr, p11_key.get()) << err; + SECStatus rv = PK11_SignWithSymKey(p11_key.get(), CKM_AES_CMAC, NULL, + &out_item, &msg_item); + + EXPECT_EQ(SECSuccess, rv) << err; + EXPECT_EQ(valid, 0 == SECITEM_CompareItem(&out_item, &tag_item)) << err; + } }; +TEST_P(Pkcs11AesCmacTest, TestVectors) { RunTestVector(GetParam()); } + +INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11AesCmacTest, + ::testing::ValuesIn(kCmacWycheproofVectors)); + // Sanity check of the PKCS #11 API only. Extensive tests for correctness of // underling CMAC implementation conducted in the following file: // gtests/freebl_gtest/cmac_unittests.cc @@ -87,4 +127,4 @@ TEST_F(Pkcs11AesCmacTest, InvalidKeySize) { ScopedPK11SymKey result = ImportKey(CKM_AES_CMAC, &key_item); ASSERT_EQ(nullptr, result.get()); } -} +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_aes_gcm_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_aes_gcm_unittest.cc index f5025fa14..5396bf6e9 100644 --- a/security/nss/gtests/pk11_gtest/pk11_aes_gcm_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_aes_gcm_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -6,6 +7,7 @@ #include #include "nss.h" #include "pk11pub.h" +#include "pk11priv.h" #include "secerr.h" #include "sechash.h" @@ -17,26 +19,24 @@ namespace nss_test { -class Pkcs11AesGcmTest : public ::testing::TestWithParam { +class Pkcs11AesGcmTest : public ::testing::TestWithParam { protected: - void RunTest(const gcm_kat_value val) { - std::vector key = hex_string_to_bytes(val.key); - std::vector iv = hex_string_to_bytes(val.iv); - std::vector plaintext = hex_string_to_bytes(val.plaintext); - std::vector aad = hex_string_to_bytes(val.additional_data); - std::vector result = hex_string_to_bytes(val.result); - bool invalid_ct = val.invalid_ct; - bool invalid_iv = val.invalid_iv; - std::stringstream s; - s << "Test #" << val.test_id << " failed."; - std::string msg = s.str(); + void RunTest(const AesGcmKatValue vec) { + std::vector key = hex_string_to_bytes(vec.key); + std::vector iv = hex_string_to_bytes(vec.iv); + std::vector plaintext = hex_string_to_bytes(vec.plaintext); + std::vector aad = hex_string_to_bytes(vec.additional_data); + std::vector result = hex_string_to_bytes(vec.result); + bool invalid_ct = vec.invalid_ct; + bool invalid_iv = vec.invalid_iv; + std::string msg = "Test #" + std::to_string(vec.id) + " failed"; // Ignore GHASH-only vectors. if (key.empty()) { return; } // Prepare AEAD params. - CK_GCM_PARAMS gcm_params; + CK_NSS_GCM_PARAMS gcm_params; gcm_params.pIv = iv.data(); gcm_params.ulIvLen = iv.size(); gcm_params.pAAD = aad.data(); @@ -125,7 +125,7 @@ class Pkcs11AesGcmTest : public ::testing::TestWithParam { std::vector aad(0); // Prepare AEAD params. - CK_GCM_PARAMS gcm_params; + CK_NSS_GCM_PARAMS gcm_params; gcm_params.pIv = iv.data(); gcm_params.ulIvLen = iv.size(); gcm_params.pAAD = aad.data(); @@ -141,6 +141,211 @@ class Pkcs11AesGcmTest : public ::testing::TestWithParam { &output_len, output.size(), data.data(), data.size()); } + SECStatus MessageInterfaceTest(int iterations, int ivFixedBits, + CK_GENERATOR_FUNCTION ivGen, + PRBool separateTag) { + // Generate a random key. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + EXPECT_NE(nullptr, slot); + ScopedPK11SymKey sym_key( + PK11_KeyGen(slot.get(), mech, nullptr, 16, nullptr)); + EXPECT_NE(nullptr, sym_key); + + const int kTagSize = 16; + int cipher_simulated_size; + int output_len_message = 0; + int output_len_simulated = 0; + unsigned int output_len_v24 = 0; + + std::vector plainIn(17); + std::vector plainOut_message(17); + std::vector plainOut_simulated(17); + std::vector plainOut_v24(17); + std::vector iv(16); + std::vector iv_init(16); + std::vector iv_simulated(16); + std::vector cipher_message(33); + std::vector cipher_simulated(33); + std::vector cipher_v24(33); + std::vector aad(16); + std::vector tag_message(16); + std::vector tag_simulated(16); + + // Prepare AEAD v2.40 params. + CK_GCM_PARAMS_V3 gcm_params; + gcm_params.pIv = iv.data(); + gcm_params.ulIvLen = iv.size(); + gcm_params.ulIvBits = iv.size() * 8; + gcm_params.pAAD = aad.data(); + gcm_params.ulAADLen = aad.size(); + gcm_params.ulTagBits = kTagSize * 8; + + // Prepare AEAD MESSAGE params. + CK_GCM_MESSAGE_PARAMS gcm_message_params; + gcm_message_params.pIv = iv.data(); + gcm_message_params.ulIvLen = iv.size(); + gcm_message_params.ulTagBits = kTagSize * 8; + gcm_message_params.ulIvFixedBits = ivFixedBits; + gcm_message_params.ivGenerator = ivGen; + if (separateTag) { + gcm_message_params.pTag = tag_message.data(); + } else { + gcm_message_params.pTag = cipher_message.data() + plainIn.size(); + } + + // Prepare AEAD MESSAGE params for simulated case + CK_GCM_MESSAGE_PARAMS gcm_simulated_params; + gcm_simulated_params = gcm_message_params; + if (separateTag) { + // The simulated case, we have to allocate temp bufs for separate + // tags, make sure that works in both the encrypt and the decrypt + // cases. + gcm_simulated_params.pTag = tag_simulated.data(); + cipher_simulated_size = cipher_simulated.size() - kTagSize; + } else { + gcm_simulated_params.pTag = cipher_simulated.data() + plainIn.size(); + cipher_simulated_size = cipher_simulated.size(); + } + /* when we are using CKG_GENERATE_RANDOM, don't independently generate + * the IV in the simulated case. Since the IV's would be random, none of + * the generated results would be the same. Just use the IV we generated + * in message interface */ + if (ivGen == CKG_GENERATE_RANDOM) { + gcm_simulated_params.ivGenerator = CKG_NO_GENERATE; + } else { + gcm_simulated_params.pIv = iv_simulated.data(); + } + + SECItem params = {siBuffer, reinterpret_cast(&gcm_params), + sizeof(gcm_params)}; + SECItem empty = {siBuffer, NULL, 0}; + + // initialize our plain text, IV and aad. + EXPECT_EQ(PK11_GenerateRandom(plainIn.data(), plainIn.size()), SECSuccess); + EXPECT_EQ(PK11_GenerateRandom(aad.data(), aad.size()), SECSuccess); + EXPECT_EQ(PK11_GenerateRandom(iv_init.data(), iv_init.size()), SECSuccess); + iv_simulated = iv_init; // vector assignment actually copies data + iv = iv_init; + + // Initialize message encrypt context + ScopedPK11Context encrypt_message_context(PK11_CreateContextBySymKey( + mech, CKA_NSS_MESSAGE | CKA_ENCRYPT, sym_key.get(), &empty)); + EXPECT_NE(nullptr, encrypt_message_context); + if (!encrypt_message_context) { + return SECFailure; + } + EXPECT_FALSE(_PK11_ContextGetAEADSimulation(encrypt_message_context.get())); + + // Initialize simulated encrypt context + ScopedPK11Context encrypt_simulated_context(PK11_CreateContextBySymKey( + mech, CKA_NSS_MESSAGE | CKA_ENCRYPT, sym_key.get(), &empty)); + EXPECT_NE(nullptr, encrypt_simulated_context); + if (!encrypt_simulated_context) { + return SECFailure; + } + EXPECT_EQ(SECSuccess, + _PK11_ContextSetAEADSimulation(encrypt_simulated_context.get())); + + // Initialize message decrypt context + ScopedPK11Context decrypt_message_context(PK11_CreateContextBySymKey( + mech, CKA_NSS_MESSAGE | CKA_DECRYPT, sym_key.get(), &empty)); + EXPECT_NE(nullptr, decrypt_message_context); + if (!decrypt_message_context) { + return SECFailure; + } + EXPECT_FALSE(_PK11_ContextGetAEADSimulation(decrypt_message_context.get())); + + // Initialize simulated decrypt context + ScopedPK11Context decrypt_simulated_context(PK11_CreateContextBySymKey( + mech, CKA_NSS_MESSAGE | CKA_DECRYPT, sym_key.get(), &empty)); + EXPECT_NE(nullptr, decrypt_simulated_context); + if (!decrypt_simulated_context) { + return SECFailure; + } + EXPECT_EQ(SECSuccess, + _PK11_ContextSetAEADSimulation(decrypt_simulated_context.get())); + + // Now walk down our iterations. Each method of calculating the operation + // should agree at each step. + for (int i = 0; i < iterations; i++) { + SECStatus rv; + /* recopy the initial vector each time */ + iv_simulated = iv_init; + iv = iv_init; + + // First encrypt. We don't test the error code here, because + // we may be testing error conditions with this function (namely + // do we fail if we try to generate to many Random IV's). + rv = + PK11_AEADRawOp(encrypt_message_context.get(), &gcm_message_params, + sizeof(gcm_message_params), aad.data(), aad.size(), + cipher_message.data(), &output_len_message, + cipher_message.size(), plainIn.data(), plainIn.size()); + if (rv != SECSuccess) { + return rv; + } + rv = + PK11_AEADRawOp(encrypt_simulated_context.get(), &gcm_simulated_params, + sizeof(gcm_simulated_params), aad.data(), aad.size(), + cipher_simulated.data(), &output_len_simulated, + cipher_simulated_size, plainIn.data(), plainIn.size()); + if (rv != SECSuccess) { + return rv; + } + // make sure simulated and message is the same + EXPECT_EQ(output_len_message, output_len_simulated); + EXPECT_EQ(0, memcmp(cipher_message.data(), cipher_simulated.data(), + output_len_message)); + EXPECT_EQ(0, memcmp(gcm_message_params.pTag, gcm_simulated_params.pTag, + kTagSize)); + EXPECT_EQ(0, memcmp(iv.data(), gcm_simulated_params.pIv, iv.size())); + // make sure v2.40 is the same. it inherits the generated iv from + // encrypt_message_context. + EXPECT_EQ(SECSuccess, + PK11_Encrypt(sym_key.get(), mech, ¶ms, cipher_v24.data(), + &output_len_v24, cipher_v24.size(), plainIn.data(), + plainIn.size())); + EXPECT_EQ(output_len_message, (int)output_len_v24 - kTagSize); + EXPECT_EQ(0, memcmp(cipher_message.data(), cipher_v24.data(), + output_len_message)); + EXPECT_EQ(0, memcmp(gcm_message_params.pTag, + cipher_v24.data() + output_len_message, kTagSize)); + // now make sure we can decrypt + EXPECT_EQ(SECSuccess, + PK11_AEADRawOp(decrypt_message_context.get(), + &gcm_message_params, sizeof(gcm_message_params), + aad.data(), aad.size(), plainOut_message.data(), + &output_len_message, plainOut_message.size(), + cipher_message.data(), output_len_message)); + EXPECT_EQ(output_len_message, (int)plainIn.size()); + EXPECT_EQ( + 0, memcmp(plainOut_message.data(), plainIn.data(), plainIn.size())); + EXPECT_EQ( + SECSuccess, + PK11_AEADRawOp(decrypt_simulated_context.get(), &gcm_simulated_params, + sizeof(gcm_simulated_params), aad.data(), aad.size(), + plainOut_simulated.data(), &output_len_simulated, + plainOut_simulated.size(), cipher_message.data(), + output_len_simulated)); + EXPECT_EQ(output_len_simulated, (int)plainIn.size()); + EXPECT_EQ( + 0, memcmp(plainOut_simulated.data(), plainIn.data(), plainIn.size())); + if (separateTag) { + // in the separateTag case, we need to copy the tag back to the + // end of the cipher_message.data() before using the v2.4 interface + memcpy(cipher_message.data() + output_len_message, + gcm_message_params.pTag, kTagSize); + } + EXPECT_EQ(SECSuccess, + PK11_Decrypt(sym_key.get(), mech, ¶ms, plainOut_v24.data(), + &output_len_v24, plainOut_v24.size(), + cipher_message.data(), output_len_v24)); + EXPECT_EQ(output_len_v24, plainIn.size()); + EXPECT_EQ(0, memcmp(plainOut_v24.data(), plainIn.data(), plainIn.size())); + } + return SECSuccess; + } + const CK_MECHANISM_TYPE mech = CKM_AES_GCM; }; @@ -167,4 +372,57 @@ TEST_F(Pkcs11AesGcmTest, TwelveByteZeroIV) { EXPECT_EQ(SECSuccess, EncryptWithIV(iv)); } +// basic message interface it's the most common configuration +TEST_F(Pkcs11AesGcmTest, MessageInterfaceBasic) { + EXPECT_EQ(SECSuccess, + MessageInterfaceTest(16, 0, CKG_GENERATE_COUNTER, PR_FALSE)); +} + +// basic interface, but return the tags in a separate buffer. This triggers +// different behaviour in the simulated case, which has to buffer the +// intermediate values in a separate buffer. +TEST_F(Pkcs11AesGcmTest, MessageInterfaceSeparateTags) { + EXPECT_EQ(SECSuccess, + MessageInterfaceTest(16, 0, CKG_GENERATE_COUNTER, PR_TRUE)); +} + +// test the case where we are only allowing a portion of the iv to be generated +TEST_F(Pkcs11AesGcmTest, MessageInterfaceIVMask) { + EXPECT_EQ(SECSuccess, + MessageInterfaceTest(16, 124, CKG_GENERATE_COUNTER, PR_FALSE)); +} + +// test the case where we using the tls1.3 iv generation +TEST_F(Pkcs11AesGcmTest, MessageInterfaceXorCounter) { + EXPECT_EQ(SECSuccess, + MessageInterfaceTest(16, 0, CKG_GENERATE_COUNTER_XOR, PR_FALSE)); +} + +// test the case where we overflow the counter (requires restricted iv) +// 128-124 = 4 bits; +TEST_F(Pkcs11AesGcmTest, MessageInterfaceCounterOverflow) { + EXPECT_EQ(SECFailure, + MessageInterfaceTest(17, 124, CKG_GENERATE_COUNTER, PR_FALSE)); +} + +// overflow the tla1.2 iv case +TEST_F(Pkcs11AesGcmTest, MessageInterfaceXorCounterOverflow) { + EXPECT_EQ(SECFailure, + MessageInterfaceTest(17, 124, CKG_GENERATE_COUNTER_XOR, PR_FALSE)); +} + +// test random generation of the IV (uses an aligned restricted iv) +TEST_F(Pkcs11AesGcmTest, MessageInterfaceRandomIV) { + EXPECT_EQ(SECSuccess, + MessageInterfaceTest(16, 56, CKG_GENERATE_RANDOM, PR_FALSE)); +} + +// test the case where we try to generate too many random IVs for the size of +// our our restricted IV (notice for counters, we can generate 16 IV with +// 4 bits, but for random we need at least 72 bits to generate 16 IVs). +// 128-56 = 72 bits +TEST_F(Pkcs11AesGcmTest, MessageInterfaceRandomOverflow) { + EXPECT_EQ(SECFailure, + MessageInterfaceTest(17, 56, CKG_GENERATE_RANDOM, PR_FALSE)); +} } // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_aeskeywrap_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_aeskeywrap_unittest.cc index c2c60f4a9..0aa711dc8 100644 --- a/security/nss/gtests/pk11_gtest/pk11_aeskeywrap_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_aeskeywrap_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/pk11_gtest/pk11_aeskeywrapkwp_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_aeskeywrapkwp_unittest.cc new file mode 100644 index 000000000..42cd0b36a --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_aeskeywrapkwp_unittest.cc @@ -0,0 +1,123 @@ +/* -*- 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 +#include "nss.h" +#include "pk11pub.h" + +#include "testvectors/kw-vectors.h" +#include "testvectors/kwp-vectors.h" +#include "gtest/gtest.h" +#include "nss_scoped_ptrs.h" + +namespace nss_test { + +class Pkcs11AESKeyWrapKwpTest + : public ::testing::TestWithParam { + protected: + CK_MECHANISM_TYPE mechanism = CKM_AES_KEY_WRAP_KWP; + + void WrapUnwrap(unsigned char* kek_data, unsigned int kek_len, + unsigned char* key_data, unsigned int key_data_len, + unsigned char* expected_ciphertext, + unsigned int expected_ciphertext_len, + std::map tests, uint32_t test_id) { + std::vector wrapped_key(PR_MAX(1U, expected_ciphertext_len)); + std::vector unwrapped_key(PR_MAX(1U, key_data_len)); + std::vector zeros(PR_MAX(1U, expected_ciphertext_len), 0); + unsigned int wrapped_key_len = 0; + unsigned int unwrapped_key_len = 0; + SECStatus rv; + + std::stringstream s; + s << "Test with original ID #" << test_id << " failed." << std::endl; + std::string msg = s.str(); + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot) << msg; + + // Import encryption key. + SECItem kek_item = {siBuffer, kek_data, kek_len}; + ScopedPK11SymKey kek(PK11_ImportSymKeyWithFlags( + slot.get(), mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &kek_item, + CKF_DECRYPT, PR_FALSE, nullptr)); + EXPECT_TRUE(!!kek) << msg; + + // Wrap key + Action test = WRAP; + if (tests.count(test)) { + rv = PK11_Encrypt(kek.get(), mechanism, nullptr /* param */, + wrapped_key.data(), &wrapped_key_len, + wrapped_key.size(), key_data, key_data_len); + ASSERT_EQ(rv, tests[test].expect_rv) << msg; + + // If we failed, check that output was not produced. + if (rv == SECFailure) { + EXPECT_TRUE(wrapped_key_len == 0); + EXPECT_TRUE(!memcmp(wrapped_key.data(), zeros.data(), wrapped_key_len)); + } + + if (tests[test].output_match) { + EXPECT_EQ(expected_ciphertext_len, wrapped_key_len) << msg; + EXPECT_TRUE(!memcmp(expected_ciphertext, wrapped_key.data(), + expected_ciphertext_len)) + << msg; + } else { + // If we produced output, verify that it doesn't match the vector + if (wrapped_key_len) { + EXPECT_FALSE(wrapped_key_len == expected_ciphertext_len && + !memcmp(wrapped_key.data(), expected_ciphertext, + expected_ciphertext_len)) + << msg; + } + } + } + + // Unwrap key + test = UNWRAP; + if (tests.count(test)) { + rv = PK11_Decrypt(kek.get(), mechanism, nullptr /* param */, + unwrapped_key.data(), &unwrapped_key_len, + unwrapped_key.size(), expected_ciphertext, + expected_ciphertext_len); + ASSERT_EQ(rv, tests[test].expect_rv) << msg; + + // If we failed, check that output was not produced. + if (rv == SECFailure) { + EXPECT_TRUE(unwrapped_key_len == 0); + EXPECT_TRUE( + !memcmp(unwrapped_key.data(), zeros.data(), unwrapped_key_len)); + } + + if (tests[test].output_match) { + EXPECT_EQ(unwrapped_key_len, key_data_len) << msg; + EXPECT_TRUE(!memcmp(key_data, unwrapped_key.data(), key_data_len)) + << msg; + } else { + // If we produced output, verify that it doesn't match the vector + if (unwrapped_key_len) { + EXPECT_FALSE( + unwrapped_key_len == expected_ciphertext_len && + !memcmp(unwrapped_key.data(), key_data, unwrapped_key_len)) + << msg; + } + } + } + } + + void WrapUnwrap(keywrap_vector testvector) { + WrapUnwrap(testvector.key.data(), testvector.key.size(), + testvector.msg.data(), testvector.msg.size(), + testvector.ct.data(), testvector.ct.size(), testvector.tests, + testvector.test_id); + } +}; + +TEST_P(Pkcs11AESKeyWrapKwpTest, TestVectors) { WrapUnwrap(GetParam()); } + +INSTANTIATE_TEST_CASE_P(Pkcs11NistAESKWPTest, Pkcs11AESKeyWrapKwpTest, + ::testing::ValuesIn(kNistAesKWPVectors)); +} /* nss_test */ diff --git a/security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc index 75078fb77..0f79abed5 100644 --- a/security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/pk11_gtest/pk11_cbc_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_cbc_unittest.cc index 1e4aa2faf..7f950422f 100644 --- a/security/nss/gtests/pk11_gtest/pk11_cbc_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_cbc_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -8,8 +9,10 @@ #include "pk11pub.h" #include "secerr.h" -#include "nss_scoped_ptrs.h" #include "gtest/gtest.h" +#include "nss_scoped_ptrs.h" +#include "testvectors/cbc-vectors.h" +#include "util.h" namespace nss_test { @@ -253,8 +256,8 @@ TEST_F(Pkcs11CbcPadTest, FailEncryptShortParam) { unsigned int encrypted_len = 0; size_t input_len = AES_BLOCK_SIZE; - // CK_GCM_PARAMS is the largest param struct used across AES modes - uint8_t param_buf[sizeof(CK_GCM_PARAMS)]; + // CK_NSS_GCM_PARAMS is the largest param struct used across AES modes + uint8_t param_buf[sizeof(CK_NSS_GCM_PARAMS)]; SECItem param = {siBuffer, param_buf, sizeof(param_buf)}; SECItem key_item = {siBuffer, const_cast(kKeyData), 16}; @@ -278,18 +281,18 @@ TEST_F(Pkcs11CbcPadTest, FailEncryptShortParam) { sizeof(encrypted), kInput, input_len); EXPECT_EQ(SECSuccess, rv); - // GCM should have a CK_GCM_PARAMS - param.len = sizeof(CK_GCM_PARAMS) - 1; + // GCM should have a CK_NSS_GCM_PARAMS + param.len = sizeof(CK_NSS_GCM_PARAMS) - 1; rv = PK11_Encrypt(key.get(), CKM_AES_GCM, ¶m, encrypted, &encrypted_len, sizeof(encrypted), kInput, input_len); EXPECT_EQ(SECFailure, rv); param.len++; - reinterpret_cast(param.data)->pIv = param_buf; - reinterpret_cast(param.data)->ulIvLen = 12; - reinterpret_cast(param.data)->pAAD = nullptr; - reinterpret_cast(param.data)->ulAADLen = 0; - reinterpret_cast(param.data)->ulTagBits = 128; + reinterpret_cast(param.data)->pIv = param_buf; + reinterpret_cast(param.data)->ulIvLen = 12; + reinterpret_cast(param.data)->pAAD = nullptr; + reinterpret_cast(param.data)->ulAADLen = 0; + reinterpret_cast(param.data)->ulTagBits = 128; rv = PK11_Encrypt(key.get(), CKM_AES_GCM, ¶m, encrypted, &encrypted_len, sizeof(encrypted), kInput, input_len); EXPECT_EQ(SECSuccess, rv); @@ -554,4 +557,52 @@ INSTANTIATE_TEST_CASE_P(EncryptDecrypt, Pkcs11CbcPadTest, ::testing::Values(CKM_AES_CBC_PAD, CKM_AES_CBC, CKM_DES3_CBC_PAD, CKM_DES3_CBC)); +class Pkcs11AesCbcWycheproofTest + : public ::testing::TestWithParam { + protected: + void RunTest(const AesCbcTestVector vec) { + bool valid = vec.valid; + std::string err = "Test #" + std::to_string(vec.id) + " failed"; + std::vector key = hex_string_to_bytes(vec.key); + std::vector iv = hex_string_to_bytes(vec.iv); + std::vector ciphertext = hex_string_to_bytes(vec.ciphertext); + std::vector msg = hex_string_to_bytes(vec.msg); + std::vector decrypted(vec.ciphertext.size()); + unsigned int decrypted_len = 0; + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + + // Don't provide a null pointer, even if the length is 0. We don't want to + // fail on trivial checks. + uint8_t tmp; + SECItem iv_item = {siBuffer, iv.data() ? iv.data() : &tmp, + static_cast(iv.size())}; + SECItem key_item = {siBuffer, key.data() ? key.data() : &tmp, + static_cast(key.size())}; + + PK11SymKey* pKey = PK11_ImportSymKey(slot.get(), kMech, PK11_OriginUnwrap, + CKA_ENCRYPT, &key_item, nullptr); + ASSERT_NE(nullptr, pKey); + ScopedPK11SymKey spKey = ScopedPK11SymKey(pKey); + + SECStatus rv = PK11_Decrypt(spKey.get(), kMech, &iv_item, decrypted.data(), + &decrypted_len, decrypted.size(), + ciphertext.data(), ciphertext.size()); + + ASSERT_EQ(valid ? SECSuccess : SECFailure, rv) << err; + if (valid) { + EXPECT_EQ(msg.size(), static_cast(decrypted_len)) << err; + EXPECT_EQ(0, memcmp(msg.data(), decrypted.data(), decrypted_len)) << err; + } + } + + const CK_MECHANISM_TYPE kMech = CKM_AES_CBC_PAD; +}; + +TEST_P(Pkcs11AesCbcWycheproofTest, TestVectors) { RunTest(GetParam()); } + +INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11AesCbcWycheproofTest, + ::testing::ValuesIn(kCbcWycheproofVectors)); + } // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc index 00d5e5244..3ea17678d 100644 --- a/security/nss/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -6,6 +7,7 @@ #include #include "nss.h" #include "pk11pub.h" +#include "pk11priv.h" #include "sechash.h" #include "secerr.h" @@ -25,7 +27,7 @@ static const uint8_t kCtrNonce[16] = {'c', 0, 0, 0, 'n'}; static const uint8_t kData[16] = {'d'}; class Pkcs11ChaCha20Poly1305Test - : public ::testing::TestWithParam { + : public ::testing::TestWithParam { public: void EncryptDecrypt(const ScopedPK11SymKey& key, const bool invalid_iv, const bool invalid_tag, const uint8_t* data, @@ -43,7 +45,7 @@ class Pkcs11ChaCha20Poly1305Test SECItem params = {siBuffer, reinterpret_cast(&aead_params), sizeof(aead_params)}; - // Encrypt with bad parameters. + // Encrypt with bad parameters (TagLen is too long). unsigned int encrypted_len = 0; std::vector encrypted(data_len + aead_params.ulTagLen); aead_params.ulTagLen = 158072; @@ -52,9 +54,16 @@ class Pkcs11ChaCha20Poly1305Test &encrypted_len, encrypted.size(), data, data_len); EXPECT_EQ(SECFailure, rv); EXPECT_EQ(0U, encrypted_len); - aead_params.ulTagLen = 16; + + // Encrypt with bad parameters (TagLen is too short). + aead_params.ulTagLen = 2; + rv = PK11_Encrypt(key.get(), kMech, ¶ms, encrypted.data(), + &encrypted_len, encrypted.size(), data, data_len); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, encrypted_len); // Encrypt. + aead_params.ulTagLen = 16; rv = PK11_Encrypt(key.get(), kMech, ¶ms, encrypted.data(), &encrypted_len, encrypted.size(), data, data_len); @@ -73,6 +82,7 @@ class Pkcs11ChaCha20Poly1305Test // Check ciphertext and tag. if (ct) { ASSERT_EQ(ct_len, encrypted_len); + EXPECT_TRUE(!memcmp(ct, encrypted.data(), encrypted.size() - 16)); EXPECT_TRUE(!memcmp(ct, encrypted.data(), encrypted.size()) != invalid_tag); } @@ -166,10 +176,10 @@ class Pkcs11ChaCha20Poly1305Test } } - void EncryptDecrypt(const chaChaTestVector testvector) { + void EncryptDecrypt(const ChaChaTestVector testvector) { ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); - SECItem keyItem = {siBuffer, toUcharPtr(testvector.Key.data()), - static_cast(testvector.Key.size())}; + SECItem keyItem = {siBuffer, toUcharPtr(testvector.key.data()), + static_cast(testvector.key.size())}; // Import key. ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), kMech, PK11_OriginUnwrap, @@ -177,11 +187,173 @@ class Pkcs11ChaCha20Poly1305Test EXPECT_TRUE(!!key); // Check. - EncryptDecrypt(key, testvector.invalidIV, testvector.invalidTag, - testvector.Data.data(), testvector.Data.size(), - testvector.AAD.data(), testvector.AAD.size(), - testvector.IV.data(), testvector.IV.size(), - testvector.CT.data(), testvector.CT.size()); + EncryptDecrypt(key, testvector.invalid_iv, testvector.invalid_tag, + testvector.plaintext.data(), testvector.plaintext.size(), + testvector.aad.data(), testvector.aad.size(), + testvector.iv.data(), testvector.iv.size(), + testvector.ciphertext.data(), testvector.ciphertext.size()); + } + + void MessageInterfaceTest(CK_MECHANISM_TYPE mech, int iterations, + PRBool separateTag) { + // Generate a random key. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey sym_key( + PK11_KeyGen(slot.get(), mech, nullptr, 32, nullptr)); + ASSERT_NE(nullptr, sym_key); + + int tagSize = 16; + int cipher_simulated_size; + int output_len_message = 0; + int output_len_simulated = 0; + unsigned int output_len_v24 = 0; + + std::vector plainIn(17); + std::vector plainOut_message(17); + std::vector plainOut_simulated(17); + std::vector plainOut_v24(17); + std::vector nonce(12); + std::vector cipher_message(33); + std::vector cipher_simulated(33); + std::vector cipher_v24(33); + std::vector aad(16); + std::vector tag_message(16); + std::vector tag_simulated(16); + + // Prepare AEAD v2.40 params. + CK_SALSA20_CHACHA20_POLY1305_PARAMS chacha_params; + chacha_params.pNonce = nonce.data(); + chacha_params.ulNonceLen = nonce.size(); + chacha_params.pAAD = aad.data(); + chacha_params.ulAADLen = aad.size(); + + // Prepare AEAD MESSAGE params. + CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS chacha_message_params; + chacha_message_params.pNonce = nonce.data(); + chacha_message_params.ulNonceLen = nonce.size(); + if (separateTag) { + chacha_message_params.pTag = tag_message.data(); + } else { + chacha_message_params.pTag = cipher_message.data() + plainIn.size(); + } + + // Prepare AEAD MESSAGE params for simulated case + CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS chacha_simulated_params; + chacha_simulated_params = chacha_message_params; + if (separateTag) { + // The simulated case, we have to allocate temp bufs for separate + // tags, make sure that works in both the encrypt and the decrypt + // cases. + chacha_simulated_params.pTag = tag_simulated.data(); + cipher_simulated_size = cipher_simulated.size() - tagSize; + } else { + chacha_simulated_params.pTag = cipher_simulated.data() + plainIn.size(); + cipher_simulated_size = cipher_simulated.size(); + } + SECItem params = {siBuffer, + reinterpret_cast(&chacha_params), + sizeof(chacha_params)}; + SECItem empty = {siBuffer, NULL, 0}; + + // initialize our plain text, IV and aad. + ASSERT_EQ(PK11_GenerateRandom(plainIn.data(), plainIn.size()), SECSuccess); + ASSERT_EQ(PK11_GenerateRandom(aad.data(), aad.size()), SECSuccess); + + // Initialize message encrypt context + ScopedPK11Context encrypt_message_context(PK11_CreateContextBySymKey( + mech, CKA_NSS_MESSAGE | CKA_ENCRYPT, sym_key.get(), &empty)); + ASSERT_NE(nullptr, encrypt_message_context); + ASSERT_FALSE(_PK11_ContextGetAEADSimulation(encrypt_message_context.get())); + + // Initialize simulated encrypt context + ScopedPK11Context encrypt_simulated_context(PK11_CreateContextBySymKey( + mech, CKA_NSS_MESSAGE | CKA_ENCRYPT, sym_key.get(), &empty)); + ASSERT_NE(nullptr, encrypt_simulated_context); + ASSERT_EQ(SECSuccess, + _PK11_ContextSetAEADSimulation(encrypt_simulated_context.get())); + + // Initialize message decrypt context + ScopedPK11Context decrypt_message_context(PK11_CreateContextBySymKey( + mech, CKA_NSS_MESSAGE | CKA_DECRYPT, sym_key.get(), &empty)); + ASSERT_NE(nullptr, decrypt_message_context); + ASSERT_FALSE(_PK11_ContextGetAEADSimulation(decrypt_message_context.get())); + + // Initialize simulated decrypt context + ScopedPK11Context decrypt_simulated_context(PK11_CreateContextBySymKey( + mech, CKA_NSS_MESSAGE | CKA_DECRYPT, sym_key.get(), &empty)); + ASSERT_NE(nullptr, decrypt_simulated_context); + EXPECT_EQ(SECSuccess, + _PK11_ContextSetAEADSimulation(decrypt_simulated_context.get())); + + // Now walk down our iterations. Each method of calculating the operation + // should agree at each step. + for (int i = 0; i < iterations; i++) { + // get a unique nonce for each iteration + EXPECT_EQ(PK11_GenerateRandom(nonce.data(), nonce.size()), SECSuccess); + EXPECT_EQ(SECSuccess, + PK11_AEADRawOp( + encrypt_message_context.get(), &chacha_message_params, + sizeof(chacha_message_params), aad.data(), aad.size(), + cipher_message.data(), &output_len_message, + cipher_message.size(), plainIn.data(), plainIn.size())); + EXPECT_EQ(SECSuccess, + PK11_AEADRawOp( + encrypt_simulated_context.get(), &chacha_simulated_params, + sizeof(chacha_simulated_params), aad.data(), aad.size(), + cipher_simulated.data(), &output_len_simulated, + cipher_simulated_size, plainIn.data(), plainIn.size())); + // make sure simulated and message is the same + EXPECT_EQ(output_len_message, output_len_simulated); + EXPECT_EQ(0, memcmp(cipher_message.data(), cipher_simulated.data(), + output_len_message)); + EXPECT_EQ(0, memcmp(chacha_message_params.pTag, + chacha_simulated_params.pTag, tagSize)); + // make sure v2.40 is the same. + EXPECT_EQ(SECSuccess, + PK11_Encrypt(sym_key.get(), mech, ¶ms, cipher_v24.data(), + &output_len_v24, cipher_v24.size(), plainIn.data(), + plainIn.size())); + EXPECT_EQ(output_len_message, (int)output_len_v24 - tagSize); + EXPECT_EQ(0, memcmp(cipher_message.data(), cipher_v24.data(), + output_len_message)); + EXPECT_EQ(0, memcmp(chacha_message_params.pTag, + cipher_v24.data() + output_len_message, tagSize)); + // now make sure we can decrypt + EXPECT_EQ( + SECSuccess, + PK11_AEADRawOp(decrypt_message_context.get(), &chacha_message_params, + sizeof(chacha_message_params), aad.data(), aad.size(), + plainOut_message.data(), &output_len_message, + plainOut_message.size(), cipher_message.data(), + output_len_message)); + EXPECT_EQ(output_len_message, (int)plainIn.size()); + EXPECT_EQ( + 0, memcmp(plainOut_message.data(), plainIn.data(), plainIn.size())); + EXPECT_EQ(SECSuccess, + PK11_AEADRawOp(decrypt_simulated_context.get(), + &chacha_simulated_params, + sizeof(chacha_simulated_params), aad.data(), + aad.size(), plainOut_simulated.data(), + &output_len_simulated, plainOut_simulated.size(), + cipher_message.data(), output_len_simulated)); + EXPECT_EQ(output_len_simulated, (int)plainIn.size()); + EXPECT_EQ( + 0, memcmp(plainOut_simulated.data(), plainIn.data(), plainIn.size())); + if (separateTag) { + // in the separateTag case, we need to copy the tag back to the + // end of the cipher_message.data() before using the v2.4 interface + memcpy(cipher_message.data() + output_len_message, + chacha_message_params.pTag, tagSize); + } + EXPECT_EQ(SECSuccess, + PK11_Decrypt(sym_key.get(), mech, ¶ms, plainOut_v24.data(), + &output_len_v24, plainOut_v24.size(), + cipher_message.data(), output_len_v24)); + EXPECT_EQ(output_len_v24, plainIn.size()); + EXPECT_EQ(0, memcmp(plainOut_v24.data(), plainIn.data(), plainIn.size())); + } + return; } protected: @@ -260,13 +432,16 @@ TEST_F(Pkcs11ChaCha20Poly1305Test, GenerateXor) { ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr)); EXPECT_TRUE(!!key); - SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce), - static_cast(sizeof(kCtrNonce))}; + std::vector iv(16); + SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), iv.data(), iv.size()); + EXPECT_EQ(SECSuccess, rv); + + SECItem ctrNonceItem = {siBuffer, toUcharPtr(iv.data()), + static_cast(iv.size())}; uint8_t encrypted[sizeof(kData)]; unsigned int encrypted_len = 88; // This should be overwritten. - SECStatus rv = - PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, - &encrypted_len, sizeof(encrypted), kData, sizeof(kData)); + rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, + &encrypted_len, sizeof(encrypted), kData, sizeof(kData)); ASSERT_EQ(SECSuccess, rv); ASSERT_EQ(sizeof(kData), static_cast(encrypted_len)); } @@ -300,4 +475,17 @@ INSTANTIATE_TEST_CASE_P(NSSTestVector, Pkcs11ChaCha20Poly1305Test, INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11ChaCha20Poly1305Test, ::testing::ValuesIn(kChaCha20WycheproofVectors)); +// basic message interface it's the most common configuration +TEST_F(Pkcs11ChaCha20Poly1305Test, ChaCha201305MessageInterfaceBasic) { + MessageInterfaceTest(CKM_CHACHA20_POLY1305, 16, PR_FALSE); +} + +// basic interface, but return the tags in a separate buffer. This triggers +// different behaviour in the simulated case, which has to buffer the +// intermediate values in a separate buffer. +TEST_F(Pkcs11ChaCha20Poly1305Test, + ChaCha20Poly1305MessageInterfaceSeparateTags) { + MessageInterfaceTest(CKM_CHACHA20_POLY1305, 16, PR_TRUE); +} + } // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_cipherop_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_cipherop_unittest.cc index 38982fd88..700750cc9 100644 --- a/security/nss/gtests/pk11_gtest/pk11_cipherop_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_cipherop_unittest.cc @@ -77,4 +77,53 @@ TEST(Pkcs11CipherOp, SingleCtxMultipleUnalignedCipherOps) { NSS_ShutdownContext(globalctx); } +TEST(Pkcs11CipherOp, SingleCtxMultipleUnalignedCipherOpsChaCha20) { + PK11SlotInfo* slot; + PK11SymKey* key; + PK11Context* ctx; + + NSSInitContext* globalctx = + NSS_InitContext("", "", "", "", NULL, + NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | + NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT); + + const CK_MECHANISM_TYPE cipher = CKM_NSS_CHACHA20_CTR; + + slot = PK11_GetInternalSlot(); + ASSERT_TRUE(slot); + + // Use arbitrary bytes for the ChaCha20 key and IV + uint8_t key_bytes[32]; + for (size_t i = 0; i < 32; i++) { + key_bytes[i] = i; + } + SECItem keyItem = {siBuffer, key_bytes, 32}; + + uint8_t iv_bytes[16]; + for (size_t i = 0; i < 16; i++) { + key_bytes[i] = i; + } + SECItem ivItem = {siBuffer, iv_bytes, 16}; + + SECItem* param = PK11_ParamFromIV(cipher, &ivItem); + + key = PK11_ImportSymKey(slot, cipher, PK11_OriginUnwrap, CKA_ENCRYPT, + &keyItem, NULL); + ctx = PK11_CreateContextBySymKey(cipher, CKA_ENCRYPT, key, param); + ASSERT_TRUE(key); + ASSERT_TRUE(ctx); + + uint8_t outbuf[128]; + // This is supposed to fail for Chacha20. This is because the underlying + // PK11_CipherOp operation is calling the C_EncryptUpdate function for + // which multi-part is disabled for ChaCha20 in counter mode. + ASSERT_EQ(GetBytes(ctx, outbuf, 7), SECFailure); + + PK11_FreeSymKey(key); + PK11_FreeSlot(slot); + SECITEM_FreeItem(param, PR_TRUE); + PK11_DestroyContext(ctx, PR_TRUE); + NSS_ShutdownContext(globalctx); +} + } // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_curve25519_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_curve25519_unittest.cc index 647e3a706..b26e5d41e 100644 --- a/security/nss/gtests/pk11_gtest/pk11_curve25519_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_curve25519_unittest.cc @@ -15,7 +15,7 @@ namespace nss_test { class Pkcs11Curve25519Test - : public ::testing::TestWithParam { + : public ::testing::TestWithParam { protected: void Derive(const uint8_t* pkcs8, size_t pkcs8_len, const uint8_t* spki, size_t spki_len, const uint8_t* secret, size_t secret_len, @@ -107,7 +107,7 @@ class Pkcs11Curve25519Test } }; - void Derive(const curve25519_testvector testvector) { + void Derive(const EcdhTestVectorStr testvector) { Derive(testvector.private_key.data(), testvector.private_key.size(), testvector.public_key.data(), testvector.public_key.size(), testvector.secret.data(), testvector.secret.size(), diff --git a/security/nss/gtests/pk11_gtest/pk11_der_private_key_import_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_der_private_key_import_unittest.cc index 0c524233d..449e7728b 100644 --- a/security/nss/gtests/pk11_gtest/pk11_der_private_key_import_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_der_private_key_import_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/pk11_gtest/pk11_des_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_des_unittest.cc index afb560ce1..30f1afb8d 100644 --- a/security/nss/gtests/pk11_gtest/pk11_des_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_des_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/pk11_gtest/pk11_dsa_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_dsa_unittest.cc new file mode 100644 index 000000000..9d6c8c856 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_dsa_unittest.cc @@ -0,0 +1,79 @@ +/* -*- 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 +#include "nss.h" +#include "pk11pub.h" +#include "sechash.h" +#include "cryptohi.h" + +#include "cpputil.h" +#include "databuffer.h" + +#include "gtest/gtest.h" +#include "nss_scoped_ptrs.h" + +#include "testvectors/dsa-vectors.h" + +namespace nss_test { + +class Pkcs11DsaTest : public ::testing::TestWithParam { + protected: + void Derive(const uint8_t* sig, size_t sig_len, const uint8_t* spki, + size_t spki_len, const uint8_t* data, size_t data_len, + bool expect_success, const uint32_t test_id, + const SECOidTag hash_oid) { + std::stringstream s; + s << "Test with original ID #" << test_id << " failed.\n"; + s << "Expected Success: " << expect_success << "\n"; + std::string msg = s.str(); + + SECItem spki_item = {siBuffer, toUcharPtr(spki), + static_cast(spki_len)}; + + ScopedCERTSubjectPublicKeyInfo cert_spki( + SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); + ASSERT_TRUE(cert_spki) << msg; + + ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get())); + ASSERT_TRUE(pub_key) << msg; + + SECItem sig_item = {siBuffer, toUcharPtr(sig), + static_cast(sig_len)}; + ScopedSECItem decoded_sig_item( + DSAU_DecodeDerSigToLen(&sig_item, SECKEY_SignatureLen(pub_key.get()))); + if (!decoded_sig_item) { + ASSERT_FALSE(expect_success) << msg; + return; + } + + DataBuffer hash; + hash.Allocate(static_cast(HASH_ResultLenByOidTag(hash_oid))); + SECStatus rv = PK11_HashBuf(hash_oid, toUcharPtr(hash.data()), + toUcharPtr(data), data_len); + ASSERT_EQ(SECSuccess, rv) << msg; + + // Verify. + SECItem hash_item = {siBuffer, toUcharPtr(hash.data()), + static_cast(hash.len())}; + rv = PK11_VerifyWithMechanism(pub_key.get(), CKM_DSA, nullptr, + decoded_sig_item.get(), &hash_item, nullptr); + EXPECT_EQ(expect_success ? SECSuccess : SECFailure, rv); + }; + + void Derive(const DsaTestVector vector) { + Derive(vector.sig.data(), vector.sig.size(), vector.public_key.data(), + vector.public_key.size(), vector.msg.data(), vector.msg.size(), + vector.valid, vector.id, vector.hash_oid); + }; +}; + +TEST_P(Pkcs11DsaTest, WycheproofVectors) { Derive(GetParam()); } + +INSTANTIATE_TEST_CASE_P(DsaTest, Pkcs11DsaTest, + ::testing::ValuesIn(kDsaWycheproofVectors)); + +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_ecdh_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_ecdh_unittest.cc new file mode 100644 index 000000000..959295c14 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_ecdh_unittest.cc @@ -0,0 +1,86 @@ +/* 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 +#include "nss.h" +#include "pk11pub.h" + +#include "cpputil.h" +#include "nss_scoped_ptrs.h" + +#include "testvectors/p256ecdh-vectors.h" +#include "testvectors/p384ecdh-vectors.h" +#include "testvectors/p521ecdh-vectors.h" +#include "gtest/gtest.h" + +namespace nss_test { + +class Pkcs11EcdhTest : public ::testing::TestWithParam { + protected: + void Derive(const EcdhTestVector vec) { + std::string err = "Test #" + std::to_string(vec.id) + " failed"; + + SECItem expect_item = {siBuffer, toUcharPtr(vec.secret.data()), + static_cast(vec.secret.size())}; + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_TRUE(slot); + + SECItem priv_item = {siBuffer, toUcharPtr(vec.private_key.data()), + static_cast(vec.private_key.size())}; + SECKEYPrivateKey* key = nullptr; + SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot.get(), &priv_item, nullptr, nullptr, false, false, KU_ALL, &key, + nullptr); + EXPECT_EQ(SECSuccess, rv) << err; + + ScopedSECKEYPrivateKey priv_key(key); + ASSERT_TRUE(priv_key) << err; + + SECItem spki_item = {siBuffer, toUcharPtr(vec.public_key.data()), + static_cast(vec.public_key.size())}; + + ScopedCERTSubjectPublicKeyInfo cert_spki( + SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); + if (vec.valid) { + ASSERT_TRUE(!!cert_spki) << err; + } else if (!cert_spki) { + ASSERT_TRUE(vec.invalid_asn) << err; + return; + } + + ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get())); + if (vec.valid) { + ASSERT_TRUE(!!pub_key) << err; + } else if (!pub_key) { + ASSERT_FALSE(vec.valid) << err; + return; + } + + ScopedPK11SymKey sym_key( + PK11_PubDeriveWithKDF(priv_key.get(), pub_key.get(), false, nullptr, + nullptr, CKM_ECDH1_DERIVE, CKM_SHA512_HMAC, + CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr)); + ASSERT_EQ(vec.valid, !!sym_key) << err; + + if (vec.valid) { + rv = PK11_ExtractKeyValue(sym_key.get()); + EXPECT_EQ(SECSuccess, rv) << err; + + SECItem* derived_key = PK11_GetKeyData(sym_key.get()); + EXPECT_EQ(0, SECITEM_CompareItem(derived_key, &expect_item)) << err; + } + }; +}; + +TEST_P(Pkcs11EcdhTest, TestVectors) { Derive(GetParam()); } + +INSTANTIATE_TEST_CASE_P(WycheproofP256EcdhTest, Pkcs11EcdhTest, + ::testing::ValuesIn(kP256EcdhWycheproofVectors)); +INSTANTIATE_TEST_CASE_P(WycheproofP384EcdhTest, Pkcs11EcdhTest, + ::testing::ValuesIn(kP384EcdhWycheproofVectors)); +INSTANTIATE_TEST_CASE_P(WycheproofP521EcdhTest, Pkcs11EcdhTest, + ::testing::ValuesIn(kP521EcdhWycheproofVectors)); + +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc index 1816e3c9c..83ac47306 100644 --- a/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc @@ -6,12 +6,16 @@ #include "nss.h" #include "pk11pub.h" #include "sechash.h" +#include "cryptohi.h" #include "gtest/gtest.h" #include "nss_scoped_ptrs.h" #include "pk11_ecdsa_vectors.h" #include "pk11_signature_test.h" +#include "testvectors/p256ecdsa-sha256-vectors.h" +#include "testvectors/p384ecdsa-sha384-vectors.h" +#include "testvectors/p521ecdsa-sha512-vectors.h" namespace nss_test { @@ -172,4 +176,48 @@ TEST_F(Pkcs11EcdsaSha256Test, ImportSpkiPointNotOnCurve) { EXPECT_EQ(handle, static_cast(CK_INVALID_HANDLE)); } +class Pkcs11EcdsaWycheproofTest + : public ::testing::TestWithParam { + protected: + void Derive(const EcdsaTestVector vec) { + SECItem spki_item = {siBuffer, toUcharPtr(vec.public_key.data()), + static_cast(vec.public_key.size())}; + SECItem sig_item = {siBuffer, toUcharPtr(vec.sig.data()), + static_cast(vec.sig.size())}; + + DataBuffer hash; + hash.Allocate(static_cast(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(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_P(Pkcs11EcdsaWycheproofTest, Verify) { Derive(GetParam()); } + +INSTANTIATE_TEST_CASE_P(WycheproofP256SignatureSha256Test, + Pkcs11EcdsaWycheproofTest, + ::testing::ValuesIn(kP256EcdsaSha256Vectors)); + +INSTANTIATE_TEST_CASE_P(WycheproofP384SignatureSha384Test, + Pkcs11EcdsaWycheproofTest, + ::testing::ValuesIn(kP384EcdsaSha384Vectors)); + +INSTANTIATE_TEST_CASE_P(WycheproofP521SignatureSha512Test, + Pkcs11EcdsaWycheproofTest, + ::testing::ValuesIn(kP521EcdsaSha512Vectors)); + } // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_encrypt_derive_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_encrypt_derive_unittest.cc index f4accac02..139346559 100644 --- a/security/nss/gtests/pk11_gtest/pk11_encrypt_derive_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_encrypt_derive_unittest.cc @@ -72,10 +72,12 @@ class EncryptDeriveTest return CKM_CAMELLIA_ECB_ENCRYPT_DATA; case CKM_CAMELLIA_CBC: return CKM_CAMELLIA_CBC_ENCRYPT_DATA; +#ifndef NSS_DISABLE_DEPRECATED_SEED case CKM_SEED_ECB: return CKM_SEED_ECB_ENCRYPT_DATA; case CKM_SEED_CBC: return CKM_SEED_CBC_ENCRYPT_DATA; +#endif default: ADD_FAILURE() << "Unknown mechanism"; break; @@ -93,7 +95,9 @@ class EncryptDeriveTest case CKM_DES3_ECB: case CKM_AES_ECB: case CKM_CAMELLIA_ECB: +#ifndef NSS_DISABLE_DEPRECATED_SEED case CKM_SEED_ECB: +#endif string_data.pData = toUcharPtr(kInput); string_data.ulLen = keysize(); param.data = reinterpret_cast(&string_data); @@ -110,7 +114,9 @@ class EncryptDeriveTest case CKM_AES_CBC: case CKM_CAMELLIA_CBC: +#ifndef NSS_DISABLE_DEPRECATED_SEED case CKM_SEED_CBC: +#endif aes_data.pData = toUcharPtr(kInput); aes_data.length = keysize(); PORT_Memcpy(aes_data.iv, kIv, keysize()); @@ -132,14 +138,18 @@ class EncryptDeriveTest case CKM_DES3_ECB: case CKM_AES_ECB: case CKM_CAMELLIA_ECB: +#ifndef NSS_DISABLE_DEPRECATED_SEED case CKM_SEED_ECB: +#endif // No parameter needed here. break; case CKM_DES3_CBC: case CKM_AES_CBC: case CKM_CAMELLIA_CBC: +#ifndef NSS_DISABLE_DEPRECATED_SEED case CKM_SEED_CBC: +#endif param.data = toUcharPtr(kIv); param.len = keysize(); break; @@ -186,8 +196,13 @@ class EncryptDeriveTest TEST_P(EncryptDeriveTest, Test) { TestEncryptDerive(); } static const CK_MECHANISM_TYPE kEncryptDeriveMechanisms[] = { - CKM_DES3_ECB, CKM_DES3_CBC, CKM_AES_ECB, CKM_AES_ECB, CKM_AES_CBC, - CKM_CAMELLIA_ECB, CKM_CAMELLIA_CBC, CKM_SEED_ECB, CKM_SEED_CBC}; + CKM_DES3_ECB, CKM_DES3_CBC, CKM_AES_ECB, CKM_AES_ECB, CKM_AES_CBC, + CKM_CAMELLIA_ECB, CKM_CAMELLIA_CBC +#ifndef NSS_DISABLE_DEPRECATED_SEED + , + CKM_SEED_ECB, CKM_SEED_CBC +#endif +}; INSTANTIATE_TEST_CASE_P(EncryptDeriveTests, EncryptDeriveTest, ::testing::ValuesIn(kEncryptDeriveMechanisms)); diff --git a/security/nss/gtests/pk11_gtest/pk11_export_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_export_unittest.cc index caf1e6265..bfd65b952 100644 --- a/security/nss/gtests/pk11_gtest/pk11_export_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_export_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/pk11_gtest/pk11_find_certs_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_find_certs_unittest.cc index cfe87020b..9f6baf178 100644 --- a/security/nss/gtests/pk11_gtest/pk11_find_certs_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_find_certs_unittest.cc @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=4 et sw=4 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/. */ @@ -141,11 +142,11 @@ class PK11FindCertsTestBase : public ::testing::Test { mod_spec.append(test_name); mod_spec.append("'"); m_slot = SECMOD_OpenUserDB(mod_spec.c_str()); - ASSERT_NE(m_slot, nullptr); + ASSERT_NE(nullptr, m_slot); } virtual void TearDown() { - ASSERT_EQ(SECMOD_CloseUserDB(m_slot), SECSuccess); + ASSERT_EQ(SECSuccess, SECMOD_CloseUserDB(m_slot)); PK11_FreeSlot(m_slot); std::string test_cert_db_path(test_cert_db_dir_.GetPath()); ASSERT_EQ(0, unlink((test_cert_db_path + "/cert9.db").c_str())); @@ -158,6 +159,41 @@ class PK11FindCertsTestBase : public ::testing::Test { class PK11FindRawCertsBySubjectTest : public PK11FindCertsTestBase {}; +TEST_F(PK11FindCertsTestBase, CertAddListWithData) { + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + ASSERT_TRUE(slot); + SECItem cert1_item = {siBuffer, const_cast(kTestCert1DER.data()), + (unsigned int)kTestCert1DER.size()}; + SECItem cert2_item = {siBuffer, const_cast(kTestCert2DER.data()), + (unsigned int)kTestCert2DER.size()}; + + // Make certificates. ScopedCERTCertList will own. + ScopedCERTCertList list(CERT_NewCertList()); + ASSERT_TRUE(list); + CERTCertificate* cert1 = CERT_NewTempCertificate( + CERT_GetDefaultCertDB(), &cert1_item, nullptr, false, false); + CERTCertificate* cert2 = CERT_NewTempCertificate( + CERT_GetDefaultCertDB(), &cert2_item, nullptr, false, false); + ASSERT_NE(nullptr, cert1); + ASSERT_NE(nullptr, cert2); + ASSERT_NE(cert1, cert2); + + SECStatus rv = CERT_AddCertToListHeadWithData(list.get(), cert1, cert1); + EXPECT_EQ(SECSuccess, rv); + rv = CERT_AddCertToListTailWithData(list.get(), cert2, cert2); + EXPECT_EQ(SECSuccess, rv); + + CERTCertListNode* node = CERT_LIST_HEAD(list.get()); + ASSERT_NE(nullptr, node); + EXPECT_EQ(node->cert, cert1); + EXPECT_EQ(node->appData, cert1); + + node = CERT_LIST_TAIL(list.get()); + ASSERT_NE(nullptr, node); + EXPECT_EQ(node->cert, cert2); + EXPECT_EQ(node->appData, cert2); +} + // If we don't have any certificates, we shouldn't get any when we search for // them. TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsImportedNoCertsFound) { @@ -167,8 +203,8 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsImportedNoCertsFound) { CERTCertificateList* certificates = nullptr; SECStatus rv = PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates); - EXPECT_EQ(rv, SECSuccess); - EXPECT_EQ(certificates, nullptr); + EXPECT_EQ(SECSuccess, rv); + EXPECT_EQ(nullptr, certificates); } // If we have one certificate but it has an unrelated subject DN, we shouldn't @@ -178,9 +214,9 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestOneCertImportedNoCertsFound) { SECItem cert_item = {siBuffer, const_cast(kUnrelatedTestCertDER.data()), (unsigned int)kUnrelatedTestCertDER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, - cert_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, + cert_nickname, false)); SECItem subject_item = { siBuffer, const_cast(kTestCertSubjectDER.data()), @@ -188,8 +224,8 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestOneCertImportedNoCertsFound) { CERTCertificateList* certificates = nullptr; SECStatus rv = PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates); - EXPECT_EQ(rv, SECSuccess); - EXPECT_EQ(certificates, nullptr); + EXPECT_EQ(SECSuccess, rv); + EXPECT_EQ(nullptr, certificates); } TEST_F(PK11FindRawCertsBySubjectTest, TestMultipleMatchingCertsFound) { @@ -197,23 +233,23 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestMultipleMatchingCertsFound) { SECItem cert1_item = {siBuffer, const_cast(kTestCert1DER.data()), (unsigned int)kTestCert1DER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, - cert1_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, + cert1_nickname, false)); char cert2_nickname[] = "Test Cert 2"; SECItem cert2_item = {siBuffer, const_cast(kTestCert2DER.data()), (unsigned int)kTestCert2DER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE, - cert2_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE, + cert2_nickname, false)); char unrelated_cert_nickname[] = "Unrelated Test Cert"; SECItem unrelated_cert_item = { siBuffer, const_cast(kUnrelatedTestCertDER.data()), (unsigned int)kUnrelatedTestCertDER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &unrelated_cert_item, CK_INVALID_HANDLE, - unrelated_cert_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &unrelated_cert_item, CK_INVALID_HANDLE, + unrelated_cert_nickname, false)); CERTCertificateList* certificates = nullptr; SECItem subject_item = { @@ -221,10 +257,10 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestMultipleMatchingCertsFound) { (unsigned int)kTestCertSubjectDER.size()}; SECStatus rv = PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates); - EXPECT_EQ(rv, SECSuccess); - ASSERT_NE(certificates, nullptr); + EXPECT_EQ(SECSuccess, rv); + ASSERT_NE(nullptr, certificates); ScopedCERTCertificateList scoped_certificates(certificates); - ASSERT_EQ(scoped_certificates->len, 2); + ASSERT_EQ(2, scoped_certificates->len); std::vector found_cert1( scoped_certificates->certs[0].data, @@ -244,9 +280,9 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsOnInternalSlots) { SECItem cert1_item = {siBuffer, const_cast(kTestCert1DER.data()), (unsigned int)kTestCert1DER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, - cert1_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, + cert1_nickname, false)); SECItem subject_item = { siBuffer, const_cast(kTestCertSubjectDER.data()), @@ -255,15 +291,15 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsOnInternalSlots) { ScopedPK11SlotInfo internal_key_slot(PK11_GetInternalKeySlot()); SECStatus rv = PK11_FindRawCertsWithSubject( internal_key_slot.get(), &subject_item, &internal_key_slot_certificates); - EXPECT_EQ(rv, SECSuccess); - EXPECT_EQ(internal_key_slot_certificates, nullptr); + EXPECT_EQ(SECSuccess, rv); + EXPECT_EQ(nullptr, internal_key_slot_certificates); CERTCertificateList* internal_slot_certificates = nullptr; ScopedPK11SlotInfo internal_slot(PK11_GetInternalSlot()); rv = PK11_FindRawCertsWithSubject(internal_slot.get(), &subject_item, &internal_slot_certificates); - EXPECT_EQ(rv, SECSuccess); - EXPECT_EQ(internal_slot_certificates, nullptr); + EXPECT_EQ(SECSuccess, rv); + EXPECT_EQ(nullptr, internal_slot_certificates); } // issuer:test cert @@ -304,10 +340,9 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestFindEmptySubject) { SECItem empty_subject_cert_item = { siBuffer, const_cast(kEmptySubjectCertDER.data()), (unsigned int)kEmptySubjectCertDER.size()}; - ASSERT_EQ( - PK11_ImportDERCert(m_slot, &empty_subject_cert_item, CK_INVALID_HANDLE, - empty_subject_cert_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, PK11_ImportDERCert(m_slot, &empty_subject_cert_item, + CK_INVALID_HANDLE, + empty_subject_cert_nickname, false)); SECItem subject_item = {siBuffer, const_cast(kEmptySubjectDER.data()), @@ -315,15 +350,15 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestFindEmptySubject) { CERTCertificateList* certificates = nullptr; SECStatus rv = PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates); - EXPECT_EQ(rv, SECSuccess); - ASSERT_NE(certificates, nullptr); + EXPECT_EQ(SECSuccess, rv); + ASSERT_NE(nullptr, certificates); ScopedCERTCertificateList scoped_certificates(certificates); - ASSERT_EQ(scoped_certificates->len, 1); + ASSERT_EQ(1, scoped_certificates->len); std::vector found_cert( scoped_certificates->certs[0].data, scoped_certificates->certs[0].data + scoped_certificates->certs[0].len); - EXPECT_EQ(found_cert, kEmptySubjectCertDER); + EXPECT_EQ(kEmptySubjectCertDER, found_cert); } // Searching for a zero-length subject doesn't make sense (the minimum subject @@ -334,16 +369,16 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestSearchForNullSubject) { SECItem cert1_item = {siBuffer, const_cast(kTestCert1DER.data()), (unsigned int)kTestCert1DER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, - cert1_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, + cert1_nickname, false)); SECItem subject_item = {siBuffer, nullptr, 0}; CERTCertificateList* certificates = nullptr; SECStatus rv = PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates); - EXPECT_EQ(rv, SECSuccess); - EXPECT_EQ(certificates, nullptr); + EXPECT_EQ(SECSuccess, rv); + EXPECT_EQ(nullptr, certificates); } class PK11GetCertsMatchingPrivateKeyTest : public PK11FindCertsTestBase {}; @@ -407,11 +442,10 @@ TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestNoCertsAtAll) { (unsigned int)kTestPrivateKeyInfoDER.size(), }; SECKEYPrivateKey* priv_key = nullptr; - ASSERT_EQ(PK11_ImportDERPrivateKeyInfoAndReturnKey( - m_slot, &private_key_info, nullptr, nullptr, false, false, - KU_ALL, &priv_key, nullptr), - SECSuccess); - ASSERT_NE(priv_key, nullptr); + ASSERT_EQ(SECSuccess, PK11_ImportDERPrivateKeyInfoAndReturnKey( + m_slot, &private_key_info, nullptr, nullptr, false, + false, KU_ALL, &priv_key, nullptr)); + ASSERT_NE(nullptr, priv_key); ScopedSECKEYPrivateKey scoped_priv_key(priv_key); ScopedCERTCertList certs( PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get())); @@ -425,20 +459,19 @@ TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestNoCertsForKey) { (unsigned int)kTestPrivateKeyInfoDER.size(), }; SECKEYPrivateKey* priv_key = nullptr; - ASSERT_EQ(PK11_ImportDERPrivateKeyInfoAndReturnKey( - m_slot, &private_key_info, nullptr, nullptr, false, false, - KU_ALL, &priv_key, nullptr), - SECSuccess); - ASSERT_NE(priv_key, nullptr); + ASSERT_EQ(SECSuccess, PK11_ImportDERPrivateKeyInfoAndReturnKey( + m_slot, &private_key_info, nullptr, nullptr, false, + false, KU_ALL, &priv_key, nullptr)); + ASSERT_NE(nullptr, priv_key); ScopedSECKEYPrivateKey scoped_priv_key(priv_key); char cert_nickname[] = "Test Cert With Other Key"; SECItem cert_item = { siBuffer, const_cast(kTestCertWithOtherKeyDER.data()), (unsigned int)kTestCertWithOtherKeyDER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, - cert_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, + cert_nickname, false)); ScopedCERTCertList certs( PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get())); @@ -448,8 +481,8 @@ TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestNoCertsForKey) { void CheckCertListForSubjects( ScopedCERTCertList& list, const std::vector& expected_subjects) { - ASSERT_NE(list.get(), nullptr); - ASSERT_NE(expected_subjects.size(), 0ul); + ASSERT_NE(nullptr, list.get()); + ASSERT_NE(0ul, expected_subjects.size()); for (const auto& expected_subject : expected_subjects) { size_t list_length = 0; bool found = false; @@ -462,7 +495,7 @@ void CheckCertListForSubjects( } } ASSERT_TRUE(found); - ASSERT_EQ(list_length, expected_subjects.size()); + ASSERT_EQ(expected_subjects.size(), list_length); } } @@ -473,28 +506,27 @@ TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestOneCertForKey) { (unsigned int)kTestPrivateKeyInfoDER.size(), }; SECKEYPrivateKey* priv_key = nullptr; - ASSERT_EQ(PK11_ImportDERPrivateKeyInfoAndReturnKey( - m_slot, &private_key_info, nullptr, nullptr, false, false, - KU_ALL, &priv_key, nullptr), - SECSuccess); - ASSERT_NE(priv_key, nullptr); + ASSERT_EQ(SECSuccess, PK11_ImportDERPrivateKeyInfoAndReturnKey( + m_slot, &private_key_info, nullptr, nullptr, false, + false, KU_ALL, &priv_key, nullptr)); + ASSERT_NE(nullptr, priv_key); ScopedSECKEYPrivateKey scoped_priv_key(priv_key); char cert1_nickname[] = "Test Cert 1"; SECItem cert1_item = {siBuffer, const_cast(kTestCert1DER.data()), (unsigned int)kTestCert1DER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, - cert1_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, + cert1_nickname, false)); char cert_nickname[] = "Test Cert With Other Key"; SECItem cert_item = { siBuffer, const_cast(kTestCertWithOtherKeyDER.data()), (unsigned int)kTestCertWithOtherKeyDER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, - cert_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, + cert_nickname, false)); ScopedCERTCertList certs( PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get())); @@ -508,39 +540,87 @@ TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestTwoCertsForKey) { (unsigned int)kTestPrivateKeyInfoDER.size(), }; SECKEYPrivateKey* priv_key = nullptr; - ASSERT_EQ(PK11_ImportDERPrivateKeyInfoAndReturnKey( - m_slot, &private_key_info, nullptr, nullptr, false, false, - KU_ALL, &priv_key, nullptr), - SECSuccess); - ASSERT_NE(priv_key, nullptr); + ASSERT_EQ(SECSuccess, PK11_ImportDERPrivateKeyInfoAndReturnKey( + m_slot, &private_key_info, nullptr, nullptr, false, + false, KU_ALL, &priv_key, nullptr)); + ASSERT_NE(nullptr, priv_key); ScopedSECKEYPrivateKey scoped_priv_key(priv_key); char cert1_nickname[] = "Test Cert 1"; SECItem cert1_item = {siBuffer, const_cast(kTestCert1DER.data()), (unsigned int)kTestCert1DER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, - cert1_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, + cert1_nickname, false)); char cert2_nickname[] = "Test Cert 2 (same key, different subject)"; SECItem cert2_item = { siBuffer, const_cast(kUnrelatedTestCertDER.data()), (unsigned int)kUnrelatedTestCertDER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE, - cert2_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE, + cert2_nickname, false)); char cert_nickname[] = "Test Cert With Other Key"; SECItem cert_item = { siBuffer, const_cast(kTestCertWithOtherKeyDER.data()), (unsigned int)kTestCertWithOtherKeyDER.size()}; - ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, - cert_nickname, false), - SECSuccess); + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, + cert_nickname, false)); ScopedCERTCertList certs( PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get())); CheckCertListForSubjects(certs, {"CN=test cert", "CN=unrelated subject DN"}); } +class PK11FindEncodedCertInSlotTest : public PK11FindCertsTestBase {}; + +TEST_F(PK11FindEncodedCertInSlotTest, TestFindEncodedCert) { + char cert_nickname[] = "Test Cert"; + SECItem cert_item = {siBuffer, + const_cast(kTestCert1DER.data()), + (unsigned int)kTestCert1DER.size()}; + ASSERT_EQ(SECSuccess, + PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, + cert_nickname, false)); + + // This certificate was just imported, so finding it by its encoded value + // should succeed. + CK_OBJECT_HANDLE cert_handle_in_slot = + PK11_FindEncodedCertInSlot(m_slot, &cert_item, nullptr); + // CK_INVALID_HANDLE is #defined to be the literal 0, which the compiler + // interprets as a signed value, which then causes a warning-as-an-error + // about comparing values of different signs. + ASSERT_NE(static_cast(CK_INVALID_HANDLE), cert_handle_in_slot); + + // The certificate should not exist on the internal slot, so this should + // return CK_INVALID_HANDLE. + ScopedPK11SlotInfo internal_slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, internal_slot); + CK_OBJECT_HANDLE cert_handle_in_internal_slot = + PK11_FindEncodedCertInSlot(internal_slot.get(), &cert_item, nullptr); + ASSERT_EQ(static_cast(CK_INVALID_HANDLE), + cert_handle_in_internal_slot); + + // The certificate should not exist on the internal key slot, so this should + // return CK_INVALID_HANDLE. + ScopedPK11SlotInfo internal_key_slot(PK11_GetInternalKeySlot()); + ASSERT_NE(nullptr, internal_key_slot); + CK_OBJECT_HANDLE cert_handle_in_internal_key_slot = + PK11_FindEncodedCertInSlot(internal_key_slot.get(), &cert_item, nullptr); + ASSERT_EQ(static_cast(CK_INVALID_HANDLE), + cert_handle_in_internal_key_slot); + + // This certificate hasn't been imported to any token, so looking for it + // should return CK_INVALID_HANDLE. + SECItem unknown_cert_item = {siBuffer, + const_cast(kTestCert2DER.data()), + (unsigned int)kTestCert2DER.size()}; + CK_OBJECT_HANDLE unknown_cert_handle_in_slot = + PK11_FindEncodedCertInSlot(m_slot, &unknown_cert_item, nullptr); + ASSERT_EQ(static_cast(CK_INVALID_HANDLE), + unknown_cert_handle_in_slot); +} + } // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_gtest.gyp b/security/nss/gtests/pk11_gtest/pk11_gtest.gyp index b521687fb..1982fb484 100644 --- a/security/nss/gtests/pk11_gtest/pk11_gtest.gyp +++ b/security/nss/gtests/pk11_gtest/pk11_gtest.gyp @@ -14,6 +14,7 @@ 'pk11_aes_cmac_unittest.cc', 'pk11_aes_gcm_unittest.cc', 'pk11_aeskeywrap_unittest.cc', + 'pk11_aeskeywrapkwp_unittest.cc', 'pk11_aeskeywrappad_unittest.cc', 'pk11_cbc_unittest.cc', 'pk11_chacha20poly1305_unittest.cc', @@ -21,16 +22,24 @@ 'pk11_curve25519_unittest.cc', 'pk11_der_private_key_import_unittest.cc', 'pk11_des_unittest.cc', + 'pk11_dsa_unittest.cc', 'pk11_ecdsa_unittest.cc', + 'pk11_ecdh_unittest.cc', 'pk11_encrypt_derive_unittest.cc', 'pk11_find_certs_unittest.cc', + 'pk11_hkdf_unittest.cc', + 'pk11_hmac_unittest.cc', + 'pk11_hpke_unittest.cc', 'pk11_import_unittest.cc', + 'pk11_kbkdf.cc', 'pk11_keygen.cc', 'pk11_key_unittest.cc', 'pk11_module_unittest.cc', 'pk11_pbkdf2_unittest.cc', 'pk11_prf_unittest.cc', 'pk11_prng_unittest.cc', + 'pk11_rsaencrypt_unittest.cc', + 'pk11_rsaoaep_unittest.cc', 'pk11_rsapkcs1_unittest.cc', 'pk11_rsapss_unittest.cc', 'pk11_seed_cbc_unittest.cc', diff --git a/security/nss/gtests/pk11_gtest/pk11_hkdf_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_hkdf_unittest.cc new file mode 100644 index 000000000..c3fff235b --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_hkdf_unittest.cc @@ -0,0 +1,199 @@ +/* -*- 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 +#include "blapi.h" +#include "gtest/gtest.h" +#include "nss.h" +#include "nss_scoped_ptrs.h" +#include "pk11pub.h" +#include "secerr.h" +#include "sechash.h" +#include "util.h" + +#include "testvectors/hkdf-sha1-vectors.h" +#include "testvectors/hkdf-sha256-vectors.h" +#include "testvectors/hkdf-sha384-vectors.h" +#include "testvectors/hkdf-sha512-vectors.h" + +namespace nss_test { + +enum class HkdfTestType { + legacy, /* CKM_NSS_HKDF_SHA... */ + derive, /* CKM_HKDF_DERIVE, ikm as secret key, salt as data. */ + deriveDataKey, /* CKM_HKDF_DERIVE, ikm as data, salt as data. */ + saltDerive, /* CKM_HKDF_DERIVE, [ikm, salt] as secret key, salt as key. */ + saltDeriveDataKey, /* CKM_HKDF_DERIVE, [ikm, salt] as data, salt as key. */ + hkdfData /* CKM_HKDF_DATA, ikm as data, salt as data. */ +}; +static const HkdfTestType kHkdfTestTypesAll[] = { + HkdfTestType::legacy, + HkdfTestType::derive, + HkdfTestType::deriveDataKey, + HkdfTestType::saltDerive, + HkdfTestType::saltDeriveDataKey, + HkdfTestType::hkdfData, +}; + +class Pkcs11HkdfTest + : public ::testing::TestWithParam< + std::tuple> { + protected: + CK_MECHANISM_TYPE Pk11MechToVendorMech(CK_MECHANISM_TYPE pk11_mech) { + switch (pk11_mech) { + case CKM_SHA_1: + return CKM_NSS_HKDF_SHA1; + case CKM_SHA256: + return CKM_NSS_HKDF_SHA256; + case CKM_SHA384: + return CKM_NSS_HKDF_SHA384; + case CKM_SHA512: + return CKM_NSS_HKDF_SHA512; + default: + ADD_FAILURE() << "Unknown hash mech"; + return CKM_INVALID_MECHANISM; + } + } + + ScopedPK11SymKey ImportKey(SECItem &ikm_item, bool import_as_data) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + ADD_FAILURE() << "Can't get slot"; + return nullptr; + } + + ScopedPK11SymKey ikm; + if (import_as_data) { + ikm.reset(PK11_ImportDataKey(slot.get(), CKM_HKDF_KEY_GEN, + PK11_OriginUnwrap, CKA_SIGN, &ikm_item, + nullptr)); + } else { + ikm.reset(PK11_ImportSymKey(slot.get(), CKM_GENERIC_SECRET_KEY_GEN, + PK11_OriginUnwrap, CKA_SIGN, &ikm_item, + nullptr)); + } + return ikm; + } + + void RunWycheproofTest(const HkdfTestVector &vec, HkdfTestType test_type, + CK_MECHANISM_TYPE hash_mech) { + std::string msg = "Test #" + std::to_string(vec.id) + " failed"; + std::vector vec_ikm = hex_string_to_bytes(vec.ikm); + std::vector vec_okm = hex_string_to_bytes(vec.okm); + std::vector vec_info = hex_string_to_bytes(vec.info); + std::vector vec_salt = hex_string_to_bytes(vec.salt); + SECItem ikm_item = {siBuffer, vec_ikm.data(), + static_cast(vec_ikm.size())}; + SECItem okm_item = {siBuffer, vec_okm.data(), + static_cast(vec_okm.size())}; + SECItem salt_item = {siBuffer, vec_salt.data(), + static_cast(vec_salt.size())}; + CK_MECHANISM_TYPE derive_mech = CKM_HKDF_DERIVE; + ScopedPK11SymKey salt_key = nullptr; + ScopedPK11SymKey ikm = nullptr; + + // Legacy vendor mech params + CK_NSS_HKDFParams nss_hkdf_params = { + true, vec_salt.data(), static_cast(vec_salt.size()), + true, vec_info.data(), static_cast(vec_info.size())}; + + // PKCS #11 v3.0 + CK_HKDF_PARAMS hkdf_params = { + true, + true, + hash_mech, + vec_salt.size() ? CKF_HKDF_SALT_DATA : CKF_HKDF_SALT_NULL, + vec_salt.size() ? vec_salt.data() : nullptr, + static_cast(vec_salt.size()), + CK_INVALID_HANDLE, + vec_info.data(), + static_cast(vec_info.size())}; + SECItem params_item = {siBuffer, (unsigned char *)&hkdf_params, + sizeof(hkdf_params)}; + + switch (test_type) { + case HkdfTestType::legacy: + derive_mech = Pk11MechToVendorMech(hash_mech); + params_item.data = (uint8_t *)&nss_hkdf_params; + params_item.len = sizeof(nss_hkdf_params); + ikm = ImportKey(ikm_item, false); + break; + case HkdfTestType::derive: + ikm = ImportKey(ikm_item, false); + break; + case HkdfTestType::deriveDataKey: + ikm = ImportKey(ikm_item, true); + break; + case HkdfTestType::saltDerive: + ikm = ImportKey(ikm_item, false); + salt_key = ImportKey(salt_item, false); + break; + case HkdfTestType::saltDeriveDataKey: + ikm = ImportKey(ikm_item, true); + salt_key = ImportKey(salt_item, true); + break; + case HkdfTestType::hkdfData: + derive_mech = CKM_HKDF_DATA; + ikm = ImportKey(ikm_item, true); + break; + default: + ADD_FAILURE() << msg; + return; + } + ASSERT_NE(nullptr, ikm) << msg; + + if (test_type == HkdfTestType::saltDerive || + test_type == HkdfTestType::saltDeriveDataKey) { + ASSERT_NE(nullptr, salt_key) << msg; + hkdf_params.ulSaltType = CKF_HKDF_SALT_KEY; + hkdf_params.ulSaltLen = 0; + hkdf_params.pSalt = NULL; + hkdf_params.hSaltKey = PK11_GetSymKeyHandle(salt_key.get()); + } + + ScopedPK11SymKey okm = ScopedPK11SymKey( + PK11_Derive(ikm.get(), derive_mech, ¶ms_item, + CKM_GENERIC_SECRET_KEY_GEN, CKA_DERIVE, vec.size)); + if (vec.valid) { + ASSERT_NE(nullptr, okm.get()) << msg; + ASSERT_EQ(SECSuccess, PK11_ExtractKeyValue(okm.get())) << msg; + ASSERT_EQ(0, SECITEM_CompareItem(&okm_item, PK11_GetKeyData(okm.get()))) + << msg; + } else { + ASSERT_EQ(nullptr, okm.get()) << msg; + } + } +}; + +TEST_P(Pkcs11HkdfTest, WycheproofVectors) { + RunWycheproofTest(std::get<0>(GetParam()), std::get<1>(GetParam()), + std::get<2>(GetParam())); +} + +INSTANTIATE_TEST_CASE_P( + HkdfSha1, Pkcs11HkdfTest, + ::testing::Combine(::testing::ValuesIn(kHkdfSha1WycheproofVectors), + ::testing::ValuesIn(kHkdfTestTypesAll), + ::testing::Values(CKM_SHA_1))); + +INSTANTIATE_TEST_CASE_P( + HkdfSha256, Pkcs11HkdfTest, + ::testing::Combine(::testing::ValuesIn(kHkdfSha256WycheproofVectors), + ::testing::ValuesIn(kHkdfTestTypesAll), + ::testing::Values(CKM_SHA256))); + +INSTANTIATE_TEST_CASE_P( + HkdfSha384, Pkcs11HkdfTest, + ::testing::Combine(::testing::ValuesIn(kHkdfSha384WycheproofVectors), + ::testing::ValuesIn(kHkdfTestTypesAll), + ::testing::Values(CKM_SHA384))); + +INSTANTIATE_TEST_CASE_P( + HkdfSha512, Pkcs11HkdfTest, + ::testing::Combine(::testing::ValuesIn(kHkdfSha512WycheproofVectors), + ::testing::ValuesIn(kHkdfTestTypesAll), + ::testing::Values(CKM_SHA512))); +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_hmac_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_hmac_unittest.cc new file mode 100644 index 000000000..00891d51c --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_hmac_unittest.cc @@ -0,0 +1,74 @@ +/* -*- 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 +#include + +#include "nss.h" +#include "pk11pub.h" +#include "secerr.h" +#include "sechash.h" +#include "blapi.h" +#include "gtest/gtest.h" +#include "nss_scoped_ptrs.h" +#include "testvectors/hmac-sha256-vectors.h" +#include "testvectors/hmac-sha384-vectors.h" +#include "testvectors/hmac-sha512-vectors.h" +#include "util.h" + +namespace nss_test { + +class Pkcs11HmacTest : public ::testing::TestWithParam< + std::tuple> { + protected: + void RunTestVector(const HmacTestVector &vec, CK_MECHANISM_TYPE mech) { + std::string err = "Test #" + std::to_string(vec.id) + " failed"; + std::vector vec_key = hex_string_to_bytes(vec.key); + std::vector vec_mac = hex_string_to_bytes(vec.tag); + std::vector vec_msg = hex_string_to_bytes(vec.msg); + std::vector output(vec_mac.size()); + + // Don't provide a null pointer, even if the input is empty. + uint8_t tmp; + SECItem key = {siBuffer, vec_key.data() ? vec_key.data() : &tmp, + static_cast(vec_key.size())}; + SECItem mac = {siBuffer, vec_mac.data() ? vec_mac.data() : &tmp, + static_cast(vec_mac.size())}; + SECItem msg = {siBuffer, vec_msg.data() ? vec_msg.data() : &tmp, + static_cast(vec_msg.size())}; + SECItem out = {siBuffer, output.data() ? output.data() : &tmp, + static_cast(output.size())}; + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot) << err; + + ScopedPK11SymKey p11_key(PK11_ImportSymKey( + slot.get(), mech, PK11_OriginUnwrap, CKA_SIGN, &key, nullptr)); + ASSERT_NE(nullptr, p11_key.get()) << err; + + SECStatus rv = PK11_SignWithSymKey(p11_key.get(), mech, NULL, &out, &msg); + EXPECT_EQ(SECSuccess, rv) << err; + EXPECT_EQ(!vec.invalid, 0 == SECITEM_CompareItem(&out, &mac)) << err; + } +}; + +TEST_P(Pkcs11HmacTest, WycheproofVectors) { + RunTestVector(std::get<0>(GetParam()), std::get<1>(GetParam())); +} + +INSTANTIATE_TEST_CASE_P( + HmacSha256, Pkcs11HmacTest, + ::testing::Combine(::testing::ValuesIn(kHmacSha256WycheproofVectors), + ::testing::Values(CKM_SHA256_HMAC))); +INSTANTIATE_TEST_CASE_P( + HmacSha384, Pkcs11HmacTest, + ::testing::Combine(::testing::ValuesIn(kHmacSha384WycheproofVectors), + ::testing::Values(CKM_SHA384_HMAC))); +INSTANTIATE_TEST_CASE_P( + HmacSha512, Pkcs11HmacTest, + ::testing::Combine(::testing::ValuesIn(kHmacSha512WycheproofVectors), + ::testing::Values(CKM_SHA512_HMAC))); +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_hpke_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_hpke_unittest.cc new file mode 100644 index 000000000..1858e7f10 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_hpke_unittest.cc @@ -0,0 +1,547 @@ +/* -*- 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 +#include "blapi.h" +#include "gtest/gtest.h" +#include "nss.h" +#include "nss_scoped_ptrs.h" +#include "pk11hpke.h" +#include "pk11pub.h" +#include "secerr.h" +#include "sechash.h" +#include "testvectors/hpke-vectors.h" +#include "util.h" + +namespace nss_test { + +/* See note in pk11pub.h. */ +#ifdef NSS_ENABLE_DRAFT_HPKE +#include "cpputil.h" + +class Pkcs11HpkeTest : public ::testing::TestWithParam { + protected: + void ReadVector(const hpke_vector &vec) { + ScopedPK11SymKey vec_psk; + if (!vec.psk.empty()) { + ASSERT_FALSE(vec.psk_id.empty()); + vec_psk_id = hex_string_to_bytes(vec.psk_id); + + std::vector psk_bytes = hex_string_to_bytes(vec.psk); + SECItem psk_item = {siBuffer, toUcharPtr(psk_bytes.data()), + static_cast(psk_bytes.size())}; + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_TRUE(slot); + PK11SymKey *psk_key = + PK11_ImportSymKey(slot.get(), CKM_HKDF_KEY_GEN, PK11_OriginUnwrap, + CKA_WRAP, &psk_item, nullptr); + ASSERT_NE(nullptr, psk_key); + vec_psk_key.reset(psk_key); + } + + vec_pkcs8_r = hex_string_to_bytes(vec.pkcs8_r); + vec_pkcs8_e = hex_string_to_bytes(vec.pkcs8_e); + vec_key = hex_string_to_bytes(vec.key); + vec_nonce = hex_string_to_bytes(vec.nonce); + vec_enc = hex_string_to_bytes(vec.enc); + vec_info = hex_string_to_bytes(vec.info); + vec_encryptions = vec.encrypt_vecs; + vec_exports = vec.export_vecs; + } + + void CheckEquality(const std::vector &expected, SECItem *actual) { + if (!actual) { + EXPECT_TRUE(expected.empty()); + return; + } + std::vector vact(actual->data, actual->data + actual->len); + EXPECT_EQ(expected, vact); + } + + void CheckEquality(SECItem *expected, SECItem *actual) { + EXPECT_EQ(!!expected, !!actual); + if (expected && actual) { + EXPECT_EQ(expected->len, actual->len); + if (expected->len == actual->len) { + EXPECT_EQ(0, memcmp(expected->data, actual->data, actual->len)); + } + } + } + + void CheckEquality(const std::vector &expected, PK11SymKey *actual) { + if (!actual) { + EXPECT_TRUE(expected.empty()); + return; + } + SECStatus rv = PK11_ExtractKeyValue(actual); + EXPECT_EQ(SECSuccess, rv); + if (rv != SECSuccess) { + return; + } + SECItem *rawkey = PK11_GetKeyData(actual); + CheckEquality(expected, rawkey); + } + + void CheckEquality(PK11SymKey *expected, PK11SymKey *actual) { + if (!actual || !expected) { + EXPECT_EQ(!!expected, !!actual); + return; + } + SECStatus rv = PK11_ExtractKeyValue(expected); + EXPECT_EQ(SECSuccess, rv); + if (rv != SECSuccess) { + return; + } + SECItem *raw = PK11_GetKeyData(expected); + ASSERT_NE(nullptr, raw); + ASSERT_NE(nullptr, raw->data); + std::vector expected_vec(raw->data, raw->data + raw->len); + CheckEquality(expected_vec, actual); + } + + void SetupS(const ScopedHpkeContext &cx, const ScopedSECKEYPublicKey &pkE, + const ScopedSECKEYPrivateKey &skE, + const ScopedSECKEYPublicKey &pkR, + const std::vector &info) { + SECItem info_item = {siBuffer, toUcharPtr(vec_info.data()), + static_cast(vec_info.size())}; + SECStatus rv = + PK11_HPKE_SetupS(cx.get(), pkE.get(), skE.get(), pkR.get(), &info_item); + EXPECT_EQ(SECSuccess, rv); + } + + void SetupR(const ScopedHpkeContext &cx, const ScopedSECKEYPublicKey &pkR, + const ScopedSECKEYPrivateKey &skR, + const std::vector &enc, + const std::vector &info) { + SECItem enc_item = {siBuffer, toUcharPtr(enc.data()), + static_cast(enc.size())}; + SECItem info_item = {siBuffer, toUcharPtr(vec_info.data()), + static_cast(vec_info.size())}; + SECStatus rv = + PK11_HPKE_SetupR(cx.get(), pkR.get(), skR.get(), &enc_item, &info_item); + EXPECT_EQ(SECSuccess, rv); + } + + void Seal(const ScopedHpkeContext &cx, std::vector &aad_vec, + std::vector &pt_vec, SECItem **out_ct) { + SECItem aad_item = {siBuffer, toUcharPtr(aad_vec.data()), + static_cast(aad_vec.size())}; + SECItem pt_item = {siBuffer, toUcharPtr(pt_vec.data()), + static_cast(pt_vec.size())}; + + SECStatus rv = PK11_HPKE_Seal(cx.get(), &aad_item, &pt_item, out_ct); + EXPECT_EQ(SECSuccess, rv); + } + + void Open(const ScopedHpkeContext &cx, std::vector &aad_vec, + std::vector &ct_vec, SECItem **out_pt) { + SECItem aad_item = {siBuffer, toUcharPtr(aad_vec.data()), + static_cast(aad_vec.size())}; + SECItem ct_item = {siBuffer, toUcharPtr(ct_vec.data()), + static_cast(ct_vec.size())}; + SECStatus rv = PK11_HPKE_Open(cx.get(), &aad_item, &ct_item, out_pt); + EXPECT_EQ(SECSuccess, rv); + } + + void TestExports(const ScopedHpkeContext &sender, + const ScopedHpkeContext &receiver) { + SECStatus rv; + + for (auto &vec : vec_exports) { + std::vector context = hex_string_to_bytes(vec.ctxt); + std::vector expected = hex_string_to_bytes(vec.exported); + SECItem context_item = {siBuffer, toUcharPtr(context.data()), + static_cast(context.size())}; + PK11SymKey *actual_r = nullptr; + PK11SymKey *actual_s = nullptr; + rv = PK11_HPKE_ExportSecret(sender.get(), &context_item, vec.len, + &actual_s); + ASSERT_EQ(SECSuccess, rv); + rv = PK11_HPKE_ExportSecret(receiver.get(), &context_item, vec.len, + &actual_r); + ASSERT_EQ(SECSuccess, rv); + ScopedPK11SymKey scoped_act_s(actual_s); + ScopedPK11SymKey scoped_act_r(actual_r); + CheckEquality(expected, scoped_act_s.get()); + CheckEquality(expected, scoped_act_r.get()); + } + } + + void TestEncryptions(const ScopedHpkeContext &sender, + const ScopedHpkeContext &receiver) { + for (auto &enc_vec : vec_encryptions) { + std::vector msg = hex_string_to_bytes(enc_vec.pt); + std::vector aad = hex_string_to_bytes(enc_vec.aad); + std::vector expect_ct = hex_string_to_bytes(enc_vec.ct); + SECItem *act_ct = nullptr; + Seal(sender, aad, msg, &act_ct); + CheckEquality(expect_ct, act_ct); + ScopedSECItem scoped_ct(act_ct); + + SECItem *act_pt = nullptr; + Open(receiver, aad, expect_ct, &act_pt); + CheckEquality(msg, act_pt); + ScopedSECItem scoped_pt(act_pt); + } + } + + void ImportKeyPairs(const ScopedHpkeContext &sender, + const ScopedHpkeContext &receiver) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + ADD_FAILURE() << "No slot"; + return; + } + + SECItem pkcs8_e_item = {siBuffer, toUcharPtr(vec_pkcs8_e.data()), + static_cast(vec_pkcs8_e.size())}; + SECKEYPrivateKey *sk_e = nullptr; + SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot.get(), &pkcs8_e_item, nullptr, nullptr, false, false, KU_ALL, + &sk_e, nullptr); + EXPECT_EQ(SECSuccess, rv); + skE_derived.reset(sk_e); + SECKEYPublicKey *pk_e = SECKEY_ConvertToPublicKey(skE_derived.get()); + ASSERT_NE(nullptr, pk_e); + pkE_derived.reset(pk_e); + + SECItem pkcs8_r_item = {siBuffer, toUcharPtr(vec_pkcs8_r.data()), + static_cast(vec_pkcs8_r.size())}; + SECKEYPrivateKey *sk_r = nullptr; + rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot.get(), &pkcs8_r_item, nullptr, nullptr, false, false, KU_ALL, + &sk_r, nullptr); + EXPECT_EQ(SECSuccess, rv); + skR_derived.reset(sk_r); + SECKEYPublicKey *pk_r = SECKEY_ConvertToPublicKey(skR_derived.get()); + ASSERT_NE(nullptr, pk_r); + pkR_derived.reset(pk_r); + } + + void SetupSenderReceiver(const ScopedHpkeContext &sender, + const ScopedHpkeContext &receiver) { + SetupS(sender, pkE_derived, skE_derived, pkR_derived, vec_info); + uint8_t buf[32]; // Curve25519 only, fixed size. + SECItem encap_item = {siBuffer, const_cast(buf), sizeof(buf)}; + SECStatus rv = PK11_HPKE_Serialize(pkE_derived.get(), encap_item.data, + &encap_item.len, encap_item.len); + ASSERT_EQ(SECSuccess, rv); + CheckEquality(vec_enc, &encap_item); + SetupR(receiver, pkR_derived, skR_derived, vec_enc, vec_info); + } + + bool GenerateKeyPair(ScopedSECKEYPublicKey &pub_key, + ScopedSECKEYPrivateKey &priv_key) { + unsigned char param_buf[65]; + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + ADD_FAILURE() << "Couldn't get slot"; + return false; + } + + SECItem ecdsa_params = {siBuffer, param_buf, sizeof(param_buf)}; + SECOidData *oid_data = SECOID_FindOIDByTag(SEC_OID_CURVE25519); + if (!oid_data) { + ADD_FAILURE() << "Couldn't get oid_data"; + return false; + } + + ecdsa_params.data[0] = SEC_ASN1_OBJECT_ID; + ecdsa_params.data[1] = oid_data->oid.len; + memcpy(ecdsa_params.data + 2, oid_data->oid.data, oid_data->oid.len); + ecdsa_params.len = oid_data->oid.len + 2; + SECKEYPublicKey *pub_tmp; + SECKEYPrivateKey *priv_tmp; + priv_tmp = + PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsa_params, + &pub_tmp, PR_FALSE, PR_TRUE, nullptr); + if (!pub_tmp || !priv_tmp) { + ADD_FAILURE() << "PK11_GenerateKeyPair failed"; + return false; + } + + pub_key.reset(pub_tmp); + priv_key.reset(priv_tmp); + return true; + } + + void RunTestVector(const hpke_vector &vec) { + ReadVector(vec); + SECItem psk_id_item = {siBuffer, toUcharPtr(vec_psk_id.data()), + static_cast(vec_psk_id.size())}; + PK11SymKey *psk = vec_psk_key ? vec_psk_key.get() : nullptr; + SECItem *psk_id = psk ? &psk_id_item : nullptr; + + ScopedHpkeContext sender( + PK11_HPKE_NewContext(vec.kem_id, vec.kdf_id, vec.aead_id, psk, psk_id)); + ScopedHpkeContext receiver( + PK11_HPKE_NewContext(vec.kem_id, vec.kdf_id, vec.aead_id, psk, psk_id)); + ASSERT_TRUE(sender); + ASSERT_TRUE(receiver); + + ImportKeyPairs(sender, receiver); + SetupSenderReceiver(sender, receiver); + TestEncryptions(sender, receiver); + TestExports(sender, receiver); + } + + private: + ScopedPK11SymKey vec_psk_key; + std::vector vec_psk_id; + std::vector vec_pkcs8_e; + std::vector vec_pkcs8_r; + std::vector vec_enc; + std::vector vec_info; + std::vector vec_key; + std::vector vec_nonce; + std::vector vec_encryptions; + std::vector vec_exports; + ScopedSECKEYPublicKey pkE_derived; + ScopedSECKEYPublicKey pkR_derived; + ScopedSECKEYPrivateKey skE_derived; + ScopedSECKEYPrivateKey skR_derived; +}; + +TEST_P(Pkcs11HpkeTest, TestVectors) { RunTestVector(GetParam()); } + +INSTANTIATE_TEST_CASE_P(Pkcs11HpkeTests, Pkcs11HpkeTest, + ::testing::ValuesIn(kHpkeTestVectors)); + +TEST_F(Pkcs11HpkeTest, BadEncapsulatedPubKey) { + ScopedHpkeContext sender( + PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256, + HpkeAeadAes128Gcm, nullptr, nullptr)); + ScopedHpkeContext receiver( + PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256, + HpkeAeadAes128Gcm, nullptr, nullptr)); + + SECItem empty = {siBuffer, nullptr, 0}; + uint8_t buf[100]; + SECItem short_encap = {siBuffer, buf, 1}; + SECItem long_encap = {siBuffer, buf, sizeof(buf)}; + + SECKEYPublicKey *tmp_pub_key; + ScopedSECKEYPublicKey pub_key; + ScopedSECKEYPrivateKey priv_key; + ASSERT_TRUE(GenerateKeyPair(pub_key, priv_key)); + + // Decapsulating an empty buffer should fail. + SECStatus rv = + PK11_HPKE_Deserialize(sender.get(), empty.data, empty.len, &tmp_pub_key); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); + + // Decapsulating anything else will succeed, but the setup will fail. + rv = PK11_HPKE_Deserialize(sender.get(), short_encap.data, short_encap.len, + &tmp_pub_key); + ScopedSECKEYPublicKey bad_pub_key(tmp_pub_key); + EXPECT_EQ(SECSuccess, rv); + + rv = PK11_HPKE_SetupS(receiver.get(), pub_key.get(), priv_key.get(), + bad_pub_key.get(), &empty); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(SEC_ERROR_INVALID_KEY, PORT_GetError()); + + // Test the same for a receiver. + rv = PK11_HPKE_SetupR(sender.get(), pub_key.get(), priv_key.get(), &empty, + &empty); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); + + rv = PK11_HPKE_SetupR(sender.get(), pub_key.get(), priv_key.get(), + &short_encap, &empty); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(SEC_ERROR_INVALID_KEY, PORT_GetError()); + + // Encapsulated key too long + rv = PK11_HPKE_Deserialize(sender.get(), long_encap.data, long_encap.len, + &tmp_pub_key); + bad_pub_key.reset(tmp_pub_key); + EXPECT_EQ(SECSuccess, rv); + + rv = PK11_HPKE_SetupS(receiver.get(), pub_key.get(), priv_key.get(), + bad_pub_key.get(), &empty); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); + + rv = PK11_HPKE_SetupR(sender.get(), pub_key.get(), priv_key.get(), + &long_encap, &empty); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); +} + +// Vectors used fixed keypairs on each end. Make sure the +// ephemeral (particularly sender) path works. +TEST_F(Pkcs11HpkeTest, EphemeralKeys) { + unsigned char info[] = {"info"}; + unsigned char msg[] = {"secret"}; + unsigned char aad[] = {"aad"}; + SECItem info_item = {siBuffer, info, sizeof(info)}; + SECItem msg_item = {siBuffer, msg, sizeof(msg)}; + SECItem aad_item = {siBuffer, aad, sizeof(aad)}; + + ScopedHpkeContext sender( + PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256, + HpkeAeadAes128Gcm, nullptr, nullptr)); + ScopedHpkeContext receiver( + PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256, + HpkeAeadAes128Gcm, nullptr, nullptr)); + ASSERT_TRUE(sender); + ASSERT_TRUE(receiver); + + ScopedSECKEYPublicKey pub_key_r; + ScopedSECKEYPrivateKey priv_key_r; + ASSERT_TRUE(GenerateKeyPair(pub_key_r, priv_key_r)); + + SECStatus rv = PK11_HPKE_SetupS(sender.get(), nullptr, nullptr, + pub_key_r.get(), &info_item); + EXPECT_EQ(SECSuccess, rv); + + const SECItem *enc = PK11_HPKE_GetEncapPubKey(sender.get()); + EXPECT_NE(nullptr, enc); + rv = PK11_HPKE_SetupR(receiver.get(), pub_key_r.get(), priv_key_r.get(), + const_cast(enc), &info_item); + EXPECT_EQ(SECSuccess, rv); + + SECItem *tmp_sealed = nullptr; + rv = PK11_HPKE_Seal(sender.get(), &aad_item, &msg_item, &tmp_sealed); + EXPECT_EQ(SECSuccess, rv); + ScopedSECItem sealed(tmp_sealed); + + SECItem *tmp_unsealed = nullptr; + rv = PK11_HPKE_Open(receiver.get(), &aad_item, sealed.get(), &tmp_unsealed); + EXPECT_EQ(SECSuccess, rv); + CheckEquality(&msg_item, tmp_unsealed); + ScopedSECItem unsealed(tmp_unsealed); + + // Once more + tmp_sealed = nullptr; + rv = PK11_HPKE_Seal(sender.get(), &aad_item, &msg_item, &tmp_sealed); + EXPECT_EQ(SECSuccess, rv); + ASSERT_NE(nullptr, sealed); + sealed.reset(tmp_sealed); + tmp_unsealed = nullptr; + rv = PK11_HPKE_Open(receiver.get(), &aad_item, sealed.get(), &tmp_unsealed); + EXPECT_EQ(SECSuccess, rv); + CheckEquality(&msg_item, tmp_unsealed); + unsealed.reset(tmp_unsealed); + + // Seal for negative tests + tmp_sealed = nullptr; + tmp_unsealed = nullptr; + rv = PK11_HPKE_Seal(sender.get(), &aad_item, &msg_item, &tmp_sealed); + EXPECT_EQ(SECSuccess, rv); + ASSERT_NE(nullptr, sealed); + sealed.reset(tmp_sealed); + + // Drop AAD + rv = PK11_HPKE_Open(receiver.get(), nullptr, sealed.get(), &tmp_unsealed); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(nullptr, tmp_unsealed); + + // Modify AAD + aad_item.data[0] ^= 0xff; + rv = PK11_HPKE_Open(receiver.get(), &aad_item, sealed.get(), &tmp_unsealed); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(nullptr, tmp_unsealed); + aad_item.data[0] ^= 0xff; + + // Modify ciphertext + sealed->data[0] ^= 0xff; + rv = PK11_HPKE_Open(receiver.get(), &aad_item, sealed.get(), &tmp_unsealed); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(nullptr, tmp_unsealed); + sealed->data[0] ^= 0xff; + + rv = PK11_HPKE_Open(receiver.get(), &aad_item, sealed.get(), &tmp_unsealed); + EXPECT_EQ(SECSuccess, rv); + EXPECT_NE(nullptr, tmp_unsealed); + unsealed.reset(tmp_unsealed); +} + +TEST_F(Pkcs11HpkeTest, InvalidContextParams) { + HpkeContext *cx = + PK11_HPKE_NewContext(static_cast(1), HpkeKdfHkdfSha256, + HpkeAeadChaCha20Poly1305, nullptr, nullptr); + EXPECT_EQ(nullptr, cx); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); + + cx = PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, static_cast(2), + HpkeAeadChaCha20Poly1305, nullptr, nullptr); + EXPECT_EQ(nullptr, cx); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); + cx = PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256, + static_cast(4), nullptr, nullptr); + EXPECT_EQ(nullptr, cx); + EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); +} + +TEST_F(Pkcs11HpkeTest, InvalidReceiverKeyType) { + ScopedHpkeContext sender( + PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256, + HpkeAeadChaCha20Poly1305, nullptr, nullptr)); + ASSERT_TRUE(!!sender); + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + ADD_FAILURE() << "No slot"; + return; + } + + // Give the client an RSA key + PK11RSAGenParams rsa_param; + rsa_param.keySizeInBits = 1024; + rsa_param.pe = 65537L; + SECKEYPublicKey *pub_tmp; + ScopedSECKEYPublicKey pub_key; + ScopedSECKEYPrivateKey priv_key( + PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, &rsa_param, + &pub_tmp, PR_FALSE, PR_FALSE, nullptr)); + ASSERT_NE(nullptr, priv_key); + ASSERT_NE(nullptr, pub_tmp); + pub_key.reset(pub_tmp); + + SECItem info_item = {siBuffer, nullptr, 0}; + SECStatus rv = PK11_HPKE_SetupS(sender.get(), nullptr, nullptr, pub_key.get(), + &info_item); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(SEC_ERROR_BAD_KEY, PORT_GetError()); + + // Try with an unexpected curve + StackSECItem ecParams; + SECOidData *oidData = SECOID_FindOIDByTag(SEC_OID_ANSIX962_EC_PRIME256V1); + ASSERT_NE(oidData, nullptr); + if (!SECITEM_AllocItem(nullptr, &ecParams, (2 + oidData->oid.len))) { + FAIL() << "Couldn't allocate memory for OID."; + } + ecParams.data[0] = SEC_ASN1_OBJECT_ID; + ecParams.data[1] = oidData->oid.len; + memcpy(ecParams.data + 2, oidData->oid.data, oidData->oid.len); + + priv_key.reset(PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, + &ecParams, &pub_tmp, PR_FALSE, PR_FALSE, + nullptr)); + ASSERT_NE(nullptr, priv_key); + ASSERT_NE(nullptr, pub_tmp); + pub_key.reset(pub_tmp); + rv = PK11_HPKE_SetupS(sender.get(), nullptr, nullptr, pub_key.get(), + &info_item); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(SEC_ERROR_BAD_KEY, PORT_GetError()); +} +#else +TEST(Pkcs11HpkeTest, EnsureNotImplemented) { + ScopedHpkeContext cx( + PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256, + HpkeAeadChaCha20Poly1305, nullptr, nullptr)); + EXPECT_FALSE(cx.get()); + EXPECT_EQ(SEC_ERROR_INVALID_ALGORITHM, PORT_GetError()); +} +#endif // NSS_ENABLE_DRAFT_HPKE + +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_import_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_import_unittest.cc index 3ca184b5f..19ecb94a2 100644 --- a/security/nss/gtests/pk11_gtest/pk11_import_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_import_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/pk11_gtest/pk11_kbkdf.cc b/security/nss/gtests/pk11_gtest/pk11_kbkdf.cc new file mode 100644 index 000000000..d8a0a0f58 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_kbkdf.cc @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 2; 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 +#include "nss.h" +#include "pk11pub.h" +#include "secerr.h" +#include "sechash.h" +#include "stdio.h" + +#include "blapi.h" + +#include "gtest/gtest.h" +#include "nss_scoped_ptrs.h" +#include "util.h" + +namespace nss_test { +class Pkcs11KbkdfTest : public ::testing::Test { + protected: + ScopedPK11SymKey ImportKey(CK_MECHANISM_TYPE mech, SECItem *key_item) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + ADD_FAILURE() << "Can't get slot"; + return nullptr; + } + + ScopedPK11SymKey result(PK11_ImportSymKey( + slot.get(), mech, PK11_OriginUnwrap, CKA_SIGN, key_item, nullptr)); + + return result; + } + + void RunKDF(CK_MECHANISM_TYPE kdfMech, CK_SP800_108_KDF_PARAMS_PTR kdfParams, + CK_BYTE_PTR inputKey, unsigned int inputKeyLen, + CK_BYTE_PTR expectedKey, unsigned int expectedKeyLen, + CK_BYTE_PTR expectedAdditional, + unsigned int expectedAdditionalLen) { + SECItem keyItem = {siBuffer, inputKey, inputKeyLen}; + ScopedPK11SymKey p11Key = ImportKey(kdfParams->prfType, &keyItem); + + ASSERT_NE(kdfParams, nullptr); + SECItem paramsItem = {siBuffer, (unsigned char *)kdfParams, + sizeof(*kdfParams)}; + + ScopedPK11SymKey result(PK11_Derive(p11Key.get(), kdfMech, ¶msItem, + CKM_SHA512_HMAC, CKA_SIGN, + expectedKeyLen)); + ASSERT_NE(result, nullptr); + + ASSERT_EQ(PK11_ExtractKeyValue(result.get()), SECSuccess); + + /* We don't need to free this -- it is just a reference... */ + SECItem *actualItem = PK11_GetKeyData(result.get()); + ASSERT_NE(actualItem, nullptr); + + SECItem expectedItem = {siBuffer, expectedKey, expectedKeyLen}; + ASSERT_EQ(SECITEM_CompareItem(actualItem, &expectedItem), 0); + + /* Extract the additional key. */ + if (expectedAdditional == NULL || kdfParams->ulAdditionalDerivedKeys != 1) { + return; + } + + ScopedPK11SlotInfo slot(PK11_GetSlotFromKey(result.get())); + + CK_OBJECT_HANDLE_PTR keyHandle = kdfParams->pAdditionalDerivedKeys[0].phKey; + ScopedPK11SymKey additionalKey( + PK11_SymKeyFromHandle(slot.get(), result.get(), PK11_OriginDerive, + CKM_SHA512_HMAC, *keyHandle, PR_FALSE, NULL)); + + ASSERT_EQ(PK11_ExtractKeyValue(additionalKey.get()), SECSuccess); + + /* We don't need to free this -- it is just a reference... */ + actualItem = PK11_GetKeyData(additionalKey.get()); + ASSERT_NE(actualItem, nullptr); + + expectedItem = {siBuffer, expectedAdditional, expectedAdditionalLen}; + ASSERT_EQ(SECITEM_CompareItem(actualItem, &expectedItem), 0); + } +}; + +TEST_F(Pkcs11KbkdfTest, TestAdditionalKey) { + /* Test number 11 of NIST CAVP vectors for Counter mode KDF, with counter + * after a fixed input (AES/128 CMAC). Resulting key (of size 256 bits) + * split into two 128-bit chunks since that aligns with a PRF invocation + * boundary. */ + CK_BYTE inputKey[] = {0x23, 0xeb, 0x06, 0x5b, 0xe1, 0x27, 0xa8, 0x81, + 0xe3, 0x5a, 0x65, 0x14, 0xd4, 0x35, 0x67, 0x9f}; + CK_BYTE expectedKey[] = {0xea, 0x4e, 0xbb, 0xb4, 0xef, 0xff, 0x4b, 0x01, + 0x68, 0x40, 0x12, 0xed, 0x8f, 0xf9, 0xc6, 0x4e}; + CK_BYTE expectedAdditional[] = {0x70, 0xae, 0x38, 0x19, 0x7c, 0x36, + 0x44, 0x5a, 0x6c, 0x80, 0x4a, 0x0e, + 0x44, 0x81, 0x9a, 0xc3}; + + CK_SP800_108_COUNTER_FORMAT iterator = {CK_FALSE, 8}; + CK_BYTE fixedData[] = { + 0xe6, 0x79, 0x86, 0x1a, 0x61, 0x34, 0x65, 0xa6, 0x73, 0x85, 0x37, 0x26, + 0x71, 0xb1, 0x07, 0xe6, 0xb8, 0x95, 0xa2, 0xf6, 0x40, 0x43, 0xc9, 0x34, + 0xff, 0x42, 0x56, 0xa7, 0xe6, 0x3c, 0xfb, 0x8b, 0xfa, 0xcc, 0x21, 0x24, + 0x25, 0x1c, 0x90, 0xfa, 0x67, 0x0d, 0x45, 0x74, 0x5c, 0x1c, 0x35, 0xda, + 0x9b, 0x6e, 0x05, 0xaf, 0x77, 0xea, 0x9c, 0x4a, 0xd4, 0x86, 0xfd, 0x1a}; + + CK_PRF_DATA_PARAM dataParams[] = { + {CK_SP800_108_BYTE_ARRAY, fixedData, + sizeof(fixedData) / sizeof(*fixedData)}, + {CK_SP800_108_ITERATION_VARIABLE, &iterator, sizeof(iterator)}}; + + CK_KEY_TYPE ckGeneric = CKK_GENERIC_SECRET; + CK_OBJECT_CLASS ckClass = CKO_SECRET_KEY; + CK_ULONG derivedLength = 16; + + CK_ATTRIBUTE derivedTemplate[] = { + {CKA_CLASS, &ckClass, sizeof(ckClass)}, + {CKA_KEY_TYPE, &ckGeneric, sizeof(ckGeneric)}, + {CKA_VALUE_LEN, &derivedLength, sizeof(derivedLength)}}; + + CK_OBJECT_HANDLE keyHandle; + CK_DERIVED_KEY derivedKey = { + derivedTemplate, sizeof(derivedTemplate) / sizeof(*derivedTemplate), + &keyHandle}; + + CK_SP800_108_KDF_PARAMS kdfParams = {CKM_AES_CMAC, + sizeof(dataParams) / sizeof(*dataParams), + dataParams, 1, &derivedKey}; + + RunKDF(CKM_SP800_108_COUNTER_KDF, &kdfParams, inputKey, + sizeof(inputKey) / sizeof(*inputKey), expectedKey, + sizeof(expectedKey) / sizeof(*expectedKey), expectedAdditional, + sizeof(expectedAdditional) / sizeof(*expectedAdditional)); +} + +// Close the namespace +} diff --git a/security/nss/gtests/pk11_gtest/pk11_module_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_module_unittest.cc index ec4667df8..9627c823e 100644 --- a/security/nss/gtests/pk11_gtest/pk11_module_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_module_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/pk11_gtest/pk11_pbkdf2_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_pbkdf2_unittest.cc index a2118ef93..503654061 100644 --- a/security/nss/gtests/pk11_gtest/pk11_pbkdf2_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_pbkdf2_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -50,6 +51,7 @@ class Pkcs11Pbkdf2Test : public ::testing::Test { const unsigned int kIterations = 10; std::string pass("passwordPASSWORDpassword"); std::string salt("saltSALTsaltSALTsaltSALTsaltSALTsalt"); + std::string salt_empty(""); // Derivation must fail when using key sizes bigger than MAX_KEY_LEN. const int big_key_size = 768; @@ -59,6 +61,10 @@ class Pkcs11Pbkdf2Test : public ::testing::Test { const int zero_key_size = 0; EXPECT_TRUE(KeySizeParam(pass, salt, zero_key_size, hash_alg, kIterations)); + // Zero is acceptable as salt size and will be managed internally. + EXPECT_TRUE( + KeySizeParam(pass, salt_empty, zero_key_size, hash_alg, kIterations)); + // -1 will be set to 0 internally and this means that the key size will be // obtained from the template. If the template doesn't have this defined, // it must fail. @@ -70,6 +76,12 @@ class Pkcs11Pbkdf2Test : public ::testing::Test { const int negative_key_size = -10; EXPECT_FALSE( KeySizeParam(pass, salt, negative_key_size, hash_alg, kIterations)); + + // Malformed inputs are handled without crashing + EXPECT_FALSE( + MalformedPass(pass, salt, big_key_size, hash_alg, kIterations)); + EXPECT_FALSE( + MalformedSalt(pass, salt, big_key_size, hash_alg, kIterations)); } private: @@ -98,13 +110,8 @@ class Pkcs11Pbkdf2Test : public ::testing::Test { return !memcmp(&derived[0], key_data->data, key_data->len); } - bool KeySizeParam(std::string& pass, std::string& salt, const int key_size, - SECOidTag hash_alg, unsigned int kIterations) { - SECItem pass_item = {siBuffer, ToUcharPtr(pass), - static_cast(pass.length())}; - SECItem salt_item = {siBuffer, ToUcharPtr(salt), - static_cast(salt.length())}; - + bool GenerateKey(SECItem pass_item, SECItem salt_item, const int key_size, + SECOidTag hash_alg, unsigned int kIterations) { // Set up PBKDF2 params. ScopedSECAlgorithmID alg_id( PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, hash_alg, hash_alg, @@ -118,6 +125,34 @@ class Pkcs11Pbkdf2Test : public ::testing::Test { // Should be nullptr if fail. return sym_key.get(); } + + bool KeySizeParam(std::string& pass, std::string& salt, const int key_size, + SECOidTag hash_alg, unsigned int kIterations) { + SECItem pass_item = {siBuffer, ToUcharPtr(pass), + static_cast(pass.length())}; + SECItem salt_item = {siBuffer, ToUcharPtr(salt), + static_cast(salt.length())}; + + return GenerateKey(pass_item, salt_item, key_size, hash_alg, kIterations); + } + + bool MalformedSalt(std::string& pass, std::string& salt, const int key_size, + SECOidTag hash_alg, unsigned int kIterations) { + SECItem pass_item = {siBuffer, ToUcharPtr(pass), + static_cast(pass.length())}; + SECItem salt_item = {siBuffer, nullptr, 0}; + + return GenerateKey(pass_item, salt_item, key_size, hash_alg, kIterations); + } + + bool MalformedPass(std::string& pass, std::string& salt, const int key_size, + SECOidTag hash_alg, unsigned int kIterations) { + SECItem pass_item = {siBuffer, nullptr, 0}; + SECItem salt_item = {siBuffer, ToUcharPtr(salt), + static_cast(salt.length())}; + + return GenerateKey(pass_item, salt_item, key_size, hash_alg, kIterations); + } }; // RFC 6070 diff --git a/security/nss/gtests/pk11_gtest/pk11_prf_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_prf_unittest.cc index e79dac5e4..3580b10a2 100644 --- a/security/nss/gtests/pk11_gtest/pk11_prf_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_prf_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/pk11_gtest/pk11_prng_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_prng_unittest.cc index 52fd91de0..ef05fe51c 100644 --- a/security/nss/gtests/pk11_gtest/pk11_prng_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_prng_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ diff --git a/security/nss/gtests/pk11_gtest/pk11_rsaencrypt_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_rsaencrypt_unittest.cc new file mode 100644 index 000000000..6c5635f6c --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_rsaencrypt_unittest.cc @@ -0,0 +1,127 @@ +/* -*- 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 + +#include "cpputil.h" +#include "cryptohi.h" +#include "gtest/gtest.h" +#include "limits.h" +#include "nss.h" +#include "nss_scoped_ptrs.h" +#include "pk11pub.h" + +#include "testvectors/rsa_pkcs1_2048_test-vectors.h" +#include "testvectors/rsa_pkcs1_3072_test-vectors.h" +#include "testvectors/rsa_pkcs1_4096_test-vectors.h" + +namespace nss_test { + +class RsaDecryptWycheproofTest + : public ::testing::TestWithParam { + protected: + void TestDecrypt(const RsaDecryptTestVector vec) { + SECItem pkcs8_item = {siBuffer, toUcharPtr(vec.priv_key.data()), + static_cast(vec.priv_key.size())}; + + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + EXPECT_NE(nullptr, slot); + + SECKEYPrivateKey* key = nullptr; + SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot.get(), &pkcs8_item, nullptr, nullptr, false, false, KU_ALL, &key, + nullptr); + ASSERT_EQ(SECSuccess, rv); + ASSERT_NE(nullptr, key); + ScopedSECKEYPrivateKey priv_key(key); + + // Decrypt + std::vector decrypted(PR_MAX(1, vec.ct.size())); + unsigned int decrypted_len = 0; + rv = PK11_PrivDecryptPKCS1(priv_key.get(), decrypted.data(), &decrypted_len, + decrypted.size(), vec.ct.data(), vec.ct.size()); + + // RSA_DecryptBlock returns SECFailure with an empty message. + if (vec.valid && vec.msg.size()) { + EXPECT_EQ(SECSuccess, rv); + decrypted.resize(decrypted_len); + EXPECT_EQ(vec.msg, decrypted); + } else { + EXPECT_EQ(SECFailure, rv); + } + }; +}; + +TEST_P(RsaDecryptWycheproofTest, Pkcs1Decrypt) { TestDecrypt(GetParam()); } + +INSTANTIATE_TEST_CASE_P(WycheproofRsa2048DecryptTest, RsaDecryptWycheproofTest, + ::testing::ValuesIn(kRsa2048DecryptWycheproofVectors)); + +INSTANTIATE_TEST_CASE_P(WycheproofRsa3072DecryptTest, RsaDecryptWycheproofTest, + ::testing::ValuesIn(kRsa3072DecryptWycheproofVectors)); + +INSTANTIATE_TEST_CASE_P(WycheproofRsa4096DecryptTest, RsaDecryptWycheproofTest, + ::testing::ValuesIn(kRsa4096DecryptWycheproofVectors)); + +TEST(RsaEncryptTest, MessageLengths) { + const uint8_t spki[] = { + 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, + 0x89, 0x02, 0x81, 0x81, 0x00, 0xf8, 0xb8, 0x6c, 0x83, 0xb4, 0xbc, 0xd9, + 0xa8, 0x57, 0xc0, 0xa5, 0xb4, 0x59, 0x76, 0x8c, 0x54, 0x1d, 0x79, 0xeb, + 0x22, 0x52, 0x04, 0x7e, 0xd3, 0x37, 0xeb, 0x41, 0xfd, 0x83, 0xf9, 0xf0, + 0xa6, 0x85, 0x15, 0x34, 0x75, 0x71, 0x5a, 0x84, 0xa8, 0x3c, 0xd2, 0xef, + 0x5a, 0x4e, 0xd3, 0xde, 0x97, 0x8a, 0xdd, 0xff, 0xbb, 0xcf, 0x0a, 0xaa, + 0x86, 0x92, 0xbe, 0xb8, 0x50, 0xe4, 0xcd, 0x6f, 0x80, 0x33, 0x30, 0x76, + 0x13, 0x8f, 0xca, 0x7b, 0xdc, 0xec, 0x5a, 0xca, 0x63, 0xc7, 0x03, 0x25, + 0xef, 0xa8, 0x8a, 0x83, 0x58, 0x76, 0x20, 0xfa, 0x16, 0x77, 0xd7, 0x79, + 0x92, 0x63, 0x01, 0x48, 0x1a, 0xd8, 0x7b, 0x67, 0xf1, 0x52, 0x55, 0x49, + 0x4e, 0xd6, 0x6e, 0x4a, 0x5c, 0xd7, 0x7a, 0x37, 0x36, 0x0c, 0xde, 0xdd, + 0x8f, 0x44, 0xe8, 0xc2, 0xa7, 0x2c, 0x2b, 0xb5, 0xaf, 0x64, 0x4b, 0x61, + 0x07, 0x02, 0x03, 0x01, 0x00, 0x01, + }; + + // Import public key (use pre-generated for performance). + SECItem spki_item = {siBuffer, toUcharPtr(spki), sizeof(spki)}; + ScopedCERTSubjectPublicKeyInfo cert_spki( + SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); + ASSERT_TRUE(cert_spki); + ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get())); + ASSERT_TRUE(pub_key); + + int mod_len = SECKEY_PublicKeyStrength(pub_key.get()); + ASSERT_TRUE(mod_len > 0); + + std::vector ctxt(mod_len); + unsigned int ctxt_len; + std::vector msg(mod_len, 0xff); + + // Test with valid inputs + SECStatus rv = + PK11_PubEncrypt(pub_key.get(), CKM_RSA_PKCS, nullptr, ctxt.data(), + &ctxt_len, mod_len, msg.data(), 1, nullptr); + ASSERT_EQ(SECSuccess, rv); + + // Maximum message length is mod_len - miniumum padding (8B) - flags (3B) + unsigned int max_msg_len = mod_len - 8 - 3; + rv = PK11_PubEncrypt(pub_key.get(), CKM_RSA_PKCS, nullptr, ctxt.data(), + &ctxt_len, mod_len, msg.data(), max_msg_len, nullptr); + ASSERT_EQ(SECSuccess, rv); + + // Test one past maximum length + rv = + PK11_PubEncrypt(pub_key.get(), CKM_RSA_PKCS, nullptr, ctxt.data(), + &ctxt_len, mod_len, msg.data(), max_msg_len + 1, nullptr); + ASSERT_EQ(SECFailure, rv); + + // Make sure the the length will not overflow - i.e. + // (padLen = modulusLen - (UINT_MAX + MINIMUM_PAD_LEN)) may overflow and + // result in a value that appears valid. + rv = PK11_PubEncrypt(pub_key.get(), CKM_RSA_PKCS, nullptr, ctxt.data(), + &ctxt_len, UINT_MAX, msg.data(), UINT_MAX, nullptr); + ASSERT_EQ(SECFailure, rv); +} +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_rsaoaep_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_rsaoaep_unittest.cc new file mode 100644 index 000000000..d14eb9c64 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_rsaoaep_unittest.cc @@ -0,0 +1,186 @@ +/* -*- 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 + +#include "cpputil.h" +#include "cryptohi.h" +#include "gtest/gtest.h" +#include "limits.h" +#include "nss.h" +#include "nss_scoped_ptrs.h" +#include "pk11pub.h" + +#include "testvectors/rsa_oaep_2048_sha1_mgf1sha1-vectors.h" +#include "testvectors/rsa_oaep_2048_sha256_mgf1sha1-vectors.h" +#include "testvectors/rsa_oaep_2048_sha256_mgf1sha256-vectors.h" +#include "testvectors/rsa_oaep_2048_sha384_mgf1sha1-vectors.h" +#include "testvectors/rsa_oaep_2048_sha384_mgf1sha384-vectors.h" +#include "testvectors/rsa_oaep_2048_sha512_mgf1sha1-vectors.h" +#include "testvectors/rsa_oaep_2048_sha512_mgf1sha512-vectors.h" + +namespace nss_test { + +class RsaOaepWycheproofTest + : public ::testing::TestWithParam { + protected: + void TestDecrypt(const RsaOaepTestVectorStr vec) { + SECItem pkcs8_item = {siBuffer, toUcharPtr(vec.priv_key.data()), + static_cast(vec.priv_key.size())}; + + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + EXPECT_NE(nullptr, slot); + + SECKEYPrivateKey* key = nullptr; + SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot.get(), &pkcs8_item, nullptr, nullptr, false, false, KU_ALL, &key, + nullptr); + ASSERT_EQ(SECSuccess, rv); + ASSERT_NE(nullptr, key); + ScopedSECKEYPrivateKey priv_key(key); + + // Set up the OAEP parameters. + CK_RSA_PKCS_OAEP_PARAMS oaepParams; + oaepParams.source = CKZ_DATA_SPECIFIED; + oaepParams.pSourceData = const_cast(vec.label.data()); + oaepParams.ulSourceDataLen = vec.label.size(); + oaepParams.mgf = vec.mgf_hash; + oaepParams.hashAlg = HashOidToHashMech(vec.hash_oid); + SECItem params_item = {siBuffer, + toUcharPtr(reinterpret_cast(&oaepParams)), + static_cast(sizeof(oaepParams))}; + // Decrypt. + std::vector decrypted(PR_MAX(1, vec.ct.size())); + unsigned int decrypted_len = 0; + rv = PK11_PrivDecrypt(priv_key.get(), CKM_RSA_PKCS_OAEP, ¶ms_item, + decrypted.data(), &decrypted_len, decrypted.size(), + vec.ct.data(), vec.ct.size()); + + if (vec.valid) { + EXPECT_EQ(SECSuccess, rv); + decrypted.resize(decrypted_len); + EXPECT_EQ(vec.msg, decrypted); + } else { + EXPECT_EQ(SECFailure, rv); + } + }; + + private: + inline CK_MECHANISM_TYPE HashOidToHashMech(SECOidTag hash_oid) { + switch (hash_oid) { + case SEC_OID_SHA1: + return CKM_SHA_1; + case SEC_OID_SHA224: + return CKM_SHA224; + case SEC_OID_SHA256: + return CKM_SHA256; + case SEC_OID_SHA384: + return CKM_SHA384; + case SEC_OID_SHA512: + return CKM_SHA512; + default: + ADD_FAILURE(); + } + return CKM_INVALID_MECHANISM; + } +}; + +TEST_P(RsaOaepWycheproofTest, OaepDecrypt) { TestDecrypt(GetParam()); } + +INSTANTIATE_TEST_CASE_P(WycheproofRsa2048Sha1OaepTest, RsaOaepWycheproofTest, + ::testing::ValuesIn(kRsaOaep2048Sha1WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + WycheproofOaep2048Sha256Sha1Test, RsaOaepWycheproofTest, + ::testing::ValuesIn(kRsaOaep2048Sha256Mgf1Sha1WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + WycheproofOaep2048Sha256Sha256Test, RsaOaepWycheproofTest, + ::testing::ValuesIn(kRsaOaep2048Sha256Mgf1Sha256WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + WycheproofOaep2048Sha384Sha1Test, RsaOaepWycheproofTest, + ::testing::ValuesIn(kRsaOaep2048Sha384Mgf1Sha1WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + WycheproofOaep2048Sha384Sha384Test, RsaOaepWycheproofTest, + ::testing::ValuesIn(kRsaOaep2048Sha384Mgf1Sha384WycheproofVectors)); + +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/security/nss/gtests/pk11_gtest/pk11_rsapkcs1_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_rsapkcs1_unittest.cc index 8a9eb56f4..941de9797 100644 --- a/security/nss/gtests/pk11_gtest/pk11_rsapkcs1_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_rsapkcs1_unittest.cc @@ -1,101 +1,206 @@ /* -*- 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 +#include #include "cryptohi.h" +#include "cpputil.h" +#include "databuffer.h" +#include "gtest/gtest.h" #include "nss.h" +#include "nss_scoped_ptrs.h" #include "pk11pub.h" +#include "secerr.h" +#include "sechash.h" -#include "gtest/gtest.h" -#include "nss_scoped_ptrs.h" -#include "cpputil.h" +#include "testvectors/rsa_signature_2048_sha224-vectors.h" +#include "testvectors/rsa_signature_2048_sha256-vectors.h" +#include "testvectors/rsa_signature_2048_sha512-vectors.h" +#include "testvectors/rsa_signature_3072_sha256-vectors.h" +#include "testvectors/rsa_signature_3072_sha384-vectors.h" +#include "testvectors/rsa_signature_3072_sha512-vectors.h" +#include "testvectors/rsa_signature_4096_sha384-vectors.h" +#include "testvectors/rsa_signature_4096_sha512-vectors.h" +#include "testvectors/rsa_signature-vectors.h" namespace nss_test { -// Test that the RSASSA-PKCS1-v1_5 implementation enforces the missing NULL -// parameter. -TEST(RsaPkcs1Test, RequireNullParameter) { - // kSpki is an RSA public key in an X.509 SubjectPublicKeyInfo. - const uint8_t kSpki[] = { - 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, - 0x89, 0x02, 0x81, 0x81, 0x00, 0xf8, 0xb8, 0x6c, 0x83, 0xb4, 0xbc, 0xd9, - 0xa8, 0x57, 0xc0, 0xa5, 0xb4, 0x59, 0x76, 0x8c, 0x54, 0x1d, 0x79, 0xeb, - 0x22, 0x52, 0x04, 0x7e, 0xd3, 0x37, 0xeb, 0x41, 0xfd, 0x83, 0xf9, 0xf0, - 0xa6, 0x85, 0x15, 0x34, 0x75, 0x71, 0x5a, 0x84, 0xa8, 0x3c, 0xd2, 0xef, - 0x5a, 0x4e, 0xd3, 0xde, 0x97, 0x8a, 0xdd, 0xff, 0xbb, 0xcf, 0x0a, 0xaa, - 0x86, 0x92, 0xbe, 0xb8, 0x50, 0xe4, 0xcd, 0x6f, 0x80, 0x33, 0x30, 0x76, - 0x13, 0x8f, 0xca, 0x7b, 0xdc, 0xec, 0x5a, 0xca, 0x63, 0xc7, 0x03, 0x25, - 0xef, 0xa8, 0x8a, 0x83, 0x58, 0x76, 0x20, 0xfa, 0x16, 0x77, 0xd7, 0x79, - 0x92, 0x63, 0x01, 0x48, 0x1a, 0xd8, 0x7b, 0x67, 0xf1, 0x52, 0x55, 0x49, - 0x4e, 0xd6, 0x6e, 0x4a, 0x5c, 0xd7, 0x7a, 0x37, 0x36, 0x0c, 0xde, 0xdd, - 0x8f, 0x44, 0xe8, 0xc2, 0xa7, 0x2c, 0x2b, 0xb5, 0xaf, 0x64, 0x4b, 0x61, - 0x07, 0x02, 0x03, 0x01, 0x00, 0x01, - }; - // kHash is the SHA-256 hash of {1,2,3,4}. - const uint8_t kHash[] = { - 0x9f, 0x64, 0xa7, 0x47, 0xe1, 0xb9, 0x7f, 0x13, 0x1f, 0xab, 0xb6, - 0xb4, 0x47, 0x29, 0x6c, 0x9b, 0x6f, 0x02, 0x01, 0xe7, 0x9f, 0xb3, - 0xc5, 0x35, 0x6e, 0x6c, 0x77, 0xe8, 0x9b, 0x6a, 0x80, 0x6a, - }; - // kSignature is the signature of kHash with RSASSA-PKCS1-v1_5. - const uint8_t kSignature[] = { - 0xa5, 0xf0, 0x8a, 0x47, 0x5d, 0x3c, 0xb3, 0xcc, 0xa9, 0x79, 0xaf, 0x4d, - 0x8c, 0xae, 0x4c, 0x14, 0xef, 0xc2, 0x0b, 0x34, 0x36, 0xde, 0xf4, 0x3e, - 0x3d, 0xbb, 0x4a, 0x60, 0x5c, 0xc8, 0x91, 0x28, 0xda, 0xfb, 0x7e, 0x04, - 0x96, 0x7e, 0x63, 0x13, 0x90, 0xce, 0xb9, 0xb4, 0x62, 0x7a, 0xfd, 0x09, - 0x3d, 0xc7, 0x67, 0x78, 0x54, 0x04, 0xeb, 0x52, 0x62, 0x6e, 0x24, 0x67, - 0xb4, 0x40, 0xfc, 0x57, 0x62, 0xc6, 0xf1, 0x67, 0xc1, 0x97, 0x8f, 0x6a, - 0xa8, 0xae, 0x44, 0x46, 0x5e, 0xab, 0x67, 0x17, 0x53, 0x19, 0x3a, 0xda, - 0x5a, 0xc8, 0x16, 0x3e, 0x86, 0xd5, 0xc5, 0x71, 0x2f, 0xfc, 0x23, 0x48, - 0xd9, 0x0b, 0x13, 0xdd, 0x7b, 0x5a, 0x25, 0x79, 0xef, 0xa5, 0x7b, 0x04, - 0xed, 0x44, 0xf6, 0x18, 0x55, 0xe4, 0x0a, 0xe9, 0x57, 0x79, 0x5d, 0xd7, - 0x55, 0xa7, 0xab, 0x45, 0x02, 0x97, 0x60, 0x42, - }; - // kSignature is an invalid signature of kHash with RSASSA-PKCS1-v1_5 with the - // NULL parameter omitted. - const uint8_t kSignatureInvalid[] = { - 0x71, 0x6c, 0x24, 0x4e, 0xc9, 0x9b, 0x19, 0xc7, 0x49, 0x29, 0xb8, 0xd4, - 0xfb, 0x26, 0x23, 0xc0, 0x96, 0x18, 0xcd, 0x1e, 0x60, 0xe8, 0x88, 0x94, - 0x8c, 0x59, 0xfb, 0x58, 0x5c, 0x61, 0x58, 0x7a, 0xae, 0xcc, 0xeb, 0xee, - 0x1e, 0x85, 0x7d, 0x83, 0xa9, 0xdc, 0x6f, 0x4c, 0x34, 0x5c, 0xcb, 0xd9, - 0xde, 0x58, 0x76, 0xdf, 0x1f, 0x5e, 0xd4, 0x57, 0x5b, 0xeb, 0xaf, 0x4f, - 0x7a, 0xa7, 0x6b, 0x21, 0xf1, 0x0a, 0x96, 0x78, 0xc7, 0xa8, 0x02, 0x7a, - 0xc2, 0x06, 0xd3, 0x18, 0x79, 0x72, 0x6b, 0xfe, 0x2d, 0xec, 0xd8, 0x8e, - 0x98, 0x86, 0x89, 0xf4, 0x67, 0x14, 0x2b, 0xac, 0x6d, 0xd7, 0x04, 0xd8, - 0xab, 0x05, 0xe6, 0x51, 0xf6, 0xee, 0x58, 0x63, 0xef, 0x6a, 0x3e, 0x89, - 0x99, 0x2a, 0x1c, 0x10, 0xc2, 0xd0, 0x41, 0x9e, 0x1e, 0x9a, 0x9a, 0x57, - 0x32, 0x0f, 0x49, 0xb4, 0x57, 0x37, 0xa4, 0x26, +class Pkcs11RsaPkcs1WycheproofTest + : public ::testing::TestWithParam { + protected: + void Derive(const RsaSignatureTestVector vec) { + SECItem spki_item = {siBuffer, toUcharPtr(vec.public_key.data()), + static_cast(vec.public_key.size())}; + + ScopedCERTSubjectPublicKeyInfo cert_spki( + SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); + ASSERT_TRUE(cert_spki); + + ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get())); + ASSERT_TRUE(pub_key); + + DataBuffer hash; + hash.Allocate(static_cast(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); + + // Verify. + SECItem hash_item = {siBuffer, toUcharPtr(hash.data()), + static_cast(hash.len())}; + SECItem sig_item = {siBuffer, toUcharPtr(vec.sig.data()), + static_cast(vec.sig.size())}; + + rv = VFY_VerifyDigestDirect(&hash_item, pub_key.get(), &sig_item, + SEC_OID_PKCS1_RSA_ENCRYPTION, vec.hash_oid, + nullptr); + EXPECT_EQ(rv, vec.valid ? SECSuccess : SECFailure); }; +}; + +/* Test that PKCS #1 v1.5 verification requires a minimum of 8B + * of padding, per-RFC3447. The padding formula is + * `pad_len = em_len - t_len - 3`, where em_len is the octet length + * of the RSA modulus and t_len is the length of the `DigestInfo || + * Hash(message)` sequence. For SHA512, t_len is 83. We'll tweak the + * modulus size to test with a pad_len of 8 (valid) and 6 (invalid): + * em_len = `8 + 83 + 3` = `94*8` = 752b + * em_len = `6 + 83 + 3` = `92*8` = 736b + * Use 6 as the invalid value since modLen % 16 must be zero. + */ +TEST(RsaPkcs1Test, Pkcs1MinimumPadding) { + const size_t kRsaShortKeyBits = 736; + const size_t kRsaKeyBits = 752; + static const std::vector kMsg{'T', 'E', 'S', 'T'}; + static const std::vector kSha512DigestInfo{ + 0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}; + static const std::vector kMsgSha512{ + 0x7B, 0xFA, 0x95, 0xA6, 0x88, 0x92, 0x4C, 0x47, 0xC7, 0xD2, 0x23, + 0x81, 0xF2, 0x0C, 0xC9, 0x26, 0xF5, 0x24, 0xBE, 0xAC, 0xB1, 0x3F, + 0x84, 0xE2, 0x03, 0xD4, 0xBD, 0x8C, 0xB6, 0xBA, 0x2F, 0xCE, 0x81, + 0xC5, 0x7A, 0x5F, 0x05, 0x9B, 0xF3, 0xD5, 0x09, 0x92, 0x64, 0x87, + 0xBD, 0xE9, 0x25, 0xB3, 0xBC, 0xEE, 0x06, 0x35, 0xE4, 0xF7, 0xBA, + 0xEB, 0xA0, 0x54, 0xE5, 0xDB, 0xA6, 0x96, 0xB2, 0xBF}; + + ScopedSECKEYPrivateKey short_priv, good_priv; + ScopedSECKEYPublicKey short_pub, good_pub; + PK11RSAGenParams rsa_params; + rsa_params.keySizeInBits = kRsaShortKeyBits; + rsa_params.pe = 65537; + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_TRUE(slot); + SECKEYPublicKey* p_pub_tmp = nullptr; + short_priv.reset(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, + &rsa_params, &p_pub_tmp, false, false, + nullptr)); + short_pub.reset(p_pub_tmp); + + rsa_params.keySizeInBits = kRsaKeyBits; + good_priv.reset(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, + &rsa_params, &p_pub_tmp, false, false, + nullptr)); + good_pub.reset(p_pub_tmp); + + size_t em_len = kRsaShortKeyBits / 8; + size_t t_len = kSha512DigestInfo.size() + kMsgSha512.size(); + size_t pad_len = em_len - t_len - 3; + ASSERT_EQ(6U, pad_len); + + std::vector invalid_pkcs; + invalid_pkcs.push_back(0x00); + invalid_pkcs.push_back(0x01); + invalid_pkcs.insert(invalid_pkcs.end(), pad_len, 0xff); + invalid_pkcs.insert(invalid_pkcs.end(), 1, 0x00); + invalid_pkcs.insert(invalid_pkcs.end(), kSha512DigestInfo.begin(), + kSha512DigestInfo.end()); + invalid_pkcs.insert(invalid_pkcs.end(), kMsgSha512.begin(), kMsgSha512.end()); + ASSERT_EQ(em_len, invalid_pkcs.size()); + + // Sign it indirectly. Signing functions check for a proper pad_len. + std::vector sig(em_len); + uint32_t sig_len; + SECStatus rv = + PK11_PubDecryptRaw(short_priv.get(), sig.data(), &sig_len, sig.size(), + invalid_pkcs.data(), invalid_pkcs.size()); + EXPECT_EQ(SECSuccess, rv); + // Verify it. + DataBuffer hash; + hash.Allocate(static_cast(HASH_ResultLenByOidTag(SEC_OID_SHA512))); + rv = PK11_HashBuf(SEC_OID_SHA512, toUcharPtr(hash.data()), + toUcharPtr(kMsg.data()), kMsg.size()); + ASSERT_EQ(rv, SECSuccess); + SECItem hash_item = {siBuffer, toUcharPtr(hash.data()), + static_cast(hash.len())}; + SECItem sig_item = {siBuffer, toUcharPtr(sig.data()), sig_len}; + rv = VFY_VerifyDigestDirect(&hash_item, short_pub.get(), &sig_item, + SEC_OID_PKCS1_RSA_ENCRYPTION, SEC_OID_SHA512, + nullptr); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(SEC_ERROR_BAD_SIGNATURE, PORT_GetError()); + + // Repeat the test with the sufficiently-long key. + em_len = kRsaKeyBits / 8; + t_len = kSha512DigestInfo.size() + kMsgSha512.size(); + pad_len = em_len - t_len - 3; + ASSERT_EQ(8U, pad_len); + + std::vector valid_pkcs; + valid_pkcs.push_back(0x00); + valid_pkcs.push_back(0x01); + valid_pkcs.insert(valid_pkcs.end(), pad_len, 0xff); + valid_pkcs.insert(valid_pkcs.end(), 1, 0x00); + valid_pkcs.insert(valid_pkcs.end(), kSha512DigestInfo.begin(), + kSha512DigestInfo.end()); + valid_pkcs.insert(valid_pkcs.end(), kMsgSha512.begin(), kMsgSha512.end()); + ASSERT_EQ(em_len, valid_pkcs.size()); + + // Sign it the same way as above (even though we could use sign APIs now). + sig.resize(em_len); + rv = PK11_PubDecryptRaw(good_priv.get(), sig.data(), &sig_len, sig.size(), + valid_pkcs.data(), valid_pkcs.size()); + EXPECT_EQ(SECSuccess, rv); + + // Verify it. + sig_item = {siBuffer, toUcharPtr(sig.data()), sig_len}; + rv = VFY_VerifyDigestDirect(&hash_item, good_pub.get(), &sig_item, + SEC_OID_PKCS1_RSA_ENCRYPTION, SEC_OID_SHA512, + nullptr); + EXPECT_EQ(SECSuccess, rv); +} + +TEST(RsaPkcs1Test, RequireNullParameter) { // The test vectors may be verified with: // // openssl rsautl -keyform der -pubin -inkey spki.bin -in sig.bin | der2ascii // openssl rsautl -keyform der -pubin -inkey spki.bin -in sig2.bin | der2ascii // Import public key. - SECItem spkiItem = {siBuffer, toUcharPtr(kSpki), sizeof(kSpki)}; - ScopedCERTSubjectPublicKeyInfo certSpki( - SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem)); - ASSERT_TRUE(certSpki); - ScopedSECKEYPublicKey pubKey(SECKEY_ExtractPublicKey(certSpki.get())); - ASSERT_TRUE(pubKey); + SECItem spki_item = {siBuffer, toUcharPtr(kSpki), sizeof(kSpki)}; + ScopedCERTSubjectPublicKeyInfo cert_spki( + SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); + ASSERT_TRUE(cert_spki); + ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get())); + ASSERT_TRUE(pub_key); SECItem hash = {siBuffer, toUcharPtr(kHash), sizeof(kHash)}; // kSignature is a valid signature. - SECItem sigItem = {siBuffer, toUcharPtr(kSignature), sizeof(kSignature)}; - SECStatus rv = VFY_VerifyDigestDirect(&hash, pubKey.get(), &sigItem, + SECItem sig_item = {siBuffer, toUcharPtr(kSignature), sizeof(kSignature)}; + SECStatus rv = VFY_VerifyDigestDirect(&hash, pub_key.get(), &sig_item, SEC_OID_PKCS1_RSA_ENCRYPTION, SEC_OID_SHA256, nullptr); EXPECT_EQ(SECSuccess, rv); // kSignatureInvalid is not. - sigItem = {siBuffer, toUcharPtr(kSignatureInvalid), - sizeof(kSignatureInvalid)}; - rv = VFY_VerifyDigestDirect(&hash, pubKey.get(), &sigItem, + sig_item = {siBuffer, toUcharPtr(kSignatureInvalid), + sizeof(kSignatureInvalid)}; + rv = VFY_VerifyDigestDirect(&hash, pub_key.get(), &sig_item, SEC_OID_PKCS1_RSA_ENCRYPTION, SEC_OID_SHA256, nullptr); #ifdef NSS_PKCS1_AllowMissingParameters @@ -105,4 +210,42 @@ TEST(RsaPkcs1Test, RequireNullParameter) { #endif } +TEST_P(Pkcs11RsaPkcs1WycheproofTest, Verify) { Derive(GetParam()); } + +INSTANTIATE_TEST_CASE_P( + Wycheproof2048RsaSignatureSha224Test, Pkcs11RsaPkcs1WycheproofTest, + ::testing::ValuesIn(kRsaSignature2048Sha224WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof2048RsaSignatureSha256Test, Pkcs11RsaPkcs1WycheproofTest, + ::testing::ValuesIn(kRsaSignature2048Sha256WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof2048RsaSignatureSha512Test, Pkcs11RsaPkcs1WycheproofTest, + ::testing::ValuesIn(kRsaSignature2048Sha512WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof3072RsaSignatureSha256Test, Pkcs11RsaPkcs1WycheproofTest, + ::testing::ValuesIn(kRsaSignature3072Sha256WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof3072RsaSignatureSha384Test, Pkcs11RsaPkcs1WycheproofTest, + ::testing::ValuesIn(kRsaSignature3072Sha384WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof3072RsaSignatureSha512Test, Pkcs11RsaPkcs1WycheproofTest, + ::testing::ValuesIn(kRsaSignature3072Sha512WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof4096RsaSignatureSha384Test, Pkcs11RsaPkcs1WycheproofTest, + ::testing::ValuesIn(kRsaSignature4096Sha384WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof4096RsaSignatureSha512Test, Pkcs11RsaPkcs1WycheproofTest, + ::testing::ValuesIn(kRsaSignature4096Sha512WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P(WycheproofRsaSignatureTest, + Pkcs11RsaPkcs1WycheproofTest, + ::testing::ValuesIn(kRsaSignatureWycheproofVectors)); + } // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc index 1ce642937..48a7cc268 100644 --- a/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -8,49 +9,117 @@ #include "pk11pub.h" #include "sechash.h" +#include "cpputil.h" +#include "databuffer.h" + #include "gtest/gtest.h" #include "nss_scoped_ptrs.h" #include "pk11_signature_test.h" #include "pk11_rsapss_vectors.h" +#include "testvectors/rsa_pss_2048_sha256_mgf1_32-vectors.h" +#include "testvectors/rsa_pss_2048_sha1_mgf1_20-vectors.h" +#include "testvectors/rsa_pss_2048_sha256_mgf1_0-vectors.h" +#include "testvectors/rsa_pss_3072_sha256_mgf1_32-vectors.h" +#include "testvectors/rsa_pss_4096_sha256_mgf1_32-vectors.h" +#include "testvectors/rsa_pss_4096_sha512_mgf1_32-vectors.h" +#include "testvectors/rsa_pss_misc-vectors.h" + namespace nss_test { +class Pkcs11RsaPssTestWycheproof + : public ::testing::TestWithParam { + protected: + void TestPss(const RsaPssTestVector& vec) { + SECItem spki_item = {siBuffer, toUcharPtr(vec.public_key.data()), + static_cast(vec.public_key.size())}; + + ScopedCERTSubjectPublicKeyInfo cert_spki( + SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); + ASSERT_TRUE(cert_spki); + + ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get())); + ASSERT_TRUE(pub_key); + + DataBuffer hash; + hash.Allocate(static_cast(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); + + // Verify. + SECItem hash_item = {siBuffer, toUcharPtr(hash.data()), + static_cast(hash.len())}; + SECItem sig_item = {siBuffer, toUcharPtr(vec.sig.data()), + static_cast(vec.sig.size())}; + CK_MECHANISM_TYPE hash_mech = 0; + switch (vec.hash_oid) { + case SEC_OID_SHA1: + hash_mech = CKM_SHA_1; + break; + case SEC_OID_SHA224: + hash_mech = CKM_SHA224; + break; + case SEC_OID_SHA256: + hash_mech = CKM_SHA256; + break; + case SEC_OID_SHA384: + hash_mech = CKM_SHA384; + break; + case SEC_OID_SHA512: + hash_mech = CKM_SHA512; + break; + default: + ASSERT_TRUE(hash_mech); + return; + } + + CK_RSA_PKCS_PSS_PARAMS pss_params = {hash_mech, vec.mgf_hash, vec.sLen}; + SECItem params = {siBuffer, reinterpret_cast(&pss_params), + sizeof(pss_params)}; + + rv = PK11_VerifyWithMechanism(pub_key.get(), CKM_RSA_PKCS_PSS, ¶ms, + &sig_item, &hash_item, nullptr); + EXPECT_EQ(vec.valid ? SECSuccess : SECFailure, rv); + }; +}; + class Pkcs11RsaPssTest : public Pk11SignatureTest { public: Pkcs11RsaPssTest() : Pk11SignatureTest(CKM_RSA_PKCS_PSS, SEC_OID_SHA1) { - rsaPssParams_.hashAlg = CKM_SHA_1; - rsaPssParams_.mgf = CKG_MGF1_SHA1; - rsaPssParams_.sLen = HASH_ResultLenByOidTag(SEC_OID_SHA1); + pss_params_.hashAlg = CKM_SHA_1; + pss_params_.mgf = CKG_MGF1_SHA1; + pss_params_.sLen = HASH_ResultLenByOidTag(SEC_OID_SHA1); params_.type = siBuffer; - params_.data = reinterpret_cast(&rsaPssParams_); - params_.len = sizeof(rsaPssParams_); + params_.data = reinterpret_cast(&pss_params_); + params_.len = sizeof(pss_params_); } protected: const SECItem* parameters() const { return ¶ms_; } private: - CK_RSA_PKCS_PSS_PARAMS rsaPssParams_; + CK_RSA_PKCS_PSS_PARAMS pss_params_; SECItem params_; }; TEST_F(Pkcs11RsaPssTest, GenerateAndSignAndVerify) { // Sign data with a 1024-bit RSA key, using PSS/SHA-256. SECOidTag hashOid = SEC_OID_SHA256; - CK_MECHANISM_TYPE hashMech = CKM_SHA256; + CK_MECHANISM_TYPE hash_mech = CKM_SHA256; CK_RSA_PKCS_MGF_TYPE mgf = CKG_MGF1_SHA256; PK11RSAGenParams rsaGenParams = {1024, 0x10001}; // Generate RSA key pair. ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); - SECKEYPublicKey* pubKeyRaw = nullptr; + SECKEYPublicKey* pub_keyRaw = nullptr; ScopedSECKEYPrivateKey privKey( PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaGenParams, - &pubKeyRaw, false, false, nullptr)); - ASSERT_TRUE(!!privKey && pubKeyRaw); - ScopedSECKEYPublicKey pubKey(pubKeyRaw); + &pub_keyRaw, false, false, nullptr)); + ASSERT_TRUE(!!privKey && pub_keyRaw); + ScopedSECKEYPublicKey pub_key(pub_keyRaw); // Generate random data to sign. uint8_t dataBuf[50]; @@ -65,30 +134,30 @@ TEST_F(Pkcs11RsaPssTest, GenerateAndSignAndVerify) { static_cast(sigBuf.size())}; // Set up PSS parameters. - CK_RSA_PKCS_PSS_PARAMS rsaPssParams = {hashMech, mgf, hLen}; - SECItem params = {siBuffer, reinterpret_cast(&rsaPssParams), - sizeof(rsaPssParams)}; + CK_RSA_PKCS_PSS_PARAMS pss_params = {hash_mech, mgf, hLen}; + SECItem params = {siBuffer, reinterpret_cast(&pss_params), + sizeof(pss_params)}; // Sign. rv = PK11_SignWithMechanism(privKey.get(), mechanism(), ¶ms, &sig, &data); EXPECT_EQ(rv, SECSuccess); // Verify. - rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), ¶ms, &sig, &data, - nullptr); + rv = PK11_VerifyWithMechanism(pub_key.get(), mechanism(), ¶ms, &sig, + &data, nullptr); EXPECT_EQ(rv, SECSuccess); // Verification with modified data must fail. data.data[0] ^= 0xff; - rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), ¶ms, &sig, &data, - nullptr); + rv = PK11_VerifyWithMechanism(pub_key.get(), mechanism(), ¶ms, &sig, + &data, nullptr); EXPECT_EQ(rv, SECFailure); // Verification with original data but the wrong signature must fail. data.data[0] ^= 0xff; // Revert previous changes. sig.data[0] ^= 0xff; - rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), ¶ms, &sig, &data, - nullptr); + rv = PK11_VerifyWithMechanism(pub_key.get(), mechanism(), ¶ms, &sig, + &data, nullptr); EXPECT_EQ(rv, SECFailure); } @@ -99,12 +168,12 @@ TEST_F(Pkcs11RsaPssTest, NoLeakWithInvalidExponent) { // Generate RSA key pair. ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); - SECKEYPublicKey* pubKey = nullptr; + SECKEYPublicKey* pub_key = nullptr; SECKEYPrivateKey* privKey = PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaGenParams, - &pubKey, false, false, nullptr); + &pub_key, false, false, nullptr); EXPECT_FALSE(privKey); - EXPECT_FALSE(pubKey); + EXPECT_FALSE(pub_key); } class Pkcs11RsaPssVectorTest : public Pkcs11RsaPssTest, @@ -158,4 +227,33 @@ static const Pkcs11SignatureTestParams kRsaPssVectors[] = { INSTANTIATE_TEST_CASE_P(RsaPssSignVerify, Pkcs11RsaPssVectorTest, ::testing::ValuesIn(kRsaPssVectors)); +TEST_P(Pkcs11RsaPssTestWycheproof, Verify) { TestPss(GetParam()); } + +INSTANTIATE_TEST_CASE_P( + Wycheproof2048RsaPssSha120Test, Pkcs11RsaPssTestWycheproof, + ::testing::ValuesIn(kRsaPss2048Sha120WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof2048RsaPssSha25632Test, Pkcs11RsaPssTestWycheproof, + ::testing::ValuesIn(kRsaPss2048Sha25632WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof2048RsaPssSha2560Test, Pkcs11RsaPssTestWycheproof, + ::testing::ValuesIn(kRsaPss2048Sha2560WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof3072RsaPssSha25632Test, Pkcs11RsaPssTestWycheproof, + ::testing::ValuesIn(kRsaPss3072Sha25632WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof4096RsaPssSha25632Test, Pkcs11RsaPssTestWycheproof, + ::testing::ValuesIn(kRsaPss4096Sha25632WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P( + Wycheproof4096RsaPssSha51232Test, Pkcs11RsaPssTestWycheproof, + ::testing::ValuesIn(kRsaPss4096Sha51232WycheproofVectors)); + +INSTANTIATE_TEST_CASE_P(WycheproofRsaPssMiscTest, Pkcs11RsaPssTestWycheproof, + ::testing::ValuesIn(kRsaPssMiscWycheproofVectors)); + } // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_seed_cbc_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_seed_cbc_unittest.cc index 40efe266e..7f389fef1 100644 --- a/security/nss/gtests/pk11_gtest/pk11_seed_cbc_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_seed_cbc_unittest.cc @@ -1,4 +1,5 @@ /* -*- 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/. */ @@ -13,58 +14,69 @@ #include "util.h" namespace nss_test { -class Pkcs11SeedCbcTest : public ::testing::Test { +class Pkcs11SeedTest : public ::testing::Test { protected: - enum class Action { Encrypt, Decrypt }; - - SECStatus EncryptDecryptSeed(Action action, unsigned int input_size, - unsigned int output_size) { + void EncryptDecryptSeed(SECStatus expected, unsigned int input_size, + unsigned int output_size, + CK_MECHANISM_TYPE mech = CKM_SEED_CBC) { // Generate a random key. ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); ScopedPK11SymKey sym_key( - PK11_KeyGen(slot.get(), kMech, nullptr, 16, nullptr)); + PK11_KeyGen(slot.get(), mech, nullptr, 16, nullptr)); EXPECT_TRUE(!!sym_key); - std::vector data(input_size); + std::vector plaintext(input_size, 0xFF); std::vector init_vector(16); - std::vector output(output_size); - SECItem params = {siBuffer, init_vector.data(), - (unsigned int)init_vector.size()}; + std::vector ciphertext(output_size, 0); + SECItem iv_param = {siBuffer, init_vector.data(), + (unsigned int)init_vector.size()}; + std::vector decrypted(output_size, 0); - // Try to encrypt/decrypt. + // Try to encrypt, decrypt if positive test. unsigned int output_len = 0; - if (action == Action::Encrypt) { - return PK11_Encrypt(sym_key.get(), kMech, ¶ms, output.data(), - &output_len, output_size, data.data(), data.size()); - } else { - return PK11_Decrypt(sym_key.get(), kMech, ¶ms, output.data(), - &output_len, output_size, data.data(), data.size()); + EXPECT_EQ(expected, + PK11_Encrypt(sym_key.get(), mech, &iv_param, ciphertext.data(), + &output_len, output_size, plaintext.data(), + plaintext.size())); + + if (expected == SECSuccess) { + EXPECT_EQ(expected, + PK11_Decrypt(sym_key.get(), mech, &iv_param, decrypted.data(), + &output_len, output_size, ciphertext.data(), + output_len)); + decrypted.resize(output_len); + EXPECT_EQ(plaintext, decrypted); } } - const CK_MECHANISM_TYPE kMech = CKM_SEED_CBC; }; +#ifndef NSS_DISABLE_DEPRECATED_SEED // The intention here is to test the arguments of these functions // The resulted content is already tested in EncryptDeriveTests. // SEED_CBC needs an IV of 16 bytes. // The input data size must be multiple of 16. // If not, some padding should be added. // The output size must be at least the size of input data. -TEST_F(Pkcs11SeedCbcTest, SeedCBC_ValidArgs) { - EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Encrypt, 16, 16)); - EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Decrypt, 16, 16)); +TEST_F(Pkcs11SeedTest, CBC_ValidArgs) { + EncryptDecryptSeed(SECSuccess, 16, 16); // No problem if maxLen is bigger than input data. - EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Encrypt, 16, 32)); - EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Decrypt, 16, 32)); + EncryptDecryptSeed(SECSuccess, 16, 32); } -TEST_F(Pkcs11SeedCbcTest, SeedCBC_InvalidArgs) { +TEST_F(Pkcs11SeedTest, CBC_InvalidArgs) { // maxLen lower than input data. - EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Encrypt, 16, 10)); - EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Decrypt, 16, 10)); + EncryptDecryptSeed(SECFailure, 16, 10); // input data not multiple of SEED_BLOCK_SIZE (16) - EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Encrypt, 17, 32)); - EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Decrypt, 17, 32)); + EncryptDecryptSeed(SECFailure, 17, 32); +} + +TEST_F(Pkcs11SeedTest, ECB_Singleblock) { + EncryptDecryptSeed(SECSuccess, 16, 16, CKM_SEED_ECB); +} + +TEST_F(Pkcs11SeedTest, ECB_Multiblock) { + EncryptDecryptSeed(SECSuccess, 64, 64, CKM_SEED_ECB); } +#endif -} // namespace nss_test \ No newline at end of file +} // namespace nss_test -- cgit v1.2.3