diff options
Diffstat (limited to 'security/nss/gtests/pk11_gtest')
23 files changed, 3072 insertions, 435 deletions
diff --git a/security/nss/gtests/pk11_gtest/manifest.mn b/security/nss/gtests/pk11_gtest/manifest.mn index ea7b43a2b..1c0ae6921 100644 --- a/security/nss/gtests/pk11_gtest/manifest.mn +++ b/security/nss/gtests/pk11_gtest/manifest.mn @@ -7,20 +7,32 @@ DEPTH = ../.. MODULE = nss CPPSRCS = \ + pk11_aes_gcm_unittest.cc \ pk11_aeskeywrap_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_ecdsa_unittest.cc \ pk11_encrypt_derive_unittest.cc \ pk11_export_unittest.cc \ + pk11_find_certs_unittest.cc \ + pk11_import_unittest.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_rsapkcs1_unittest.cc \ pk11_rsapss_unittest.cc \ - pk11_der_private_key_import_unittest.cc \ + pk11_seed_cbc_unittest.cc \ $(NULL) +DEFINES += -DDLL_PREFIX=\"$(DLL_PREFIX)\" -DDLL_SUFFIX=\"$(DLL_SUFFIX)\" + INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \ -I$(CORE_DEPTH)/gtests/common \ -I$(CORE_DEPTH)/cpputil @@ -33,4 +45,3 @@ EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)gtest.$(LIB_SUFFIX) \ $(DIST)/lib/$(LIB_PREFIX)cpputil.$(LIB_SUFFIX) \ $(DIST)/lib/$(LIB_PREFIX)gtestutil.$(LIB_SUFFIX) \ $(NULL) - diff --git a/security/nss/gtests/pk11_gtest/pk11_aes_cmac_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_aes_cmac_unittest.cc new file mode 100644 index 000000000..c520fffd1 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_aes_cmac_unittest.cc @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <memory> +#include "nss.h" +#include "pk11pub.h" +#include "secerr.h" +#include "sechash.h" + +#include "blapi.h" + +#include "gtest/gtest.h" +#include "nss_scoped_ptrs.h" +#include "util.h" + +namespace nss_test { + +class Pkcs11AesCmacTest : 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 RunTest(uint8_t *key, unsigned int key_len, uint8_t *data, + unsigned int data_len, uint8_t *expected, + unsigned int expected_len, CK_ULONG mechanism) { + // Create SECItems for everything... + std::vector<uint8_t> output(expected_len); + SECItem key_item = {siBuffer, key, key_len}; + SECItem output_item = {siBuffer, output.data(), expected_len}; + SECItem data_item = {siBuffer, data, data_len}; + SECItem expected_item = {siBuffer, expected, expected_len}; + + // Do the PKCS #11 stuff... + ScopedPK11SymKey p11_key = ImportKey(mechanism, &key_item); + ASSERT_NE(nullptr, p11_key.get()); + + SECStatus ret = PK11_SignWithSymKey(p11_key.get(), CKM_AES_CMAC, NULL, + &output_item, &data_item); + + // Verify the result... + ASSERT_EQ(SECSuccess, ret); + ASSERT_EQ(0, SECITEM_CompareItem(&output_item, &expected_item)); + } +}; + +// 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 + +TEST_F(Pkcs11AesCmacTest, Aes128NistExample1) { + uint8_t key[AES_128_KEY_LENGTH] = {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, + 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, + 0x09, 0xCF, 0x4F, 0x3C}; + uint8_t known[AES_BLOCK_SIZE] = {0xBB, 0x1D, 0x69, 0x29, 0xE9, 0x59, + 0x37, 0x28, 0x7F, 0xA3, 0x7D, 0x12, + 0x9B, 0x75, 0x67, 0x46}; + + RunTest(key, AES_128_KEY_LENGTH, NULL, 0, known, AES_BLOCK_SIZE, + CKM_AES_CMAC); +} + +TEST_F(Pkcs11AesCmacTest, General) { + uint8_t key[AES_128_KEY_LENGTH] = {0x2B, 0x7E, 0x15, 0x16, 0x28, 0xAE, + 0xD2, 0xA6, 0xAB, 0xF7, 0x15, 0x88, + 0x09, 0xCF, 0x4F, 0x3C}; + uint8_t known[4] = {0xBB, 0x1D, 0x69, 0x29}; + + RunTest(key, AES_128_KEY_LENGTH, NULL, 0, known, 4, CKM_AES_CMAC_GENERAL); +} + +TEST_F(Pkcs11AesCmacTest, InvalidKeySize) { + uint8_t key[4] = {0x00, 0x00, 0x00, 0x00}; + SECItem key_item = {siBuffer, key, 4}; + + ScopedPK11SymKey result = ImportKey(CKM_AES_CMAC, &key_item); + ASSERT_EQ(nullptr, result.get()); +} +} 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 4072cf2b7..2c58063d4 100644 --- a/security/nss/gtests/pk11_gtest/pk11_aes_gcm_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_aes_gcm_unittest.cc @@ -12,7 +12,7 @@ #include "nss_scoped_ptrs.h" -#include "gcm-vectors.h" +#include "testvectors/gcm-vectors.h" #include "gtest/gtest.h" #include "util.h" @@ -26,87 +26,120 @@ class Pkcs11AesGcmTest : public ::testing::TestWithParam<gcm_kat_value> { std::vector<uint8_t> plaintext = hex_string_to_bytes(val.plaintext); std::vector<uint8_t> aad = hex_string_to_bytes(val.additional_data); std::vector<uint8_t> 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(); // Ignore GHASH-only vectors. if (key.empty()) { return; } // Prepare AEAD params. - CK_GCM_PARAMS gcmParams; - gcmParams.pIv = iv.data(); - gcmParams.ulIvLen = iv.size(); - gcmParams.pAAD = aad.data(); - gcmParams.ulAADLen = aad.size(); - gcmParams.ulTagBits = 128; + CK_GCM_PARAMS gcm_params; + gcm_params.pIv = iv.data(); + gcm_params.ulIvLen = iv.size(); + gcm_params.pAAD = aad.data(); + gcm_params.ulAADLen = aad.size(); + gcm_params.ulTagBits = 128; - SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&gcmParams), - sizeof(gcmParams)}; + SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&gcm_params), + sizeof(gcm_params)}; ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); - SECItem keyItem = {siBuffer, key.data(), - static_cast<unsigned int>(key.size())}; + SECItem key_item = {siBuffer, key.data(), + static_cast<unsigned int>(key.size())}; // Import key. - ScopedPK11SymKey symKey(PK11_ImportSymKey( - slot.get(), mech, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr)); - EXPECT_TRUE(!!symKey); + ScopedPK11SymKey sym_key(PK11_ImportSymKey( + slot.get(), mech, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, nullptr)); + ASSERT_TRUE(!!sym_key) << msg; + + // Encrypt with bogus parameters. + unsigned int output_len = 0; + std::vector<uint8_t> output(plaintext.size() + gcm_params.ulTagBits / 8); + // "maxout" must be at least "inlen + tagBytes", or, in this case: + // "output.size()" must be at least "plaintext.size() + tagBytes" + gcm_params.ulTagBits = 128; + SECStatus rv = + PK11_Encrypt(sym_key.get(), mech, ¶ms, output.data(), &output_len, + output.size() - 10, plaintext.data(), plaintext.size()); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, output_len); + + // The valid values for tag size in AES_GCM are: + // 32, 64, 96, 104, 112, 120 and 128. + gcm_params.ulTagBits = 110; + rv = PK11_Encrypt(sym_key.get(), mech, ¶ms, output.data(), &output_len, + output.size(), plaintext.data(), plaintext.size()); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, output_len); // Encrypt. - unsigned int outputLen = 0; - std::vector<uint8_t> output(plaintext.size() + gcmParams.ulTagBits / 8); - SECStatus rv = - PK11_Encrypt(symKey.get(), mech, ¶ms, output.data(), &outputLen, - output.size(), plaintext.data(), plaintext.size()); - EXPECT_EQ(rv, SECSuccess); - ASSERT_EQ(outputLen, output.size()); + gcm_params.ulTagBits = 128; + rv = PK11_Encrypt(sym_key.get(), mech, ¶ms, output.data(), &output_len, + output.size(), plaintext.data(), plaintext.size()); + if (invalid_iv) { + EXPECT_EQ(SECFailure, rv) << msg; + EXPECT_EQ(0U, output_len); + return; + } + EXPECT_EQ(SECSuccess, rv) << msg; + + ASSERT_EQ(output_len, output.size()) << msg; // Check ciphertext and tag. - EXPECT_EQ(result, output); + if (invalid_ct) { + EXPECT_NE(result, output) << msg; + } else { + EXPECT_EQ(result, output) << msg; + } // Decrypt. - unsigned int decryptedLen = 0; + unsigned int decrypted_len = 0; // The PK11 AES API is stupid, it expects an explicit IV and thus wants // a block more of available output memory. std::vector<uint8_t> decrypted(output.size()); - rv = - PK11_Decrypt(symKey.get(), mech, ¶ms, decrypted.data(), - &decryptedLen, decrypted.size(), output.data(), outputLen); - EXPECT_EQ(rv, SECSuccess); - ASSERT_EQ(decryptedLen, plaintext.size()); + rv = PK11_Decrypt(sym_key.get(), mech, ¶ms, decrypted.data(), + &decrypted_len, decrypted.size(), output.data(), + output_len); + EXPECT_EQ(SECSuccess, rv) << msg; + ASSERT_EQ(decrypted_len, plaintext.size()) << msg; // Check the plaintext. EXPECT_EQ(plaintext, std::vector<uint8_t>(decrypted.begin(), - decrypted.begin() + decryptedLen)); + decrypted.begin() + decrypted_len)) + << msg; } SECStatus EncryptWithIV(std::vector<uint8_t>& iv) { // Generate a random key. ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); - ScopedPK11SymKey symKey( + ScopedPK11SymKey sym_key( PK11_KeyGen(slot.get(), mech, nullptr, 16, nullptr)); - EXPECT_TRUE(!!symKey); + EXPECT_TRUE(!!sym_key); std::vector<uint8_t> data(17); std::vector<uint8_t> output(33); std::vector<uint8_t> aad(0); // Prepare AEAD params. - CK_GCM_PARAMS gcmParams; - gcmParams.pIv = iv.data(); - gcmParams.ulIvLen = iv.size(); - gcmParams.pAAD = aad.data(); - gcmParams.ulAADLen = aad.size(); - gcmParams.ulTagBits = 128; + CK_GCM_PARAMS gcm_params; + gcm_params.pIv = iv.data(); + gcm_params.ulIvLen = iv.size(); + gcm_params.pAAD = aad.data(); + gcm_params.ulAADLen = aad.size(); + gcm_params.ulTagBits = 128; - SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&gcmParams), - sizeof(gcmParams)}; + SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&gcm_params), + sizeof(gcm_params)}; // Try to encrypt. - unsigned int outputLen = 0; - return PK11_Encrypt(symKey.get(), mech, ¶ms, output.data(), &outputLen, - output.size(), data.data(), data.size()); + unsigned int output_len = 0; + return PK11_Encrypt(sym_key.get(), mech, ¶ms, output.data(), + &output_len, output.size(), data.data(), data.size()); } const CK_MECHANISM_TYPE mech = CKM_AES_GCM; @@ -117,19 +150,22 @@ TEST_P(Pkcs11AesGcmTest, TestVectors) { RunTest(GetParam()); } INSTANTIATE_TEST_CASE_P(NISTTestVector, Pkcs11AesGcmTest, ::testing::ValuesIn(kGcmKatValues)); +INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11AesGcmTest, + ::testing::ValuesIn(kGcmWycheproofVectors)); + TEST_F(Pkcs11AesGcmTest, ZeroLengthIV) { std::vector<uint8_t> iv(0); - EXPECT_EQ(EncryptWithIV(iv), SECFailure); + EXPECT_EQ(SECFailure, EncryptWithIV(iv)); } TEST_F(Pkcs11AesGcmTest, AllZeroIV) { std::vector<uint8_t> iv(16, 0); - EXPECT_EQ(EncryptWithIV(iv), SECSuccess); + EXPECT_EQ(SECSuccess, EncryptWithIV(iv)); } TEST_F(Pkcs11AesGcmTest, TwelveByteZeroIV) { std::vector<uint8_t> iv(12, 0); - EXPECT_EQ(EncryptWithIV(iv), SECSuccess); + EXPECT_EQ(SECSuccess, EncryptWithIV(iv)); } } // 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 4d4250a5e..0aa711dc8 100644 --- a/security/nss/gtests/pk11_gtest/pk11_aeskeywrap_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_aeskeywrap_unittest.cc @@ -8,125 +8,115 @@ #include "nss.h" #include "pk11pub.h" +#include "testvectors/kw-vectors.h" #include "gtest/gtest.h" #include "nss_scoped_ptrs.h" namespace nss_test { -// Test vectors from https://tools.ietf.org/html/rfc3394#section-4.1 to 4.6 -unsigned char kKEK1[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; - -unsigned char kKD1[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; - -unsigned char kC1[] = {0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47, - 0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82, - 0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5}; - -unsigned char kKEK2[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17}; - -unsigned char kC2[] = {0x96, 0x77, 0x8B, 0x25, 0xAE, 0x6C, 0xA4, 0x35, - 0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2, - 0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D}; - -unsigned char kKEK3[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F}; - -unsigned char kC3[] = {0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2, - 0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A, - 0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7}; - -unsigned char kKD4[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; - -unsigned char kC4[] = {0x03, 0x1D, 0x33, 0x26, 0x4E, 0x15, 0xD3, 0x32, - 0x68, 0xF2, 0x4E, 0xC2, 0x60, 0x74, 0x3E, 0xDC, - 0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93, - 0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2}; - -unsigned char kC5[] = {0xA8, 0xF9, 0xBC, 0x16, 0x12, 0xC6, 0x8B, 0x3F, - 0xF6, 0xE6, 0xF4, 0xFB, 0xE3, 0x0E, 0x71, 0xE4, - 0x76, 0x9C, 0x8B, 0x80, 0xA3, 0x2C, 0xB8, 0x95, - 0x8C, 0xD5, 0xD1, 0x7D, 0x6B, 0x25, 0x4D, 0xA1}; - -unsigned char kKD6[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, - 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; - -unsigned char kC6[] = {0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4, - 0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26, - 0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26, - 0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B, - 0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21}; - -class Pkcs11AESKeyWrapTest : public ::testing::Test { +class Pkcs11AESKeyWrapTest : public ::testing::TestWithParam<keywrap_vector> { protected: CK_MECHANISM_TYPE mechanism = CKM_NSS_AES_KEY_WRAP; - void WrapUnwrap(unsigned char* kek, unsigned int kekLen, - unsigned char* keyData, unsigned int keyDataLen, - unsigned char* expectedCiphertext) { - unsigned char wrappedKey[40]; - unsigned int wrappedKeyLen; - unsigned char unwrappedKey[40]; - unsigned int unwrappedKeyLen = 0; + 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<Action, Result> tests, uint32_t test_id) { + std::vector<unsigned char> wrapped_key(PR_MAX(1U, expected_ciphertext_len)); + std::vector<unsigned char> unwrapped_key(PR_MAX(1U, key_data_len)); + std::vector<unsigned char> zeros(PR_MAX(1U, expected_ciphertext_len)); + std::fill(zeros.begin(), zeros.end(), 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); + ASSERT_NE(nullptr, slot) << msg; // Import encryption key. - SECItem keyItem = {siBuffer, kek, kekLen}; - ScopedPK11SymKey encryptionKey( - PK11_ImportSymKey(slot.get(), CKM_NSS_AES_KEY_WRAP, PK11_OriginUnwrap, - CKA_ENCRYPT, &keyItem, nullptr)); - EXPECT_TRUE(!!encryptionKey); + SECItem kek_item = {siBuffer, kek_data, kek_len}; + ScopedPK11SymKey kek(PK11_ImportSymKey(slot.get(), CKM_NSS_AES_KEY_WRAP, + PK11_OriginUnwrap, CKA_ENCRYPT, + &kek_item, nullptr)); + EXPECT_TRUE(!!kek) << msg; // Wrap key - rv = PK11_Encrypt(encryptionKey.get(), mechanism, nullptr /* param */, - wrappedKey, &wrappedKeyLen, sizeof(wrappedKey), keyData, - keyDataLen); - EXPECT_EQ(rv, SECSuccess) << "CKM_NSS_AES_KEY_WRAP encrypt failed"; - EXPECT_TRUE(!memcmp(expectedCiphertext, wrappedKey, wrappedKeyLen)); + 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 - rv = PK11_Decrypt(encryptionKey.get(), mechanism, nullptr /* param */, - unwrappedKey, &unwrappedKeyLen, sizeof(unwrappedKey), - wrappedKey, wrappedKeyLen); - EXPECT_EQ(rv, SECSuccess) << " CKM_NSS_AES_KEY_WRAP decrypt failed\n"; - EXPECT_TRUE(!memcmp(keyData, unwrappedKey, unwrappedKeyLen)); + 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; + } + } + } } -}; - -TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest1) { - WrapUnwrap(kKEK1, sizeof(kKEK1), kKD1, sizeof(kKD1), kC1); -} -TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest2) { - WrapUnwrap(kKEK2, sizeof(kKEK2), kKD1, sizeof(kKD1), kC2); -} - -TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest3) { - WrapUnwrap(kKEK3, sizeof(kKEK3), kKD1, sizeof(kKD1), kC3); -} - -TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest4) { - WrapUnwrap(kKEK2, sizeof(kKEK2), kKD4, sizeof(kKD4), kC4); -} - -TEST_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest5) { - WrapUnwrap(kKEK3, sizeof(kKEK3), kKD4, sizeof(kKD4), kC5); -} + 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_F(Pkcs11AESKeyWrapTest, WrapUnwrepTest6) { - WrapUnwrap(kKEK3, sizeof(kKEK3), kKD6, sizeof(kKD6), kC6); -} +TEST_P(Pkcs11AESKeyWrapTest, TestVectors) { WrapUnwrap(GetParam()); } +INSTANTIATE_TEST_CASE_P(Pkcs11WycheproofAESKWTest, Pkcs11AESKeyWrapTest, + ::testing::ValuesIn(kWycheproofAesKWVectors)); } /* nss_test */ diff --git a/security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc new file mode 100644 index 000000000..0f79abed5 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc @@ -0,0 +1,415 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <memory> +#include "gtest/gtest.h" +#include "nss.h" +#include "nss_scoped_ptrs.h" +#include "pk11pub.h" + +namespace nss_test { + +class Pkcs11AESKeyWrapPadTest : public ::testing::Test {}; + +// Encrypt an ephemeral EC key (U2F use case) +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapECKey) { + const uint32_t kwrappedBufLen = 256; + const uint32_t kPublicKeyLen = 65; + const uint32_t kOidLen = 65; + unsigned char param_buf[kOidLen]; + unsigned char unwrap_buf[kPublicKeyLen]; + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + + SECItem ecdsa_params = {siBuffer, param_buf, sizeof(param_buf)}; + SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); + ASSERT_NE(oid_data, nullptr); + 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; + ScopedSECKEYPublicKey pub_key; + ScopedSECKEYPrivateKey priv_key( + PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsa_params, + &pub_tmp, PR_FALSE, PR_TRUE, nullptr)); + ASSERT_NE(nullptr, priv_key); + ASSERT_NE(nullptr, pub_tmp); + pub_key.reset(pub_tmp); + + // Generate a KEK. + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + ScopedSECItem wrapped(::SECITEM_AllocItem(nullptr, nullptr, kwrappedBufLen)); + ScopedSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, nullptr)); + + SECStatus rv = PK11_WrapPrivKey(slot.get(), kek.get(), priv_key.get(), + CKM_NSS_AES_KEY_WRAP_PAD, param.get(), + wrapped.get(), nullptr); + ASSERT_EQ(rv, SECSuccess); + + SECItem pubKey = {siBuffer, unwrap_buf, kPublicKeyLen}; + CK_ATTRIBUTE_TYPE usages[] = {CKA_SIGN}; + int usageCount = 1; + + ScopedSECKEYPrivateKey unwrapped( + PK11_UnwrapPrivKey(slot.get(), kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, + param.get(), wrapped.get(), nullptr, &pubKey, false, + true, CKK_EC, usages, usageCount, nullptr)); + ASSERT_EQ(0, PORT_GetError()); + ASSERT_TRUE(!!unwrapped); +} + +// Encrypt an ephemeral RSA key +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRsaKey) { + const uint32_t kwrappedBufLen = 648; + unsigned char unwrap_buf[kwrappedBufLen]; + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + + 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); + + // Generate a KEK. + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + ScopedSECItem wrapped(::SECITEM_AllocItem(nullptr, nullptr, kwrappedBufLen)); + ScopedSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, nullptr)); + + SECStatus rv = PK11_WrapPrivKey(slot.get(), kek.get(), priv_key.get(), + CKM_NSS_AES_KEY_WRAP_PAD, param.get(), + wrapped.get(), nullptr); + ASSERT_EQ(rv, SECSuccess); + + SECItem pubKey = {siBuffer, unwrap_buf, kwrappedBufLen}; + CK_ATTRIBUTE_TYPE usages[] = {CKA_SIGN}; + int usageCount = 1; + + ScopedSECKEYPrivateKey unwrapped( + PK11_UnwrapPrivKey(slot.get(), kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, + param.get(), wrapped.get(), nullptr, &pubKey, false, + false, CKK_EC, usages, usageCount, nullptr)); + ASSERT_EQ(0, PORT_GetError()); + ASSERT_TRUE(!!unwrapped); + + ScopedSECItem priv_key_data( + PK11_ExportDERPrivateKeyInfo(priv_key.get(), nullptr)); + ScopedSECItem unwrapped_data( + PK11_ExportDERPrivateKeyInfo(unwrapped.get(), nullptr)); + EXPECT_TRUE(!!priv_key_data); + EXPECT_TRUE(!!unwrapped_data); + ASSERT_EQ(priv_key_data->len, unwrapped_data->len); + ASSERT_EQ( + 0, memcmp(priv_key_data->data, unwrapped_data->data, priv_key_data->len)); +} + +// Wrap a random that's a multiple of the block size, and compare the unwrap +// result. +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_EvenBlock) { + const uint32_t kInputKeyLen = 128; + uint32_t out_len = 0; + std::vector<unsigned char> input_key(kInputKeyLen); + std::vector<unsigned char> wrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + std::vector<unsigned char> unwrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + + // Generate input key material + SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size()); + EXPECT_EQ(SECSuccess, rv); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + wrapped_key.data(), &out_len, + static_cast<unsigned int>(wrapped_key.size()), + input_key.data(), + static_cast<unsigned int>(input_key.size())); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(input_key.size(), out_len); + ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len)); +} + +// Wrap a random that's NOT a multiple of the block size, and compare the unwrap +// result. +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_OddBlock1) { + const uint32_t kInputKeyLen = 65; + uint32_t out_len = 0; + std::vector<unsigned char> input_key(kInputKeyLen); + std::vector<unsigned char> wrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + std::vector<unsigned char> unwrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + + // Generate input key material + SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size()); + EXPECT_EQ(SECSuccess, rv); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + wrapped_key.data(), &out_len, + static_cast<unsigned int>(wrapped_key.size()), + input_key.data(), + static_cast<unsigned int>(input_key.size())); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(input_key.size(), out_len); + ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len)); +} + +// Wrap a random that's NOT a multiple of the block size, and compare the unwrap +// result. +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_OddBlock2) { + const uint32_t kInputKeyLen = 63; + uint32_t out_len = 0; + std::vector<unsigned char> input_key(kInputKeyLen); + std::vector<unsigned char> wrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + std::vector<unsigned char> unwrapped_key( + kInputKeyLen + AES_BLOCK_SIZE); // One block of padding + + // Generate input key material + SECStatus rv = PK11_GenerateRandom(input_key.data(), input_key.size()); + EXPECT_EQ(SECSuccess, rv); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + rv = PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + wrapped_key.data(), &out_len, wrapped_key.size(), + input_key.data(), input_key.size()); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(input_key.size(), out_len); + ASSERT_EQ(0, memcmp(input_key.data(), unwrapped_key.data(), out_len)); +} + +// Invalid long padding (over the block size, but otherwise valid) +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_PaddingTooLong) { + const uint32_t kInputKeyLen = 32; + uint32_t out_len = 0; + + // Apply our own padding + const unsigned char buf[32] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}; + std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + SECStatus rv = + PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding + /* param */ nullptr, wrapped_key.data(), &out_len, + wrapped_key.size(), buf, sizeof(buf)); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECFailure, rv); +} + +// Invalid 0-length padding (there should be a full block if the message doesn't +// need to be padded) +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_NoPadding) { + const uint32_t kInputKeyLen = 32; + uint32_t out_len = 0; + + // Apply our own padding + const unsigned char buf[32] = {0}; + std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + SECStatus rv = + PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding + /* param */ nullptr, wrapped_key.data(), &out_len, + wrapped_key.size(), buf, sizeof(buf)); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECFailure, rv); +} + +// Invalid padding +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_BadPadding1) { + const uint32_t kInputKeyLen = 32; + uint32_t out_len = 0; + + // Apply our own padding + const unsigned char buf[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08}; // Check all 8 bytes + std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + SECStatus rv = + PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding + /* param */ nullptr, wrapped_key.data(), &out_len, + wrapped_key.size(), buf, sizeof(buf)); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECFailure, rv); +} + +// Invalid padding +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_BadPadding2) { + const uint32_t kInputKeyLen = 32; + uint32_t out_len = 0; + + // Apply our own padding + const unsigned char + buf[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x02}; // Check first loop repeat + std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + SECStatus rv = + PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding + /* param */ nullptr, wrapped_key.data(), &out_len, + wrapped_key.size(), buf, sizeof(buf)); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECFailure, rv); +} + +// Minimum valid padding +TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_ShortValidPadding) { + const uint32_t kInputKeyLen = 32; + uint32_t out_len = 0; + + // Apply our own padding + const unsigned char buf[kInputKeyLen] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; // Minimum + std::vector<unsigned char> wrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + std::vector<unsigned char> unwrapped_key(kInputKeyLen + AES_BLOCK_SIZE); + + // Generate a KEK. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey kek( + PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr)); + ASSERT_NE(nullptr, kek); + + // Wrap the key + SECStatus rv = + PK11_Encrypt(kek.get(), CKM_NSS_AES_KEY_WRAP, // Don't apply more padding + /* param */ nullptr, wrapped_key.data(), &out_len, + wrapped_key.size(), buf, sizeof(buf)); + ASSERT_EQ(SECSuccess, rv); + + rv = PK11_Decrypt(kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, /* param */ nullptr, + unwrapped_key.data(), &out_len, + static_cast<unsigned int>(unwrapped_key.size()), + wrapped_key.data(), out_len); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(kInputKeyLen - 1, out_len); + ASSERT_EQ(0, memcmp(buf, unwrapped_key.data(), out_len)); +} + +} /* nss_test */ diff --git a/security/nss/gtests/pk11_gtest/pk11_cbc_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_cbc_unittest.cc new file mode 100644 index 000000000..ecc705ad4 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_cbc_unittest.cc @@ -0,0 +1,558 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <memory> +#include "nss.h" +#include "pk11pub.h" +#include "secerr.h" + +#include "nss_scoped_ptrs.h" +#include "gtest/gtest.h" + +namespace nss_test { + +static const uint8_t kInput[99] = {1, 2, 3}; +static const uint8_t kKeyData[24] = {'K', 'E', 'Y'}; + +static SECItem* GetIv() { + static const uint8_t kIvData[16] = {'I', 'V'}; + static const SECItem kIv = {siBuffer, const_cast<uint8_t*>(kIvData), + static_cast<unsigned int>(sizeof(kIvData))}; + return const_cast<SECItem*>(&kIv); +} + +class Pkcs11CbcPadTest : public ::testing::TestWithParam<CK_MECHANISM_TYPE> { + protected: + bool is_padded() const { + switch (GetParam()) { + case CKM_AES_CBC_PAD: + case CKM_DES3_CBC_PAD: + return true; + + case CKM_AES_CBC: + case CKM_DES3_CBC: + return false; + + default: + ADD_FAILURE() << "Unknown mechanism " << GetParam(); + } + return false; + } + + uint32_t GetUnpaddedMechanism() const { + switch (GetParam()) { + case CKM_AES_CBC_PAD: + return CKM_AES_CBC; + case CKM_DES3_CBC_PAD: + return CKM_DES3_CBC; + default: + ADD_FAILURE() << "Unknown padded mechanism " << GetParam(); + } + return 0; + } + + size_t block_size() const { + return static_cast<size_t>(PK11_GetBlockSize(GetParam(), nullptr)); + } + + size_t GetInputLen(CK_ATTRIBUTE_TYPE op) const { + if (is_padded() && op == CKA_ENCRYPT) { + // Anything goes for encryption when padded. + return sizeof(kInput); + } + + // Otherwise, use a strict multiple of the block size. + size_t block_count = sizeof(kInput) / block_size(); + EXPECT_LT(1U, block_count) << "need 2 blocks for tests"; + return block_count * block_size(); + } + + ScopedPK11SymKey MakeKey(CK_ATTRIBUTE_TYPE op) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + EXPECT_NE(nullptr, slot); + if (!slot) { + return nullptr; + } + + unsigned int key_len = 0; + switch (GetParam()) { + case CKM_AES_CBC_PAD: + case CKM_AES_CBC: + key_len = 16; // This doesn't do AES-256 to keep it simple. + break; + + case CKM_DES3_CBC_PAD: + case CKM_DES3_CBC: + key_len = 24; + break; + + default: + ADD_FAILURE() << "Unknown mechanism " << GetParam(); + return nullptr; + } + + SECItem key_item = {siBuffer, const_cast<uint8_t*>(kKeyData), key_len}; + PK11SymKey* p = PK11_ImportSymKey(slot.get(), GetParam(), PK11_OriginUnwrap, + op, &key_item, nullptr); + EXPECT_NE(nullptr, p); + return ScopedPK11SymKey(p); + } + + ScopedPK11Context MakeContext(CK_ATTRIBUTE_TYPE op) { + ScopedPK11SymKey k = MakeKey(op); + PK11Context* ctx = + PK11_CreateContextBySymKey(GetParam(), op, k.get(), GetIv()); + EXPECT_NE(nullptr, ctx); + return ScopedPK11Context(ctx); + } +}; + +TEST_P(Pkcs11CbcPadTest, EncryptDecrypt) { + uint8_t encrypted[sizeof(kInput) + 64]; // Allow for padding and expansion. + size_t input_len = GetInputLen(CKA_ENCRYPT); + + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + unsigned int encrypted_len = 0; + SECStatus rv = + PK11_Encrypt(ek.get(), GetParam(), GetIv(), encrypted, &encrypted_len, + sizeof(encrypted), kInput, input_len); + ASSERT_EQ(SECSuccess, rv); + EXPECT_LE(input_len, static_cast<size_t>(encrypted_len)); + + // Though the decrypted result can't be larger than the input we provided, + // NSS needs extra space to put the padding in. + uint8_t decrypted[sizeof(kInput) + 64]; + unsigned int decrypted_len = 0; + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted, &decrypted_len, + sizeof(decrypted), encrypted, encrypted_len); + ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(input_len, static_cast<size_t>(decrypted_len)); + EXPECT_EQ(0, memcmp(kInput, decrypted, input_len)); +} + +TEST_P(Pkcs11CbcPadTest, ContextEncryptDecrypt) { + uint8_t encrypted[sizeof(kInput) + 64]; // Allow for padding and expansion. + size_t input_len = GetInputLen(CKA_ENCRYPT); + + ScopedPK11Context ectx = MakeContext(CKA_ENCRYPT); + int encrypted_len = 0; + SECStatus rv = PK11_CipherOp(ectx.get(), encrypted, &encrypted_len, + sizeof(encrypted), kInput, input_len); + ASSERT_EQ(SECSuccess, rv); + EXPECT_LE(0, encrypted_len); // Stupid signed parameters. + + unsigned int final_len = 0; + rv = PK11_CipherFinal(ectx.get(), encrypted + encrypted_len, &final_len, + sizeof(encrypted) - encrypted_len); + ASSERT_EQ(SECSuccess, rv); + encrypted_len += final_len; + EXPECT_LE(input_len, static_cast<size_t>(encrypted_len)); + + uint8_t decrypted[sizeof(kInput) + 64]; + int decrypted_len = 0; + ScopedPK11Context dctx = MakeContext(CKA_DECRYPT); + rv = PK11_CipherOp(dctx.get(), decrypted, &decrypted_len, sizeof(decrypted), + encrypted, encrypted_len); + ASSERT_EQ(SECSuccess, rv); + EXPECT_LE(0, decrypted_len); + + rv = PK11_CipherFinal(dctx.get(), decrypted + decrypted_len, &final_len, + sizeof(decrypted) - decrypted_len); + ASSERT_EQ(SECSuccess, rv); + decrypted_len += final_len; + EXPECT_EQ(input_len, static_cast<size_t>(decrypted_len)); + EXPECT_EQ(0, memcmp(kInput, decrypted, input_len)); +} + +TEST_P(Pkcs11CbcPadTest, ContextEncryptDecryptTwoParts) { + uint8_t encrypted[sizeof(kInput) + 64]; + size_t input_len = GetInputLen(CKA_ENCRYPT); + + ScopedPK11Context ectx = MakeContext(CKA_ENCRYPT); + int first_len = 0; + SECStatus rv = PK11_CipherOp(ectx.get(), encrypted, &first_len, + sizeof(encrypted), kInput, block_size()); + ASSERT_EQ(SECSuccess, rv); + ASSERT_LE(0, first_len); + + int second_len = 0; + rv = PK11_CipherOp(ectx.get(), encrypted + first_len, &second_len, + sizeof(encrypted) - first_len, kInput + block_size(), + input_len - block_size()); + ASSERT_EQ(SECSuccess, rv); + ASSERT_LE(0, second_len); + + unsigned int final_len = 0; + rv = PK11_CipherFinal(ectx.get(), encrypted + first_len + second_len, + &final_len, sizeof(encrypted) - first_len - second_len); + ASSERT_EQ(SECSuccess, rv); + unsigned int encrypted_len = first_len + second_len + final_len; + ASSERT_LE(input_len, static_cast<size_t>(encrypted_len)); + + // Now decrypt this in a similar fashion. + uint8_t decrypted[sizeof(kInput) + 64]; + ScopedPK11Context dctx = MakeContext(CKA_DECRYPT); + rv = PK11_CipherOp(dctx.get(), decrypted, &first_len, sizeof(decrypted), + encrypted, block_size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_LE(0, first_len); + + rv = PK11_CipherOp(dctx.get(), decrypted + first_len, &second_len, + sizeof(decrypted) - first_len, encrypted + block_size(), + encrypted_len - block_size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_LE(0, second_len); + + unsigned int decrypted_len = 0; + rv = PK11_CipherFinal(dctx.get(), decrypted + first_len + second_len, + &decrypted_len, + sizeof(decrypted) - first_len - second_len); + ASSERT_EQ(SECSuccess, rv); + decrypted_len += first_len + second_len; + EXPECT_EQ(input_len, static_cast<size_t>(decrypted_len)); + EXPECT_EQ(0, memcmp(kInput, decrypted, input_len)); +} + +TEST_P(Pkcs11CbcPadTest, FailDecryptSimple) { + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + uint8_t output[sizeof(kInput) + 64]; + unsigned int output_len = 999; + SECStatus rv = + PK11_Decrypt(dk.get(), GetParam(), GetIv(), output, &output_len, + sizeof(output), kInput, GetInputLen(CKA_DECRYPT)); + if (is_padded()) { + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(999U, output_len); + } else { + // Unpadded decryption can't really fail. + EXPECT_EQ(SECSuccess, rv); + } +} + +TEST_P(Pkcs11CbcPadTest, FailEncryptSimple) { + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + uint8_t output[3]; // Too small for anything. + unsigned int output_len = 333; + + SECStatus rv = + PK11_Encrypt(ek.get(), GetParam(), GetIv(), output, &output_len, + sizeof(output), kInput, GetInputLen(CKA_ENCRYPT)); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(333U, output_len); +} + +// It's a bit of a lie to put this in pk11_cbc_unittest, since we +// also test bounds checking in other modes. There doesn't seem +// to be an appropriately-generic place elsewhere. +TEST_F(Pkcs11CbcPadTest, FailEncryptShortParam) { + SECStatus rv = SECFailure; + uint8_t encrypted[sizeof(kInput)]; + 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)]; + SECItem param = {siBuffer, param_buf, sizeof(param_buf)}; + SECItem key_item = {siBuffer, const_cast<uint8_t*>(kKeyData), 16}; + + // Setup (we use the ECB key for other modes) + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ASSERT_NE(nullptr, slot); + ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), CKM_AES_ECB, + PK11_OriginUnwrap, CKA_ENCRYPT, + &key_item, nullptr)); + ASSERT_TRUE(key.get()); + + // CTR should have a CK_AES_CTR_PARAMS + param.len = sizeof(CK_AES_CTR_PARAMS) - 1; + rv = PK11_Encrypt(key.get(), CKM_AES_CTR, ¶m, encrypted, &encrypted_len, + sizeof(encrypted), kInput, input_len); + EXPECT_EQ(SECFailure, rv); + + param.len++; + reinterpret_cast<CK_AES_CTR_PARAMS*>(param.data)->ulCounterBits = 32; + rv = PK11_Encrypt(key.get(), CKM_AES_CTR, ¶m, encrypted, &encrypted_len, + sizeof(encrypted), kInput, input_len); + EXPECT_EQ(SECSuccess, rv); + + // GCM should have a CK_GCM_PARAMS + param.len = sizeof(CK_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<CK_GCM_PARAMS*>(param.data)->pIv = param_buf; + reinterpret_cast<CK_GCM_PARAMS*>(param.data)->ulIvLen = 12; + reinterpret_cast<CK_GCM_PARAMS*>(param.data)->pAAD = nullptr; + reinterpret_cast<CK_GCM_PARAMS*>(param.data)->ulAADLen = 0; + reinterpret_cast<CK_GCM_PARAMS*>(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); + + // CBC should have a 16B IV + param.len = AES_BLOCK_SIZE - 1; + rv = PK11_Encrypt(key.get(), CKM_AES_CBC, ¶m, encrypted, &encrypted_len, + sizeof(encrypted), kInput, input_len); + EXPECT_EQ(SECFailure, rv); + + param.len++; + rv = PK11_Encrypt(key.get(), CKM_AES_CBC, ¶m, encrypted, &encrypted_len, + sizeof(encrypted), kInput, input_len); + EXPECT_EQ(SECSuccess, rv); + + // CTS + param.len = AES_BLOCK_SIZE - 1; + rv = PK11_Encrypt(key.get(), CKM_AES_CTS, ¶m, encrypted, &encrypted_len, + sizeof(encrypted), kInput, input_len); + EXPECT_EQ(SECFailure, rv); + + param.len++; + rv = PK11_Encrypt(key.get(), CKM_AES_CTS, ¶m, encrypted, &encrypted_len, + sizeof(encrypted), kInput, input_len); + EXPECT_EQ(SECSuccess, rv); +} + +TEST_P(Pkcs11CbcPadTest, ContextFailDecryptSimple) { + ScopedPK11Context dctx = MakeContext(CKA_DECRYPT); + uint8_t output[sizeof(kInput) + 64]; + int output_len = 77; + + SECStatus rv = PK11_CipherOp(dctx.get(), output, &output_len, sizeof(output), + kInput, GetInputLen(CKA_DECRYPT)); + EXPECT_EQ(SECSuccess, rv); + EXPECT_LE(0, output_len) << "this is not an AEAD, so content leaks"; + + unsigned int final_len = 88; + rv = PK11_CipherFinal(dctx.get(), output, &final_len, sizeof(output)); + if (is_padded()) { + EXPECT_EQ(SECFailure, rv); + ASSERT_EQ(88U, final_len) << "final_len should be untouched"; + } else { + // Unpadded decryption can't really fail. + EXPECT_EQ(SECSuccess, rv); + } +} + +TEST_P(Pkcs11CbcPadTest, ContextFailDecryptInvalidBlockSize) { + ScopedPK11Context dctx = MakeContext(CKA_DECRYPT); + uint8_t output[sizeof(kInput) + 64]; + int output_len = 888; + + SECStatus rv = PK11_CipherOp(dctx.get(), output, &output_len, sizeof(output), + kInput, GetInputLen(CKA_DECRYPT) - 1); + EXPECT_EQ(SECFailure, rv); + // Because PK11_CipherOp is partial, it can return data on failure. + // This means that it needs to reset its output length to 0 when it starts. + EXPECT_EQ(0, output_len) << "output_len is reset"; +} + +TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_PaddingTooLong) { + if (!is_padded()) { + return; + } + + // Padding that's over the block size + const std::vector<uint8_t> input = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}; + std::vector<uint8_t> encrypted(input.size()); + uint32_t encrypted_len = 0; + + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(), + encrypted.data(), &encrypted_len, + encrypted.size(), input.data(), input.size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size(), encrypted_len); + + std::vector<uint8_t> decrypted(input.size()); + uint32_t decrypted_len = 0; + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(), + &decrypted_len, decrypted.size(), encrypted.data(), + encrypted_len); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, decrypted_len); +} + +TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_ShortPadding1) { + if (!is_padded()) { + return; + } + + // Padding that's one byte short + const std::vector<uint8_t> input = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; + std::vector<uint8_t> encrypted(input.size()); + uint32_t encrypted_len = 0; + + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(), + encrypted.data(), &encrypted_len, + encrypted.size(), input.data(), input.size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size(), encrypted_len); + + std::vector<uint8_t> decrypted(input.size()); + uint32_t decrypted_len = 0; + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(), + &decrypted_len, decrypted.size(), encrypted.data(), + encrypted_len); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, decrypted_len); +} + +TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_ShortPadding2) { + if (!is_padded()) { + return; + } + + // Padding that's one byte short + const std::vector<uint8_t> input = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}; + std::vector<uint8_t> encrypted(input.size()); + uint32_t encrypted_len = 0; + + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(), + encrypted.data(), &encrypted_len, + encrypted.size(), input.data(), input.size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size(), encrypted_len); + + std::vector<uint8_t> decrypted(input.size()); + uint32_t decrypted_len = 0; + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(), + &decrypted_len, decrypted.size(), encrypted.data(), + encrypted_len); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, decrypted_len); +} + +TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_ZeroLengthPadding) { + if (!is_padded()) { + return; + } + + // Padding of length zero + const std::vector<uint8_t> input = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::vector<uint8_t> encrypted(input.size()); + uint32_t encrypted_len = 0; + + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(), + encrypted.data(), &encrypted_len, + encrypted.size(), input.data(), input.size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size(), encrypted_len); + + std::vector<uint8_t> decrypted(input.size()); + uint32_t decrypted_len = 0; + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(), + &decrypted_len, decrypted.size(), encrypted.data(), + encrypted_len); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, decrypted_len); +} + +TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_OverflowPadding) { + if (!is_padded()) { + return; + } + + // Padding that's much longer than block size + const std::vector<uint8_t> input = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + std::vector<uint8_t> encrypted(input.size()); + uint32_t encrypted_len = 0; + + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(), + encrypted.data(), &encrypted_len, + encrypted.size(), input.data(), input.size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size(), encrypted_len); + + std::vector<uint8_t> decrypted(input.size()); + uint32_t decrypted_len = 0; + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(), + &decrypted_len, decrypted.size(), encrypted.data(), + encrypted_len); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(0U, decrypted_len); +} + +TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_ShortValidPadding) { + if (!is_padded()) { + return; + } + + // Minimal valid padding + const std::vector<uint8_t> input = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + std::vector<uint8_t> encrypted(input.size()); + uint32_t encrypted_len = 0; + + ScopedPK11SymKey ek = MakeKey(CKA_ENCRYPT); + SECStatus rv = PK11_Encrypt(ek.get(), GetUnpaddedMechanism(), GetIv(), + encrypted.data(), &encrypted_len, + encrypted.size(), input.data(), input.size()); + ASSERT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size(), encrypted_len); + + std::vector<uint8_t> decrypted(input.size()); + uint32_t decrypted_len = 0; + ScopedPK11SymKey dk = MakeKey(CKA_DECRYPT); + rv = PK11_Decrypt(dk.get(), GetParam(), GetIv(), decrypted.data(), + &decrypted_len, decrypted.size(), encrypted.data(), + encrypted_len); + EXPECT_EQ(SECSuccess, rv); + EXPECT_EQ(input.size() - 1, decrypted_len); + EXPECT_EQ(0, memcmp(decrypted.data(), input.data(), decrypted_len)); +} + +INSTANTIATE_TEST_CASE_P(EncryptDecrypt, Pkcs11CbcPadTest, + ::testing::Values(CKM_AES_CBC_PAD, CKM_AES_CBC, + CKM_DES3_CBC_PAD, CKM_DES3_CBC)); + +} // 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 07bc91ee6..882f1f0d2 100644 --- a/security/nss/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc @@ -8,114 +8,31 @@ #include "nss.h" #include "pk11pub.h" #include "sechash.h" +#include "secerr.h" #include "cpputil.h" #include "nss_scoped_ptrs.h" +#include "testvectors/chachapoly-vectors.h" #include "gtest/gtest.h" namespace nss_test { -// ChaCha20/Poly1305 Test Vector 1, RFC 7539 -// <http://tools.ietf.org/html/rfc7539#section-2.8.2> -const uint8_t kTestVector1Data[] = { - 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, - 0x65, 0x6e, 0x74, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x6f, 0x66, - 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, - 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x79, - 0x6f, 0x75, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, - 0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73, - 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, - 0x62, 0x65, 0x20, 0x69, 0x74, 0x2e}; -const uint8_t kTestVector1AAD[] = {0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, - 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7}; -const uint8_t kTestVector1Key[] = { - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, - 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, - 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f}; -const uint8_t kTestVector1IV[] = {0x07, 0x00, 0x00, 0x00, 0x40, 0x41, - 0x42, 0x43, 0x44, 0x45, 0x46, 0x47}; -const uint8_t kTestVector1CT[] = { - 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, - 0x53, 0xef, 0x7e, 0xc2, 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, - 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, 0x3d, 0xbe, 0xa4, 0x5e, - 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, - 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6, - 0x7e, 0xcd, 0x3b, 0x36, 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, - 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, 0xfa, 0xb3, 0x24, 0xe4, - 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, - 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65, - 0x86, 0xce, 0xc6, 0x4b, 0x61, 0x16, 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, - 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91}; - -// ChaCha20/Poly1305 Test Vector 2, RFC 7539 -// <http://tools.ietf.org/html/rfc7539#appendix-A.5> -const uint8_t kTestVector2Data[] = { - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2d, 0x44, 0x72, 0x61, - 0x66, 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x64, 0x72, 0x61, 0x66, - 0x74, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x20, - 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x20, 0x6f, 0x66, 0x20, 0x73, - 0x69, 0x78, 0x20, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x73, 0x20, 0x61, 0x6e, - 0x64, 0x20, 0x6d, 0x61, 0x79, 0x20, 0x62, 0x65, 0x20, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, - 0x65, 0x64, 0x2c, 0x20, 0x6f, 0x72, 0x20, 0x6f, 0x62, 0x73, 0x6f, 0x6c, - 0x65, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6f, 0x74, 0x68, 0x65, - 0x72, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, - 0x61, 0x74, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x2e, - 0x20, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x61, 0x70, 0x70, - 0x72, 0x6f, 0x70, 0x72, 0x69, 0x61, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, - 0x75, 0x73, 0x65, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, - 0x2d, 0x44, 0x72, 0x61, 0x66, 0x74, 0x73, 0x20, 0x61, 0x73, 0x20, 0x72, - 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x6d, 0x61, 0x74, - 0x65, 0x72, 0x69, 0x61, 0x6c, 0x20, 0x6f, 0x72, 0x20, 0x74, 0x6f, 0x20, - 0x63, 0x69, 0x74, 0x65, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x20, 0x6f, 0x74, - 0x68, 0x65, 0x72, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x20, - 0x2f, 0xe2, 0x80, 0x9c, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x69, 0x6e, 0x20, - 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x2f, 0xe2, 0x80, - 0x9d}; -const uint8_t kTestVector2AAD[] = {0xf3, 0x33, 0x88, 0x86, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4e, 0x91}; -const uint8_t kTestVector2Key[] = { - 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, 0xf3, 0x33, 0x88, - 0x86, 0x04, 0xf6, 0xb5, 0xf0, 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, - 0x80, 0x09, 0x9d, 0xca, 0x5c, 0xbc, 0x20, 0x70, 0x75, 0xc0}; -const uint8_t kTestVector2IV[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x02, - 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; -const uint8_t kTestVector2CT[] = { - 0x64, 0xa0, 0x86, 0x15, 0x75, 0x86, 0x1a, 0xf4, 0x60, 0xf0, 0x62, 0xc7, - 0x9b, 0xe6, 0x43, 0xbd, 0x5e, 0x80, 0x5c, 0xfd, 0x34, 0x5c, 0xf3, 0x89, - 0xf1, 0x08, 0x67, 0x0a, 0xc7, 0x6c, 0x8c, 0xb2, 0x4c, 0x6c, 0xfc, 0x18, - 0x75, 0x5d, 0x43, 0xee, 0xa0, 0x9e, 0xe9, 0x4e, 0x38, 0x2d, 0x26, 0xb0, - 0xbd, 0xb7, 0xb7, 0x3c, 0x32, 0x1b, 0x01, 0x00, 0xd4, 0xf0, 0x3b, 0x7f, - 0x35, 0x58, 0x94, 0xcf, 0x33, 0x2f, 0x83, 0x0e, 0x71, 0x0b, 0x97, 0xce, - 0x98, 0xc8, 0xa8, 0x4a, 0xbd, 0x0b, 0x94, 0x81, 0x14, 0xad, 0x17, 0x6e, - 0x00, 0x8d, 0x33, 0xbd, 0x60, 0xf9, 0x82, 0xb1, 0xff, 0x37, 0xc8, 0x55, - 0x97, 0x97, 0xa0, 0x6e, 0xf4, 0xf0, 0xef, 0x61, 0xc1, 0x86, 0x32, 0x4e, - 0x2b, 0x35, 0x06, 0x38, 0x36, 0x06, 0x90, 0x7b, 0x6a, 0x7c, 0x02, 0xb0, - 0xf9, 0xf6, 0x15, 0x7b, 0x53, 0xc8, 0x67, 0xe4, 0xb9, 0x16, 0x6c, 0x76, - 0x7b, 0x80, 0x4d, 0x46, 0xa5, 0x9b, 0x52, 0x16, 0xcd, 0xe7, 0xa4, 0xe9, - 0x90, 0x40, 0xc5, 0xa4, 0x04, 0x33, 0x22, 0x5e, 0xe2, 0x82, 0xa1, 0xb0, - 0xa0, 0x6c, 0x52, 0x3e, 0xaf, 0x45, 0x34, 0xd7, 0xf8, 0x3f, 0xa1, 0x15, - 0x5b, 0x00, 0x47, 0x71, 0x8c, 0xbc, 0x54, 0x6a, 0x0d, 0x07, 0x2b, 0x04, - 0xb3, 0x56, 0x4e, 0xea, 0x1b, 0x42, 0x22, 0x73, 0xf5, 0x48, 0x27, 0x1a, - 0x0b, 0xb2, 0x31, 0x60, 0x53, 0xfa, 0x76, 0x99, 0x19, 0x55, 0xeb, 0xd6, - 0x31, 0x59, 0x43, 0x4e, 0xce, 0xbb, 0x4e, 0x46, 0x6d, 0xae, 0x5a, 0x10, - 0x73, 0xa6, 0x72, 0x76, 0x27, 0x09, 0x7a, 0x10, 0x49, 0xe6, 0x17, 0xd9, - 0x1d, 0x36, 0x10, 0x94, 0xfa, 0x68, 0xf0, 0xff, 0x77, 0x98, 0x71, 0x30, - 0x30, 0x5b, 0xea, 0xba, 0x2e, 0xda, 0x04, 0xdf, 0x99, 0x7b, 0x71, 0x4d, - 0x6c, 0x6f, 0x2c, 0x29, 0xa6, 0xad, 0x5c, 0xb4, 0x02, 0x2b, 0x02, 0x70, - 0x9b, 0xee, 0xad, 0x9d, 0x67, 0x89, 0x0c, 0xbb, 0x22, 0x39, 0x23, 0x36, - 0xfe, 0xa1, 0x85, 0x1f, 0x38}; - -class Pkcs11ChaCha20Poly1305Test : public ::testing::Test { +static const CK_MECHANISM_TYPE kMech = CKM_NSS_CHACHA20_POLY1305; +static const CK_MECHANISM_TYPE kMechXor = CKM_NSS_CHACHA20_CTR; +// Some test data for simple tests. +static const uint8_t kKeyData[32] = {'k'}; +static const uint8_t kCtrNonce[16] = {'c', 0, 0, 0, 'n'}; +static const uint8_t kData[16] = {'d'}; + +class Pkcs11ChaCha20Poly1305Test + : public ::testing::TestWithParam<chaChaTestVector> { public: - void EncryptDecrypt(PK11SymKey* symKey, const uint8_t* data, size_t data_len, - const uint8_t* aad, size_t aad_len, const uint8_t* iv, - size_t iv_len, const uint8_t* ct = nullptr, - size_t ct_len = 0) { + void EncryptDecrypt(const ScopedPK11SymKey& key, const bool invalid_iv, + const bool invalid_tag, const uint8_t* data, + size_t data_len, const uint8_t* aad, size_t aad_len, + const uint8_t* iv, size_t iv_len, + const uint8_t* ct = nullptr, size_t ct_len = 0) { // Prepare AEAD params. CK_NSS_AEAD_PARAMS aead_params; aead_params.pNonce = toUcharPtr(iv); @@ -127,135 +44,261 @@ class Pkcs11ChaCha20Poly1305Test : public ::testing::Test { SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&aead_params), sizeof(aead_params)}; + // Encrypt with bad parameters. + unsigned int encrypted_len = 0; + std::vector<uint8_t> encrypted(data_len + aead_params.ulTagLen); + aead_params.ulTagLen = 158072; + SECStatus 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); + aead_params.ulTagLen = 16; + // Encrypt. - unsigned int outputLen = 0; - std::vector<uint8_t> output(data_len + aead_params.ulTagLen); - SECStatus rv = PK11_Encrypt(symKey, mech, ¶ms, &output[0], &outputLen, - output.size(), data, data_len); + rv = PK11_Encrypt(key.get(), kMech, ¶ms, encrypted.data(), + &encrypted_len, encrypted.size(), data, data_len); + + // Return if encryption failure was expected due to invalid IV. + // Without valid ciphertext, all further tests can be skipped. + if (invalid_iv) { + EXPECT_EQ(rv, SECFailure); + EXPECT_EQ(0U, encrypted_len) + << "encrypted_len is unmodified after failure"; + return; + } + EXPECT_EQ(rv, SECSuccess); + EXPECT_EQ(encrypted.size(), static_cast<size_t>(encrypted_len)); // Check ciphertext and tag. if (ct) { - EXPECT_TRUE(!memcmp(ct, &output[0], outputLen)); + ASSERT_EQ(ct_len, encrypted_len); + EXPECT_TRUE(!memcmp(ct, encrypted.data(), encrypted.size()) != + invalid_tag); } - // Decrypt. - unsigned int decryptedLen = 0; - std::vector<uint8_t> decrypted(data_len); - rv = PK11_Decrypt(symKey, mech, ¶ms, &decrypted[0], &decryptedLen, - decrypted.size(), &output[0], outputLen); + // Get the *estimated* plaintext length. This value should + // never be zero as it could lead to a NULL outPtr being + // passed to a subsequent decryption call (for AEAD we + // must authenticate even when the pt is zero-length). + unsigned int decrypt_bytes_needed = 0; + rv = PK11_Decrypt(key.get(), kMech, ¶ms, nullptr, &decrypt_bytes_needed, + 0, encrypted.data(), encrypted_len); + EXPECT_EQ(rv, SECSuccess); + EXPECT_GT(decrypt_bytes_needed, data_len); + + // Now decrypt it + std::vector<uint8_t> decrypted(decrypt_bytes_needed); + unsigned int decrypted_len = 0; + rv = PK11_Decrypt(key.get(), kMech, ¶ms, decrypted.data(), + &decrypted_len, decrypted.size(), encrypted.data(), + encrypted.size()); EXPECT_EQ(rv, SECSuccess); // Check the plaintext. - EXPECT_TRUE(!memcmp(data, &decrypted[0], decryptedLen)); + ASSERT_EQ(data_len, decrypted_len); + EXPECT_TRUE(!memcmp(data, decrypted.data(), decrypted_len)); // Decrypt with bogus data. - { - std::vector<uint8_t> bogusCiphertext(output); - bogusCiphertext[0] ^= 0xff; - rv = PK11_Decrypt(symKey, mech, ¶ms, &decrypted[0], &decryptedLen, - decrypted.size(), &bogusCiphertext[0], outputLen); - EXPECT_NE(rv, SECSuccess); + // Skip if there's no data to modify. + if (encrypted_len > 0) { + decrypted_len = 0; + std::vector<uint8_t> bogus_ciphertext(encrypted); + bogus_ciphertext[0] ^= 0xff; + rv = PK11_Decrypt(key.get(), kMech, ¶ms, decrypted.data(), + &decrypted_len, decrypted.size(), + bogus_ciphertext.data(), encrypted_len); + EXPECT_EQ(rv, SECFailure); + EXPECT_EQ(0U, decrypted_len); } // Decrypt with bogus tag. - { - std::vector<uint8_t> bogusTag(output); - bogusTag[outputLen - 1] ^= 0xff; - rv = PK11_Decrypt(symKey, mech, ¶ms, &decrypted[0], &decryptedLen, - decrypted.size(), &bogusTag[0], outputLen); - EXPECT_NE(rv, SECSuccess); + // Skip if there's no tag to modify. + if (encrypted_len > 0) { + decrypted_len = 0; + std::vector<uint8_t> bogus_tag(encrypted); + bogus_tag[encrypted_len - 1] ^= 0xff; + rv = PK11_Decrypt(key.get(), kMech, ¶ms, decrypted.data(), + &decrypted_len, decrypted.size(), bogus_tag.data(), + encrypted_len); + EXPECT_EQ(rv, SECFailure); + EXPECT_EQ(0U, decrypted_len); } // Decrypt with bogus IV. - { - SECItem bogusParams(params); + // iv_len == 0 is invalid and should be caught earlier. + // Still skip, if there's no IV to modify. + if (iv_len != 0) { + decrypted_len = 0; + SECItem bogus_params(params); CK_NSS_AEAD_PARAMS bogusAeadParams(aead_params); - bogusParams.data = reinterpret_cast<unsigned char*>(&bogusAeadParams); + bogus_params.data = reinterpret_cast<unsigned char*>(&bogusAeadParams); std::vector<uint8_t> bogusIV(iv, iv + iv_len); - bogusAeadParams.pNonce = toUcharPtr(&bogusIV[0]); + bogusAeadParams.pNonce = toUcharPtr(bogusIV.data()); bogusIV[0] ^= 0xff; - rv = PK11_Decrypt(symKey, mech, &bogusParams, &decrypted[0], - &decryptedLen, data_len, &output[0], outputLen); - EXPECT_NE(rv, SECSuccess); + rv = PK11_Decrypt(key.get(), kMech, &bogus_params, decrypted.data(), + &decrypted_len, data_len, encrypted.data(), + encrypted.size()); + EXPECT_EQ(rv, SECFailure); + EXPECT_EQ(0U, decrypted_len); } // Decrypt with bogus additional data. - { - SECItem bogusParams(params); - CK_NSS_AEAD_PARAMS bogusAeadParams(aead_params); - bogusParams.data = reinterpret_cast<unsigned char*>(&bogusAeadParams); - - std::vector<uint8_t> bogusAAD(aad, aad + aad_len); - bogusAeadParams.pAAD = toUcharPtr(&bogusAAD[0]); - bogusAAD[0] ^= 0xff; - - rv = PK11_Decrypt(symKey, mech, &bogusParams, &decrypted[0], - &decryptedLen, data_len, &output[0], outputLen); - EXPECT_NE(rv, SECSuccess); + // Skip when AAD was empty and can't be modified. + // Alternatively we could generate random aad. + if (aad_len != 0) { + decrypted_len = 0; + SECItem bogus_params(params); + CK_NSS_AEAD_PARAMS bogus_aead_params(aead_params); + bogus_params.data = reinterpret_cast<unsigned char*>(&bogus_aead_params); + + std::vector<uint8_t> bogus_aad(aad, aad + aad_len); + bogus_aead_params.pAAD = toUcharPtr(bogus_aad.data()); + bogus_aad[0] ^= 0xff; + + rv = PK11_Decrypt(key.get(), kMech, &bogus_params, decrypted.data(), + &decrypted_len, data_len, encrypted.data(), + encrypted.size()); + EXPECT_EQ(rv, SECFailure); + EXPECT_EQ(0U, decrypted_len); } } - void EncryptDecrypt(const uint8_t* key, size_t key_len, const uint8_t* data, - size_t data_len, const uint8_t* aad, size_t aad_len, - const uint8_t* iv, size_t iv_len, const uint8_t* ct, - size_t ct_len) { + void EncryptDecrypt(const chaChaTestVector testvector) { ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); - SECItem keyItem = {siBuffer, toUcharPtr(key), - static_cast<unsigned int>(key_len)}; + SECItem keyItem = {siBuffer, toUcharPtr(testvector.Key.data()), + static_cast<unsigned int>(testvector.Key.size())}; // Import key. - ScopedPK11SymKey symKey(PK11_ImportSymKey( - slot.get(), mech, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr)); - EXPECT_TRUE(!!symKey); + ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), kMech, PK11_OriginUnwrap, + CKA_ENCRYPT, &keyItem, nullptr)); + EXPECT_TRUE(!!key); // Check. - EncryptDecrypt(symKey.get(), data, data_len, aad, aad_len, iv, iv_len, ct, - ct_len); + 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()); } protected: - CK_MECHANISM_TYPE mech = CKM_NSS_CHACHA20_POLY1305; }; -#define ENCRYPT_DECRYPT(v) \ - EncryptDecrypt(v##Key, sizeof(v##Key), v##Data, sizeof(v##Data), v##AAD, \ - sizeof(v##AAD), v##IV, sizeof(v##IV), v##CT, sizeof(v##CT)); - TEST_F(Pkcs11ChaCha20Poly1305Test, GenerateEncryptDecrypt) { // Generate a random key. ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); - ScopedPK11SymKey symKey(PK11_KeyGen(slot.get(), mech, nullptr, 32, nullptr)); - EXPECT_TRUE(!!symKey); + ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr)); + EXPECT_TRUE(!!key); // Generate random data. - std::vector<uint8_t> data(512); - SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), &data[0], data.size()); + std::vector<uint8_t> input(512); + SECStatus rv = + PK11_GenerateRandomOnSlot(slot.get(), input.data(), input.size()); EXPECT_EQ(rv, SECSuccess); // Generate random AAD. std::vector<uint8_t> aad(16); - rv = PK11_GenerateRandomOnSlot(slot.get(), &aad[0], aad.size()); + rv = PK11_GenerateRandomOnSlot(slot.get(), aad.data(), aad.size()); EXPECT_EQ(rv, SECSuccess); // Generate random IV. std::vector<uint8_t> iv(12); - rv = PK11_GenerateRandomOnSlot(slot.get(), &iv[0], iv.size()); + rv = PK11_GenerateRandomOnSlot(slot.get(), iv.data(), iv.size()); EXPECT_EQ(rv, SECSuccess); // Check. - EncryptDecrypt(symKey.get(), &data[0], data.size(), &aad[0], aad.size(), - &iv[0], iv.size()); + EncryptDecrypt(key, false, false, input.data(), input.size(), aad.data(), + aad.size(), iv.data(), iv.size()); +} + +TEST_F(Pkcs11ChaCha20Poly1305Test, Xor) { + static const uint8_t kExpected[sizeof(kData)] = { + 0xd8, 0x15, 0xd3, 0xb3, 0xe9, 0x34, 0x3b, 0x7a, + 0x24, 0xf6, 0x5f, 0xd7, 0x95, 0x3d, 0xd3, 0x51}; + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + SECItem keyItem = {siBuffer, toUcharPtr(kKeyData), + static_cast<unsigned int>(sizeof(kKeyData))}; + ScopedPK11SymKey key(PK11_ImportSymKey( + slot.get(), kMechXor, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr)); + EXPECT_TRUE(!!key); + + SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce), + static_cast<unsigned int>(sizeof(kCtrNonce))}; + 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)); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(sizeof(kExpected), static_cast<size_t>(encrypted_len)); + EXPECT_EQ(0, memcmp(kExpected, encrypted, sizeof(kExpected))); + + // Decrypting has the same effect. + rv = PK11_Decrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, + &encrypted_len, sizeof(encrypted), kData, sizeof(kData)); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len)); + EXPECT_EQ(0, memcmp(kExpected, encrypted, sizeof(kExpected))); + + // Operating in reverse too. + rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, + &encrypted_len, sizeof(encrypted), kExpected, + sizeof(kExpected)); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(sizeof(kExpected), static_cast<size_t>(encrypted_len)); + EXPECT_EQ(0, memcmp(kData, encrypted, sizeof(kData))); } -TEST_F(Pkcs11ChaCha20Poly1305Test, CheckTestVector1) { - ENCRYPT_DECRYPT(kTestVector1); +// This test just ensures that a key can be generated for use with the XOR +// function. The result is random and therefore cannot be checked. +TEST_F(Pkcs11ChaCha20Poly1305Test, GenerateXor) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr)); + EXPECT_TRUE(!!key); + + SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce), + static_cast<unsigned int>(sizeof(kCtrNonce))}; + 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)); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len)); } -TEST_F(Pkcs11ChaCha20Poly1305Test, CheckTestVector2) { - ENCRYPT_DECRYPT(kTestVector2); +TEST_F(Pkcs11ChaCha20Poly1305Test, XorInvalidParams) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr)); + EXPECT_TRUE(!!key); + + SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce), + static_cast<unsigned int>(sizeof(kCtrNonce)) - 1}; + uint8_t encrypted[sizeof(kData)]; + unsigned int encrypted_len = 88; + SECStatus rv = + PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, + &encrypted_len, sizeof(encrypted), kData, sizeof(kData)); + EXPECT_EQ(SECFailure, rv); + + ctrNonceItem.data = nullptr; + rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted, + &encrypted_len, sizeof(encrypted), kData, sizeof(kData)); + EXPECT_EQ(SECFailure, rv); + EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError()); } +TEST_P(Pkcs11ChaCha20Poly1305Test, TestVectors) { EncryptDecrypt(GetParam()); } + +INSTANTIATE_TEST_CASE_P(NSSTestVector, Pkcs11ChaCha20Poly1305Test, + ::testing::ValuesIn(kChaCha20Vectors)); + +INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11ChaCha20Poly1305Test, + ::testing::ValuesIn(kChaCha20WycheproofVectors)); + } // 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 009c44fce..647e3a706 100644 --- a/security/nss/gtests/pk11_gtest/pk11_curve25519_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_curve25519_unittest.cc @@ -5,111 +5,122 @@ #include <memory> #include "nss.h" #include "pk11pub.h" - +#include "prerror.h" #include "cpputil.h" #include "nss_scoped_ptrs.h" +#include "testvectors/curve25519-vectors.h" #include "gtest/gtest.h" namespace nss_test { -// <https://tools.ietf.org/html/rfc7748#section-6.1> -const uint8_t kPkcs8[] = { - 0x30, 0x67, 0x02, 0x01, 0x00, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, - 0xce, 0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, - 0x47, 0x0f, 0x01, 0x04, 0x4c, 0x30, 0x4a, 0x02, 0x01, 0x01, 0x04, 0x20, - 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, - 0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, - 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a, 0xa1, 0x23, 0x03, 0x21, - 0x00, 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, - 0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, - 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a}; -const uint8_t kSpki[] = { - 0x30, 0x39, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01, - 0x03, 0x21, 0x00, 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, - 0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, - 0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f}; -const uint8_t kSecret[] = {0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1, - 0x72, 0x8e, 0x3b, 0xf4, 0x80, 0x35, 0x0f, 0x25, - 0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1, 0x9e, 0x33, - 0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42}; - -// A public key that's too short (31 bytes). -const uint8_t kSpkiShort[] = { - 0x30, 0x38, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01, - 0x03, 0x20, 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, - 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, - 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f}; - -// A public key that's too long (33 bytes). -const uint8_t kSpkiLong[] = { - 0x30, 0x3a, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01, - 0x03, 0x22, 0x00, 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, - 0x5b, 0x61, 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, - 0x78, 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f, 0x34}; - -class Pkcs11Curve25519Test : public ::testing::Test { +class Pkcs11Curve25519Test + : public ::testing::TestWithParam<curve25519_testvector> { 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, bool expect_success) { - ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); ASSERT_TRUE(slot); - SECItem pkcs8Item = {siBuffer, toUcharPtr(pkcs8), - static_cast<unsigned int>(pkcs8_len)}; + SECItem pkcs8_item = {siBuffer, toUcharPtr(pkcs8), + static_cast<unsigned int>(pkcs8_len)}; SECKEYPrivateKey* key = nullptr; SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( - slot.get(), &pkcs8Item, nullptr, nullptr, false, false, KU_ALL, &key, + slot.get(), &pkcs8_item, nullptr, nullptr, false, false, KU_ALL, &key, nullptr); EXPECT_EQ(SECSuccess, rv); - ScopedSECKEYPrivateKey privKey(key); - ASSERT_TRUE(privKey); + ScopedSECKEYPrivateKey priv_key_sess(key); + ASSERT_TRUE(priv_key_sess); - SECItem spkiItem = {siBuffer, toUcharPtr(spki), - static_cast<unsigned int>(spki_len)}; + SECItem spki_item = {siBuffer, toUcharPtr(spki), + static_cast<unsigned int>(spki_len)}; - ScopedCERTSubjectPublicKeyInfo certSpki( - SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem)); - ASSERT_TRUE(certSpki); + ScopedCERTSubjectPublicKeyInfo cert_spki( + SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); + if (!expect_success && !cert_spki) { + return; + } + ASSERT_TRUE(cert_spki); - ScopedSECKEYPublicKey pubKey(SECKEY_ExtractPublicKey(certSpki.get())); - ASSERT_TRUE(pubKey); + ScopedSECKEYPublicKey pub_key_remote( + SECKEY_ExtractPublicKey(cert_spki.get())); + ASSERT_TRUE(pub_key_remote); - ScopedPK11SymKey symKey(PK11_PubDeriveWithKDF( - privKey.get(), pubKey.get(), false, nullptr, nullptr, CKM_ECDH1_DERIVE, - CKM_SHA512_HMAC, CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr)); - EXPECT_EQ(expect_success, !!symKey); + // sym_key_sess = ECDH(session_import(private_test), public_test) + ScopedPK11SymKey sym_key_sess(PK11_PubDeriveWithKDF( + priv_key_sess.get(), pub_key_remote.get(), false, nullptr, nullptr, + CKM_ECDH1_DERIVE, CKM_SHA512_HMAC, CKA_DERIVE, 0, CKD_NULL, nullptr, + nullptr)); + ASSERT_EQ(expect_success, !!sym_key_sess); if (expect_success) { - rv = PK11_ExtractKeyValue(symKey.get()); + rv = PK11_ExtractKeyValue(sym_key_sess.get()); EXPECT_EQ(SECSuccess, rv); - SECItem* keyData = PK11_GetKeyData(symKey.get()); - EXPECT_EQ(secret_len, keyData->len); - EXPECT_EQ(memcmp(keyData->data, secret, secret_len), 0); + SECItem* key_data = PK11_GetKeyData(sym_key_sess.get()); + EXPECT_EQ(secret_len, key_data->len); + EXPECT_EQ(memcmp(key_data->data, secret, secret_len), 0); + + // Perform wrapped export on the imported private, import it as + // permanent, and verify we derive the same shared secret + static const uint8_t pw[] = "pw"; + SECItem pwItem = {siBuffer, toUcharPtr(pw), sizeof(pw)}; + ScopedSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo( + slot.get(), SEC_OID_AES_256_CBC, &pwItem, priv_key_sess.get(), 1, + nullptr)); + ASSERT_NE(nullptr, epki) << "PK11_ExportEncryptedPrivKeyInfo failed: " + << PORT_ErrorToName(PORT_GetError()); + + ScopedSECKEYPublicKey pub_key_local( + SECKEY_ConvertToPublicKey(priv_key_sess.get())); + + SECKEYPrivateKey* priv_key_tok = nullptr; + rv = PK11_ImportEncryptedPrivateKeyInfoAndReturnKey( + slot.get(), epki.get(), &pwItem, nullptr, + &pub_key_local->u.ec.publicValue, PR_TRUE, PR_TRUE, ecKey, 0, + &priv_key_tok, nullptr); + ASSERT_EQ(SECSuccess, rv) << "PK11_ImportEncryptedPrivateKeyInfo failed " + << PORT_ErrorToName(PORT_GetError()); + ASSERT_TRUE(priv_key_tok); + + // sym_key_tok = ECDH(token_import(export(private_test)), + // public_test) + ScopedPK11SymKey sym_key_tok(PK11_PubDeriveWithKDF( + priv_key_tok, pub_key_remote.get(), false, nullptr, nullptr, + CKM_ECDH1_DERIVE, CKM_SHA512_HMAC, CKA_DERIVE, 0, CKD_NULL, nullptr, + nullptr)); + EXPECT_TRUE(sym_key_tok); + + if (sym_key_tok) { + rv = PK11_ExtractKeyValue(sym_key_tok.get()); + EXPECT_EQ(SECSuccess, rv); + + key_data = PK11_GetKeyData(sym_key_tok.get()); + EXPECT_EQ(secret_len, key_data->len); + EXPECT_EQ(memcmp(key_data->data, secret, secret_len), 0); + } + rv = PK11_DeleteTokenPrivateKey(priv_key_tok, true); + EXPECT_EQ(SECSuccess, rv); } - } + }; + + void Derive(const curve25519_testvector testvector) { + Derive(testvector.private_key.data(), testvector.private_key.size(), + testvector.public_key.data(), testvector.public_key.size(), + testvector.secret.data(), testvector.secret.size(), + testvector.valid); + }; }; -TEST_F(Pkcs11Curve25519Test, DeriveSharedSecret) { - Derive(kPkcs8, sizeof(kPkcs8), kSpki, sizeof(kSpki), kSecret, sizeof(kSecret), - true); -} +TEST_P(Pkcs11Curve25519Test, TestVectors) { Derive(GetParam()); } -TEST_F(Pkcs11Curve25519Test, DeriveSharedSecretShort) { - Derive(kPkcs8, sizeof(kPkcs8), kSpkiShort, sizeof(kSpkiShort), nullptr, 0, - false); -} +INSTANTIATE_TEST_CASE_P(NSSTestVector, Pkcs11Curve25519Test, + ::testing::ValuesIn(kCurve25519Vectors)); -TEST_F(Pkcs11Curve25519Test, DeriveSharedSecretLong) { - Derive(kPkcs8, sizeof(kPkcs8), kSpkiLong, sizeof(kSpkiLong), nullptr, 0, - false); -} +INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11Curve25519Test, + ::testing::ValuesIn(kCurve25519WycheproofVectors)); } // namespace nss_test 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 88c283317..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 @@ -15,6 +15,20 @@ namespace nss_test { +const std::vector<uint8_t> kValidP256Key = { + 0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, + 0xc9, 0xaf, 0xa9, 0xd8, 0x45, 0xba, 0x75, 0x16, 0x6b, 0x5c, 0x21, 0x57, + 0x67, 0xb1, 0xd6, 0x93, 0x4e, 0x50, 0xc3, 0xdb, 0x36, 0xe8, 0x9b, 0x12, + 0x7b, 0x8a, 0x62, 0x2b, 0x12, 0x0f, 0x67, 0x21, 0xa1, 0x44, 0x03, 0x42, + 0x00, 0x04, 0x60, 0xfe, 0xd4, 0xba, 0x25, 0x5a, 0x9d, 0x31, 0xc9, 0x61, + 0xeb, 0x74, 0xc6, 0x35, 0x6d, 0x68, 0xc0, 0x49, 0xb8, 0x92, 0x3b, 0x61, + 0xfa, 0x6c, 0xe6, 0x69, 0x62, 0x2e, 0x60, 0xf2, 0x9f, 0xb6, 0x79, 0x03, + 0xfe, 0x10, 0x08, 0xb8, 0xbc, 0x99, 0xa4, 0x1a, 0xe9, 0xe9, 0x56, 0x28, + 0xbc, 0x64, 0xf2, 0xf1, 0xb2, 0x0c, 0x2d, 0x7e, 0x9f, 0x51, 0x77, 0xa3, + 0xc2, 0x94, 0xd4, 0x46, 0x22, 0x99}; + const std::vector<uint8_t> kValidRSAKey = { // 512-bit RSA private key (PKCS#8) 0x30, 0x82, 0x01, 0x54, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, @@ -73,38 +87,76 @@ const std::vector<uint8_t> kInvalidZeroLengthKey = { class DERPrivateKeyImportTest : public ::testing::Test { public: - bool ParsePrivateKey(const std::vector<uint8_t>& data) { - ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); - EXPECT_TRUE(slot); - + bool ParsePrivateKey(const std::vector<uint8_t>& data, bool expect_success) { SECKEYPrivateKey* key = nullptr; + SECStatus rv = SECFailure; + std::string nick_str = + ::testing::UnitTest::GetInstance()->current_test_info()->name() + + std::to_string(rand()); SECItem item = {siBuffer, const_cast<unsigned char*>(data.data()), - (unsigned int)data.size()}; + static_cast<unsigned int>(data.size())}; + SECItem nick = {siBuffer, reinterpret_cast<unsigned char*>( + const_cast<char*>(nick_str.data())), + static_cast<unsigned int>(nick_str.length())}; + + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + EXPECT_TRUE(slot); + if (!slot) { + return false; + } - SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( - slot.get(), &item, nullptr, nullptr, false, false, KU_ALL, &key, - nullptr); + if (PK11_NeedUserInit(slot.get())) { + if (PK11_InitPin(slot.get(), nullptr, nullptr) != SECSuccess) { + EXPECT_EQ(rv, SECSuccess) << "PK11_InitPin failed"; + } + } + rv = PK11_Authenticate(slot.get(), PR_TRUE, nullptr); + EXPECT_EQ(rv, SECSuccess); + rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot.get(), &item, &nick, nullptr, true, false, KU_ALL, &key, nullptr); EXPECT_EQ(rv == SECSuccess, key != nullptr); - SECKEY_DestroyPrivateKey(key); + + if (expect_success) { + // Try to find the key via its label + ScopedSECKEYPrivateKeyList list(PK11_ListPrivKeysInSlot( + slot.get(), const_cast<char*>(nick_str.c_str()), nullptr)); + EXPECT_FALSE(!list); + } + + if (key) { + rv = PK11_DeleteTokenPrivateKey(key, true); + EXPECT_EQ(SECSuccess, rv); + + // PK11_DeleteTokenPrivateKey leaves an errorCode set when there's + // no cert. This is expected, so clear it. + if (PORT_GetError() == SSL_ERROR_NO_CERTIFICATE) { + PORT_SetError(0); + } + } return rv == SECSuccess; } }; TEST_F(DERPrivateKeyImportTest, ImportPrivateRSAKey) { - EXPECT_TRUE(ParsePrivateKey(kValidRSAKey)); - EXPECT_FALSE(PORT_GetError()); + EXPECT_TRUE(ParsePrivateKey(kValidRSAKey, true)); + EXPECT_FALSE(PORT_GetError()) << PORT_GetError(); +} + +TEST_F(DERPrivateKeyImportTest, ImportEcdsaKey) { + EXPECT_TRUE(ParsePrivateKey(kValidP256Key, true)); + EXPECT_FALSE(PORT_GetError()) << PORT_GetError(); } TEST_F(DERPrivateKeyImportTest, ImportInvalidPrivateKey) { - EXPECT_FALSE(ParsePrivateKey(kInvalidLengthKey)); - EXPECT_EQ(PORT_GetError(), SEC_ERROR_BAD_DER); + EXPECT_FALSE(ParsePrivateKey(kInvalidLengthKey, false)); + EXPECT_EQ(PORT_GetError(), SEC_ERROR_BAD_DER) << PORT_GetError(); } TEST_F(DERPrivateKeyImportTest, ImportZeroLengthPrivateKey) { - EXPECT_FALSE(ParsePrivateKey(kInvalidZeroLengthKey)); - EXPECT_EQ(PORT_GetError(), SEC_ERROR_BAD_KEY); + EXPECT_FALSE(ParsePrivateKey(kInvalidZeroLengthKey, false)); + EXPECT_EQ(PORT_GetError(), SEC_ERROR_BAD_KEY) << PORT_GetError(); } } // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_des_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_des_unittest.cc new file mode 100644 index 000000000..30f1afb8d --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_des_unittest.cc @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <memory> +#include "nss.h" +#include "pk11pub.h" + +#include "nss_scoped_ptrs.h" + +#include "gtest/gtest.h" + +namespace nss_test { + +class Pkcs11DesTest : public ::testing::Test { + protected: + SECStatus EncryptWithIV(std::vector<uint8_t>& iv, + const CK_MECHANISM_TYPE mech) { + // Generate a random key. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ScopedPK11SymKey sym_key( + PK11_KeyGen(slot.get(), mech, nullptr, 8, nullptr)); + EXPECT_TRUE(!!sym_key); + + std::vector<uint8_t> data(16); + std::vector<uint8_t> output(16); + + SECItem params = {siBuffer, iv.data(), + static_cast<unsigned int>(iv.size())}; + + // Try to encrypt. + unsigned int output_len = 0; + return PK11_Encrypt(sym_key.get(), mech, ¶ms, output.data(), + &output_len, output.size(), data.data(), data.size()); + } +}; + +TEST_F(Pkcs11DesTest, ZeroLengthIV) { + std::vector<uint8_t> iv(0); + EXPECT_EQ(SECFailure, EncryptWithIV(iv, CKM_DES_CBC)); + EXPECT_EQ(SECFailure, EncryptWithIV(iv, CKM_DES3_CBC)); +} + +TEST_F(Pkcs11DesTest, IVTooShort) { + std::vector<uint8_t> iv(7); + EXPECT_EQ(SECFailure, EncryptWithIV(iv, CKM_DES_CBC)); + EXPECT_EQ(SECFailure, EncryptWithIV(iv, CKM_DES3_CBC)); +} + +TEST_F(Pkcs11DesTest, WrongLengthIV) { + // We tolerate IVs > 8 + std::vector<uint8_t> iv(15, 0); + EXPECT_EQ(SECSuccess, EncryptWithIV(iv, CKM_DES_CBC)); + EXPECT_EQ(SECSuccess, EncryptWithIV(iv, CKM_DES3_CBC)); +} + +TEST_F(Pkcs11DesTest, AllGood) { + std::vector<uint8_t> iv(8, 0); + EXPECT_EQ(SECSuccess, EncryptWithIV(iv, CKM_DES_CBC)); + EXPECT_EQ(SECSuccess, EncryptWithIV(iv, CKM_DES3_CBC)); +} + +} // 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 e905f7835..1816e3c9c 100644 --- a/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc @@ -45,6 +45,11 @@ static const Pkcs11EcdsaTestParams kEcdsaVectors[] = { DataBuffer(kP256Spki, sizeof(kP256Spki)), DataBuffer(kP256Data, sizeof(kP256Data)), DataBuffer(kP256Signature, sizeof(kP256Signature))}}, + {SEC_OID_SHA256, + {DataBuffer(kP256Pkcs8ZeroPad, sizeof(kP256Pkcs8ZeroPad)), + DataBuffer(kP256SpkiZeroPad, sizeof(kP256SpkiZeroPad)), + DataBuffer(kP256DataZeroPad, sizeof(kP256DataZeroPad)), + DataBuffer(kP256SignatureZeroPad, sizeof(kP256SignatureZeroPad))}}, {SEC_OID_SHA384, {DataBuffer(kP384Pkcs8, sizeof(kP384Pkcs8)), DataBuffer(kP384Spki, sizeof(kP384Spki)), diff --git a/security/nss/gtests/pk11_gtest/pk11_ecdsa_vectors.h b/security/nss/gtests/pk11_gtest/pk11_ecdsa_vectors.h index 1dd2c8728..9f625dd08 100644 --- a/security/nss/gtests/pk11_gtest/pk11_ecdsa_vectors.h +++ b/security/nss/gtests/pk11_gtest/pk11_ecdsa_vectors.h @@ -130,6 +130,38 @@ const uint8_t kP521Signature[] = { 0xd8, 0xb8, 0xc3, 0x7f, 0xf0, 0x77, 0x7b, 0x1a, 0x20, 0xf8, 0xcc, 0xb1, 0xdc, 0xcc, 0x43, 0x99, 0x7f, 0x1e, 0xe0, 0xe4, 0x4d, 0xa4, 0xa6, 0x7a}; +// ECDSA P256 test case with a leading zero in the private key +const uint8_t kP256Pkcs8ZeroPad[] = { + 0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, + 0x00, 0x16, 0x40, 0x71, 0x99, 0xe3, 0x07, 0xaa, 0xdc, 0x98, 0x0b, 0x21, + 0x62, 0xce, 0x66, 0x1f, 0xe4, 0x1a, 0x86, 0x9a, 0x23, 0x33, 0xf6, 0x72, + 0xb4, 0xa3, 0xdc, 0x3b, 0x50, 0xba, 0x20, 0xce, 0xa1, 0x44, 0x03, 0x42, + 0x00, 0x04, 0x53, 0x11, 0x9a, 0x86, 0xa0, 0xc2, 0x99, 0x4f, 0xa6, 0xf8, + 0x08, 0xf8, 0x61, 0x01, 0x0e, 0x6b, 0x04, 0x9c, 0xd8, 0x15, 0x63, 0x2e, + 0xd1, 0x38, 0x00, 0x10, 0xee, 0xe4, 0xc9, 0x11, 0xff, 0x05, 0xba, 0xd6, + 0xcd, 0x94, 0xea, 0x00, 0xec, 0x85, 0x26, 0x2c, 0xbd, 0x4d, 0x85, 0xbd, + 0x20, 0xce, 0xa5, 0xb1, 0x3f, 0x4d, 0x82, 0x9b, 0x9f, 0x28, 0x2e, 0xd3, + 0x8a, 0x87, 0x1f, 0x89, 0xf8, 0x02}; +const uint8_t kP256SpkiZeroPad[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0x53, 0x11, 0x9a, 0x86, 0xa0, 0xc2, 0x99, 0x4f, 0xa6, + 0xf8, 0x08, 0xf8, 0x61, 0x01, 0x0e, 0x6b, 0x04, 0x9c, 0xd8, 0x15, 0x63, + 0x2e, 0xd1, 0x38, 0x00, 0x10, 0xee, 0xe4, 0xc9, 0x11, 0xff, 0x05, 0xba, + 0xd6, 0xcd, 0x94, 0xea, 0x00, 0xec, 0x85, 0x26, 0x2c, 0xbd, 0x4d, 0x85, + 0xbd, 0x20, 0xce, 0xa5, 0xb1, 0x3f, 0x4d, 0x82, 0x9b, 0x9f, 0x28, 0x2e, + 0xd3, 0x8a, 0x87, 0x1f, 0x89, 0xf8, 0x02}; +const uint8_t kP256DataZeroPad[] = {'s', 'a', 'm', 'p', 'l', 'e'}; +const uint8_t kP256SignatureZeroPad[] = { + 0xa6, 0xf4, 0xe4, 0xa8, 0x3f, 0x03, 0x59, 0x89, 0x60, 0x53, 0xe7, + 0xdc, 0xb5, 0xbe, 0x78, 0xaf, 0xc1, 0xca, 0xc0, 0x65, 0xba, 0xa4, + 0x3c, 0xf1, 0xe4, 0xae, 0xe3, 0xba, 0x22, 0x3d, 0xac, 0x9d, 0x6d, + 0x1b, 0x26, 0x00, 0xcf, 0x47, 0xa1, 0xe1, 0x04, 0x21, 0x8d, 0x0b, + 0xbb, 0x16, 0xfa, 0x3e, 0x59, 0x32, 0x01, 0xb0, 0x45, 0x3e, 0x27, + 0xa4, 0xc4, 0xfd, 0x31, 0xc9, 0x1a, 0x8e, 0x74, 0xd8}; + // ECDSA test vectors, SPKI and PKCS#8 edge cases. const uint8_t kP256Pkcs8NoCurveOIDOrAlgorithmParams[] = { 0x30, 0x7d, 0x02, 0x01, 0x00, 0x30, 0x09, 0x06, 0x07, 0x2a, 0x86, 0x48, diff --git a/security/nss/gtests/pk11_gtest/pk11_find_certs_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_find_certs_unittest.cc new file mode 100644 index 000000000..5958b2367 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_find_certs_unittest.cc @@ -0,0 +1,547 @@ +/* -*- 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/. */ + +#include <string.h> + +#include "nss.h" +#include "pk11pub.h" +#include "prenv.h" +#include "prerror.h" +#include "secmod.h" + +#include "gtest/gtest.h" +#include "nss_scoped_ptrs.h" +#include "util.h" + +namespace nss_test { + +// These test certificates were generated using pycert/pykey from +// mozilla-central (https://hg.mozilla.org/mozilla-central/file/ ... +// 9968319230a74eb8c1953444a0e6973c7500a9f8/security/manager/ssl/ ... +// tests/unit/pycert.py). + +// issuer:test cert +// subject:test cert +// issuerKey:secp256r1 +// subjectKey:secp256r1 +// serialNumber:1 +const std::vector<uint8_t> kTestCert1DER = { + 0x30, 0x82, 0x01, 0x1D, 0x30, 0x81, 0xC2, 0xA0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20, + 0x63, 0x65, 0x72, 0x74, 0x30, 0x22, 0x18, 0x0F, 0x32, 0x30, 0x31, 0x37, + 0x31, 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18, + 0x0F, 0x32, 0x30, 0x32, 0x30, 0x30, 0x32, 0x30, 0x35, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5A, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20, 0x63, 0x65, + 0x72, 0x74, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, + 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, + 0x07, 0x03, 0x42, 0x00, 0x04, 0x4F, 0xBF, 0xBB, 0xBB, 0x61, 0xE0, 0xF8, + 0xF9, 0xB1, 0xA6, 0x0A, 0x59, 0xAC, 0x87, 0x04, 0xE2, 0xEC, 0x05, 0x0B, + 0x42, 0x3E, 0x3C, 0xF7, 0x2E, 0x92, 0x3F, 0x2C, 0x4F, 0x79, 0x4B, 0x45, + 0x5C, 0x2A, 0x69, 0xD2, 0x33, 0x45, 0x6C, 0x36, 0xC4, 0x11, 0x9D, 0x07, + 0x06, 0xE0, 0x0E, 0xED, 0xC8, 0xD1, 0x93, 0x90, 0xD7, 0x99, 0x1B, 0x7B, + 0x2D, 0x07, 0xA3, 0x04, 0xEA, 0xA0, 0x4A, 0xA6, 0xC0, 0x30, 0x0D, 0x06, + 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, + 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x5C, 0x75, 0x51, 0x9F, 0x13, + 0x11, 0x50, 0xCD, 0x5D, 0x8A, 0xDE, 0x20, 0xA3, 0xBC, 0x06, 0x30, 0x91, + 0xFF, 0xB2, 0x73, 0x75, 0x5F, 0x31, 0x64, 0xEC, 0xFD, 0xCB, 0x42, 0x80, + 0x0A, 0x70, 0xE6, 0x02, 0x20, 0x11, 0xFA, 0xA2, 0xCA, 0x06, 0xF3, 0xBC, + 0x5F, 0x8A, 0xCA, 0x17, 0x63, 0x36, 0x87, 0xCF, 0x8D, 0x5C, 0xA0, 0x56, + 0x84, 0x44, 0x61, 0xB2, 0x33, 0x42, 0x07, 0x58, 0x9F, 0x0C, 0x9E, 0x49, + 0x83, +}; + +// issuer:test cert +// subject:test cert +// issuerKey:secp256r1 +// subjectKey:secp256r1 +// serialNumber:2 +const std::vector<uint8_t> kTestCert2DER = { + 0x30, 0x82, 0x01, 0x1E, 0x30, 0x81, 0xC2, 0xA0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20, + 0x63, 0x65, 0x72, 0x74, 0x30, 0x22, 0x18, 0x0F, 0x32, 0x30, 0x31, 0x37, + 0x31, 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18, + 0x0F, 0x32, 0x30, 0x32, 0x30, 0x30, 0x32, 0x30, 0x35, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5A, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20, 0x63, 0x65, + 0x72, 0x74, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, + 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, + 0x07, 0x03, 0x42, 0x00, 0x04, 0x4F, 0xBF, 0xBB, 0xBB, 0x61, 0xE0, 0xF8, + 0xF9, 0xB1, 0xA6, 0x0A, 0x59, 0xAC, 0x87, 0x04, 0xE2, 0xEC, 0x05, 0x0B, + 0x42, 0x3E, 0x3C, 0xF7, 0x2E, 0x92, 0x3F, 0x2C, 0x4F, 0x79, 0x4B, 0x45, + 0x5C, 0x2A, 0x69, 0xD2, 0x33, 0x45, 0x6C, 0x36, 0xC4, 0x11, 0x9D, 0x07, + 0x06, 0xE0, 0x0E, 0xED, 0xC8, 0xD1, 0x93, 0x90, 0xD7, 0x99, 0x1B, 0x7B, + 0x2D, 0x07, 0xA3, 0x04, 0xEA, 0xA0, 0x4A, 0xA6, 0xC0, 0x30, 0x0D, 0x06, + 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, + 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x5C, 0x75, 0x51, 0x9F, 0x13, + 0x11, 0x50, 0xCD, 0x5D, 0x8A, 0xDE, 0x20, 0xA3, 0xBC, 0x06, 0x30, 0x91, + 0xFF, 0xB2, 0x73, 0x75, 0x5F, 0x31, 0x64, 0xEC, 0xFD, 0xCB, 0x42, 0x80, + 0x0A, 0x70, 0xE6, 0x02, 0x21, 0x00, 0xF6, 0x5E, 0x42, 0xC7, 0x54, 0x40, + 0x81, 0xE9, 0x4C, 0x16, 0x48, 0xB1, 0x39, 0x0A, 0xA0, 0xE2, 0x8C, 0x23, + 0xAA, 0xC5, 0xBB, 0xAC, 0xEB, 0x9B, 0x15, 0x0B, 0x2F, 0xB7, 0xF5, 0x85, + 0xB2, 0x54, +}; + +const std::vector<uint8_t> kTestCertSubjectDER = { + 0x30, 0x14, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20, 0x63, 0x65, 0x72, 0x74, +}; + +// issuer:test cert +// subject:unrelated subject DN +// issuerKey:secp256r1 +// subjectKey:secp256r1 +// serialNumber:3 +const std::vector<uint8_t> kUnrelatedTestCertDER = { + 0x30, 0x82, 0x01, 0x28, 0x30, 0x81, 0xCD, 0xA0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x01, 0x03, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20, + 0x63, 0x65, 0x72, 0x74, 0x30, 0x22, 0x18, 0x0F, 0x32, 0x30, 0x31, 0x37, + 0x31, 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18, + 0x0F, 0x32, 0x30, 0x32, 0x30, 0x30, 0x32, 0x30, 0x35, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5A, 0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0C, 0x14, 0x75, 0x6E, 0x72, 0x65, 0x6C, 0x61, 0x74, + 0x65, 0x64, 0x20, 0x73, 0x75, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x20, 0x44, + 0x4E, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, + 0x03, 0x42, 0x00, 0x04, 0x4F, 0xBF, 0xBB, 0xBB, 0x61, 0xE0, 0xF8, 0xF9, + 0xB1, 0xA6, 0x0A, 0x59, 0xAC, 0x87, 0x04, 0xE2, 0xEC, 0x05, 0x0B, 0x42, + 0x3E, 0x3C, 0xF7, 0x2E, 0x92, 0x3F, 0x2C, 0x4F, 0x79, 0x4B, 0x45, 0x5C, + 0x2A, 0x69, 0xD2, 0x33, 0x45, 0x6C, 0x36, 0xC4, 0x11, 0x9D, 0x07, 0x06, + 0xE0, 0x0E, 0xED, 0xC8, 0xD1, 0x93, 0x90, 0xD7, 0x99, 0x1B, 0x7B, 0x2D, + 0x07, 0xA3, 0x04, 0xEA, 0xA0, 0x4A, 0xA6, 0xC0, 0x30, 0x0D, 0x06, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, + 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x5C, 0x75, 0x51, 0x9F, 0x13, 0x11, + 0x50, 0xCD, 0x5D, 0x8A, 0xDE, 0x20, 0xA3, 0xBC, 0x06, 0x30, 0x91, 0xFF, + 0xB2, 0x73, 0x75, 0x5F, 0x31, 0x64, 0xEC, 0xFD, 0xCB, 0x42, 0x80, 0x0A, + 0x70, 0xE6, 0x02, 0x20, 0x0F, 0x1A, 0x04, 0xC2, 0xF8, 0xBA, 0xC2, 0x94, + 0x26, 0x6E, 0xBC, 0x91, 0x7D, 0xDB, 0x75, 0x7B, 0xE8, 0xA3, 0x4F, 0x69, + 0x1B, 0xF3, 0x1F, 0x2C, 0xCE, 0x82, 0x67, 0xC9, 0x5B, 0xBB, 0xBA, 0x0A, +}; + +class PK11FindCertsTestBase : public ::testing::Test { + protected: + PK11FindCertsTestBase() + : m_slot(nullptr), test_cert_db_dir_("PK11FindCertsTestBase-") {} + + virtual void SetUp() { + std::string test_cert_db_path(test_cert_db_dir_.GetPath()); + const char* test_name = + ::testing::UnitTest::GetInstance()->current_test_info()->name(); + std::string mod_spec = "configDir='sql:"; + mod_spec.append(test_cert_db_path); + mod_spec.append("' tokenDescription='"); + mod_spec.append(test_name); + mod_spec.append("'"); + m_slot = SECMOD_OpenUserDB(mod_spec.c_str()); + ASSERT_NE(m_slot, nullptr); + } + + virtual void TearDown() { + ASSERT_EQ(SECMOD_CloseUserDB(m_slot), SECSuccess); + 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())); + ASSERT_EQ(0, unlink((test_cert_db_path + "/key4.db").c_str())); + } + + PK11SlotInfo* m_slot; + ScopedUniqueDirectory test_cert_db_dir_; +}; + +class PK11FindRawCertsBySubjectTest : public PK11FindCertsTestBase {}; + +// If we don't have any certificates, we shouldn't get any when we search for +// them. +TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsImportedNoCertsFound) { + SECItem subject_item = { + siBuffer, const_cast<unsigned char*>(kTestCertSubjectDER.data()), + (unsigned int)kTestCertSubjectDER.size()}; + CERTCertificateList* certificates = nullptr; + SECStatus rv = + PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates); + EXPECT_EQ(rv, SECSuccess); + EXPECT_EQ(certificates, nullptr); +} + +// If we have one certificate but it has an unrelated subject DN, we shouldn't +// get it when we search. +TEST_F(PK11FindRawCertsBySubjectTest, TestOneCertImportedNoCertsFound) { + char cert_nickname[] = "Unrelated Cert"; + SECItem cert_item = {siBuffer, + const_cast<unsigned char*>(kUnrelatedTestCertDER.data()), + (unsigned int)kUnrelatedTestCertDER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, + cert_nickname, false), + SECSuccess); + + SECItem subject_item = { + siBuffer, const_cast<unsigned char*>(kTestCertSubjectDER.data()), + (unsigned int)kTestCertSubjectDER.size()}; + CERTCertificateList* certificates = nullptr; + SECStatus rv = + PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates); + EXPECT_EQ(rv, SECSuccess); + EXPECT_EQ(certificates, nullptr); +} + +TEST_F(PK11FindRawCertsBySubjectTest, TestMultipleMatchingCertsFound) { + char cert1_nickname[] = "Test Cert 1"; + SECItem cert1_item = {siBuffer, + const_cast<unsigned char*>(kTestCert1DER.data()), + (unsigned int)kTestCert1DER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, + cert1_nickname, false), + SECSuccess); + char cert2_nickname[] = "Test Cert 2"; + SECItem cert2_item = {siBuffer, + const_cast<unsigned char*>(kTestCert2DER.data()), + (unsigned int)kTestCert2DER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE, + cert2_nickname, false), + SECSuccess); + char unrelated_cert_nickname[] = "Unrelated Test Cert"; + SECItem unrelated_cert_item = { + siBuffer, const_cast<unsigned char*>(kUnrelatedTestCertDER.data()), + (unsigned int)kUnrelatedTestCertDER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &unrelated_cert_item, CK_INVALID_HANDLE, + unrelated_cert_nickname, false), + SECSuccess); + + CERTCertificateList* certificates = nullptr; + SECItem subject_item = { + siBuffer, const_cast<unsigned char*>(kTestCertSubjectDER.data()), + (unsigned int)kTestCertSubjectDER.size()}; + SECStatus rv = + PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates); + EXPECT_EQ(rv, SECSuccess); + ASSERT_NE(certificates, nullptr); + ScopedCERTCertificateList scoped_certificates(certificates); + ASSERT_EQ(scoped_certificates->len, 2); + + std::vector<uint8_t> found_cert1( + scoped_certificates->certs[0].data, + scoped_certificates->certs[0].data + scoped_certificates->certs[0].len); + std::vector<uint8_t> found_cert2( + scoped_certificates->certs[1].data, + scoped_certificates->certs[1].data + scoped_certificates->certs[1].len); + EXPECT_TRUE(found_cert1 == kTestCert1DER || found_cert1 == kTestCert2DER); + EXPECT_TRUE(found_cert2 == kTestCert1DER || found_cert2 == kTestCert2DER); + EXPECT_TRUE(found_cert1 != found_cert2); +} + +// If we try to search the internal slots, we won't find the certificate we just +// imported (because it's on a different slot). +TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsOnInternalSlots) { + char cert1_nickname[] = "Test Cert 1"; + SECItem cert1_item = {siBuffer, + const_cast<unsigned char*>(kTestCert1DER.data()), + (unsigned int)kTestCert1DER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, + cert1_nickname, false), + SECSuccess); + + SECItem subject_item = { + siBuffer, const_cast<unsigned char*>(kTestCertSubjectDER.data()), + (unsigned int)kTestCertSubjectDER.size()}; + CERTCertificateList* internal_key_slot_certificates = nullptr; + 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); + + 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); +} + +// issuer:test cert +// subject:(empty - this had to be done by hand as pycert doesn't support this) +// issuerKey:secp256r1 +// subjectKey:secp256r1 +// serialNumber:4 +const std::vector<uint8_t> kEmptySubjectCertDER = { + 0x30, 0x82, 0x01, 0x09, 0x30, 0x81, 0xAE, 0xA0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x01, 0x04, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x30, 0x14, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x09, 0x74, 0x65, 0x73, 0x74, 0x20, + 0x63, 0x65, 0x72, 0x74, 0x30, 0x22, 0x18, 0x0F, 0x32, 0x30, 0x31, 0x37, + 0x31, 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5A, 0x18, + 0x0F, 0x32, 0x30, 0x32, 0x30, 0x30, 0x32, 0x30, 0x35, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5A, 0x30, 0x00, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, + 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x4F, 0xBF, 0xBB, + 0xBB, 0x61, 0xE0, 0xF8, 0xF9, 0xB1, 0xA6, 0x0A, 0x59, 0xAC, 0x87, 0x04, + 0xE2, 0xEC, 0x05, 0x0B, 0x42, 0x3E, 0x3C, 0xF7, 0x2E, 0x92, 0x3F, 0x2C, + 0x4F, 0x79, 0x4B, 0x45, 0x5C, 0x2A, 0x69, 0xD2, 0x33, 0x45, 0x6C, 0x36, + 0xC4, 0x11, 0x9D, 0x07, 0x06, 0xE0, 0x0E, 0xED, 0xC8, 0xD1, 0x93, 0x90, + 0xD7, 0x99, 0x1B, 0x7B, 0x2D, 0x07, 0xA3, 0x04, 0xEA, 0xA0, 0x4A, 0xA6, + 0xC0, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, + 0x01, 0x0B, 0x05, 0x00, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, 0x5C, + 0x75, 0x51, 0x9F, 0x13, 0x11, 0x50, 0xCD, 0x5D, 0x8A, 0xDE, 0x20, 0xA3, + 0xBC, 0x06, 0x30, 0x91, 0xFF, 0xB2, 0x73, 0x75, 0x5F, 0x31, 0x64, 0xEC, + 0xFD, 0xCB, 0x42, 0x80, 0x0A, 0x70, 0xE6, 0x02, 0x20, 0x31, 0x1B, 0x92, + 0xAA, 0xA8, 0xB7, 0x51, 0x52, 0x7B, 0x64, 0xD6, 0xF7, 0x2F, 0x0C, 0xFB, + 0xBB, 0xD5, 0xDF, 0x86, 0xA3, 0x97, 0x96, 0x60, 0x42, 0xDA, 0xD4, 0xA8, + 0x5F, 0x2F, 0xA4, 0xDE, 0x7C}; + +std::vector<uint8_t> kEmptySubjectDER = {0x30, 0x00}; + +// This certificate has the smallest possible subject. Finding it should work. +TEST_F(PK11FindRawCertsBySubjectTest, TestFindEmptySubject) { + char empty_subject_cert_nickname[] = "Empty Subject Cert"; + SECItem empty_subject_cert_item = { + siBuffer, const_cast<unsigned char*>(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); + + SECItem subject_item = {siBuffer, + const_cast<unsigned char*>(kEmptySubjectDER.data()), + (unsigned int)kEmptySubjectDER.size()}; + CERTCertificateList* certificates = nullptr; + SECStatus rv = + PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates); + EXPECT_EQ(rv, SECSuccess); + ASSERT_NE(certificates, nullptr); + ScopedCERTCertificateList scoped_certificates(certificates); + ASSERT_EQ(scoped_certificates->len, 1); + + std::vector<uint8_t> found_cert( + scoped_certificates->certs[0].data, + scoped_certificates->certs[0].data + scoped_certificates->certs[0].len); + EXPECT_EQ(found_cert, kEmptySubjectCertDER); +} + +// Searching for a zero-length subject doesn't make sense (the minimum subject +// is the SEQUENCE tag followed by a length byte of 0), but it shouldn't cause +// problems. +TEST_F(PK11FindRawCertsBySubjectTest, TestSearchForNullSubject) { + char cert1_nickname[] = "Test Cert 1"; + SECItem cert1_item = {siBuffer, + const_cast<unsigned char*>(kTestCert1DER.data()), + (unsigned int)kTestCert1DER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, + cert1_nickname, false), + SECSuccess); + + 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); +} + +class PK11GetCertsMatchingPrivateKeyTest : public PK11FindCertsTestBase {}; + +// This is the private secp256r1 key corresponding to the above test +// certificates. +const std::vector<uint8_t> kTestPrivateKeyInfoDER = { + 0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, + 0x21, 0x91, 0x40, 0x3d, 0x57, 0x10, 0xbf, 0x15, 0xa2, 0x65, 0x81, 0x8c, + 0xd4, 0x2e, 0xd6, 0xfe, 0xdf, 0x09, 0xad, 0xd9, 0x2d, 0x78, 0xb1, 0x8e, + 0x7a, 0x1e, 0x9f, 0xeb, 0x95, 0x52, 0x47, 0x02, 0xa1, 0x44, 0x03, 0x42, + 0x00, 0x04, 0x4f, 0xbf, 0xbb, 0xbb, 0x61, 0xe0, 0xf8, 0xf9, 0xb1, 0xa6, + 0x0a, 0x59, 0xac, 0x87, 0x04, 0xe2, 0xec, 0x05, 0x0b, 0x42, 0x3e, 0x3c, + 0xf7, 0x2e, 0x92, 0x3f, 0x2c, 0x4f, 0x79, 0x4b, 0x45, 0x5c, 0x2a, 0x69, + 0xd2, 0x33, 0x45, 0x6c, 0x36, 0xc4, 0x11, 0x9d, 0x07, 0x06, 0xe0, 0x0e, + 0xed, 0xc8, 0xd1, 0x93, 0x90, 0xd7, 0x99, 0x1b, 0x7b, 0x2d, 0x07, 0xa3, + 0x04, 0xea, 0xa0, 0x4a, 0xa6, 0xc0, +}; + +// issuer:test cert (different key) +// subject:test cert (different key) +// issuerKey:secp256k1 +// subjectKey:secp256k1 +// serialNumber:1 +const std::vector<uint8_t> kTestCertWithOtherKeyDER = { + 0x30, 0x82, 0x01, 0x3a, 0x30, 0x81, 0xdf, 0xa0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x24, 0x31, 0x22, 0x30, 0x20, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x19, 0x74, 0x65, 0x73, 0x74, 0x20, + 0x63, 0x65, 0x72, 0x74, 0x20, 0x28, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x30, 0x22, 0x18, 0x0f, + 0x32, 0x30, 0x31, 0x37, 0x31, 0x31, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x30, 0x30, 0x32, 0x30, + 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x24, 0x31, 0x22, + 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x19, 0x74, 0x65, 0x73, + 0x74, 0x20, 0x63, 0x65, 0x72, 0x74, 0x20, 0x28, 0x64, 0x69, 0x66, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x29, 0x30, 0x56, + 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, + 0x05, 0x2b, 0x81, 0x04, 0x00, 0x0a, 0x03, 0x42, 0x00, 0x04, 0x35, 0xee, + 0x7c, 0x72, 0x89, 0xd8, 0xfe, 0xf7, 0xa8, 0x6a, 0xfe, 0x5d, 0xa6, 0x6d, + 0x8b, 0xc2, 0xeb, 0xb6, 0xa8, 0x54, 0x3f, 0xd2, 0xfe, 0xad, 0x08, 0x9f, + 0x45, 0xce, 0x7a, 0xcd, 0x0f, 0xa6, 0x43, 0x82, 0xa9, 0x50, 0x0c, 0x41, + 0xda, 0xd7, 0x70, 0xff, 0xd4, 0xb5, 0x11, 0xbf, 0x4b, 0x49, 0x2e, 0xb1, + 0x23, 0x88, 0x00, 0xc3, 0x2c, 0x4f, 0x76, 0xc7, 0x3a, 0x3f, 0x32, 0x94, + 0xe7, 0xc5, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x47, 0x00, 0x30, 0x44, 0x02, 0x20, + 0x63, 0x59, 0x02, 0x01, 0x89, 0xd7, 0x3e, 0x5b, 0xff, 0xd1, 0x16, 0x4e, + 0xe3, 0xe2, 0x0a, 0xe0, 0x4a, 0xd8, 0x75, 0xaf, 0x77, 0x5c, 0x93, 0x60, + 0xba, 0x10, 0x1f, 0x97, 0xdd, 0x27, 0x2d, 0x24, 0x02, 0x20, 0x1e, 0xa0, + 0x7b, 0xee, 0x90, 0x9b, 0x5f, 0x2c, 0x49, 0xd6, 0x61, 0xda, 0x31, 0x14, + 0xb1, 0xa4, 0x0d, 0x2d, 0x90, 0x2b, 0x70, 0xd8, 0x6b, 0x07, 0x64, 0x27, + 0xa5, 0x2e, 0xfe, 0xca, 0x6e, 0xe6, +}; + +// If there are no certs at all, we'll get back a null list. +TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestNoCertsAtAll) { + SECItem private_key_info = { + siBuffer, const_cast<unsigned char*>(kTestPrivateKeyInfoDER.data()), + (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); + ScopedSECKEYPrivateKey scoped_priv_key(priv_key); + ScopedCERTCertList certs( + PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get())); + ASSERT_TRUE(CERT_LIST_EMPTY(certs)); +} + +// If there are no certs for the private key, we'll get back a null list. +TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestNoCertsForKey) { + SECItem private_key_info = { + siBuffer, const_cast<unsigned char*>(kTestPrivateKeyInfoDER.data()), + (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); + ScopedSECKEYPrivateKey scoped_priv_key(priv_key); + + char cert_nickname[] = "Test Cert With Other Key"; + SECItem cert_item = { + siBuffer, const_cast<unsigned char*>(kTestCertWithOtherKeyDER.data()), + (unsigned int)kTestCertWithOtherKeyDER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, + cert_nickname, false), + SECSuccess); + + ScopedCERTCertList certs( + PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get())); + ASSERT_TRUE(CERT_LIST_EMPTY(certs)); +} + +void CheckCertListForSubjects( + ScopedCERTCertList& list, + const std::vector<const char*>& expected_subjects) { + ASSERT_NE(list.get(), nullptr); + ASSERT_NE(expected_subjects.size(), 0ul); + for (const auto& expected_subject : expected_subjects) { + size_t list_length = 0; + bool found = false; + for (CERTCertListNode* n = CERT_LIST_HEAD(list); !CERT_LIST_END(n, list); + n = CERT_LIST_NEXT(n)) { + list_length++; + if (strcmp(n->cert->subjectName, expected_subject) == 0) { + ASSERT_FALSE(found); + found = true; + } + } + ASSERT_TRUE(found); + ASSERT_EQ(list_length, expected_subjects.size()); + } +} + +// We should only get back certs that actually match the private key. +TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestOneCertForKey) { + SECItem private_key_info = { + siBuffer, const_cast<unsigned char*>(kTestPrivateKeyInfoDER.data()), + (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); + ScopedSECKEYPrivateKey scoped_priv_key(priv_key); + + char cert1_nickname[] = "Test Cert 1"; + SECItem cert1_item = {siBuffer, + const_cast<unsigned char*>(kTestCert1DER.data()), + (unsigned int)kTestCert1DER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, + cert1_nickname, false), + SECSuccess); + + char cert_nickname[] = "Test Cert With Other Key"; + SECItem cert_item = { + siBuffer, const_cast<unsigned char*>(kTestCertWithOtherKeyDER.data()), + (unsigned int)kTestCertWithOtherKeyDER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, + cert_nickname, false), + SECSuccess); + + ScopedCERTCertList certs( + PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get())); + CheckCertListForSubjects(certs, {"CN=test cert"}); +} + +// We should be able to get back all certs that match the private key. +TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestTwoCertsForKey) { + SECItem private_key_info = { + siBuffer, const_cast<unsigned char*>(kTestPrivateKeyInfoDER.data()), + (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); + ScopedSECKEYPrivateKey scoped_priv_key(priv_key); + + char cert1_nickname[] = "Test Cert 1"; + SECItem cert1_item = {siBuffer, + const_cast<unsigned char*>(kTestCert1DER.data()), + (unsigned int)kTestCert1DER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE, + cert1_nickname, false), + SECSuccess); + char cert2_nickname[] = "Test Cert 2 (same key, different subject)"; + SECItem cert2_item = { + siBuffer, const_cast<unsigned char*>(kUnrelatedTestCertDER.data()), + (unsigned int)kUnrelatedTestCertDER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE, + cert2_nickname, false), + SECSuccess); + + char cert_nickname[] = "Test Cert With Other Key"; + SECItem cert_item = { + siBuffer, const_cast<unsigned char*>(kTestCertWithOtherKeyDER.data()), + (unsigned int)kTestCertWithOtherKeyDER.size()}; + ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE, + cert_nickname, false), + SECSuccess); + + ScopedCERTCertList certs( + PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get())); + CheckCertListForSubjects(certs, {"CN=test cert", "CN=unrelated subject DN"}); +} + +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_gtest.gyp b/security/nss/gtests/pk11_gtest/pk11_gtest.gyp index c73139b05..b521687fb 100644 --- a/security/nss/gtests/pk11_gtest/pk11_gtest.gyp +++ b/security/nss/gtests/pk11_gtest/pk11_gtest.gyp @@ -11,39 +11,50 @@ 'target_name': 'pk11_gtest', 'type': 'executable', 'sources': [ - 'pk11_aeskeywrap_unittest.cc', + 'pk11_aes_cmac_unittest.cc', 'pk11_aes_gcm_unittest.cc', + 'pk11_aeskeywrap_unittest.cc', + 'pk11_aeskeywrappad_unittest.cc', + 'pk11_cbc_unittest.cc', 'pk11_chacha20poly1305_unittest.cc', 'pk11_cipherop_unittest.cc', 'pk11_curve25519_unittest.cc', + 'pk11_der_private_key_import_unittest.cc', + 'pk11_des_unittest.cc', 'pk11_ecdsa_unittest.cc', 'pk11_encrypt_derive_unittest.cc', + 'pk11_find_certs_unittest.cc', + 'pk11_import_unittest.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_rsapkcs1_unittest.cc', 'pk11_rsapss_unittest.cc', - 'pk11_der_private_key_import_unittest.cc', + 'pk11_seed_cbc_unittest.cc', '<(DEPTH)/gtests/common/gtests.cc' ], 'dependencies': [ '<(DEPTH)/exports.gyp:nss_exports', - '<(DEPTH)/lib/util/util.gyp:nssutil3', '<(DEPTH)/cpputil/cpputil.gyp:cpputil', '<(DEPTH)/gtests/google_test/google_test.gyp:gtest', + '<(DEPTH)/lib/util/util.gyp:nssutil3', ], 'conditions': [ - [ 'test_build==1', { + [ 'static_libs==1', { 'dependencies': [ - '<(DEPTH)/lib/nss/nss.gyp:nss_static', - '<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static', - '<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi', - '<(DEPTH)/lib/certhigh/certhigh.gyp:certhi', - '<(DEPTH)/lib/certdb/certdb.gyp:certdb', '<(DEPTH)/lib/base/base.gyp:nssb', + '<(DEPTH)/lib/certdb/certdb.gyp:certdb', + '<(DEPTH)/lib/certhigh/certhigh.gyp:certhi', + '<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi', '<(DEPTH)/lib/dev/dev.gyp:nssdev', + '<(DEPTH)/lib/nss/nss.gyp:nss_static', + '<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static', '<(DEPTH)/lib/pki/pki.gyp:nsspki', '<(DEPTH)/lib/ssl/ssl.gyp:ssl', + '<(DEPTH)/lib/libpkix/libpkix.gyp:libpkix', ], }, { 'dependencies': [ @@ -54,6 +65,12 @@ ], } ], + 'target_defaults': { + 'defines': [ + 'DLL_PREFIX=\"<(dll_prefix)\"', + 'DLL_SUFFIX=\"<(dll_suffix)\"' + ] + }, 'variables': { 'module': 'nss' } diff --git a/security/nss/gtests/pk11_gtest/pk11_import_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_import_unittest.cc new file mode 100644 index 000000000..19ecb94a2 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_import_unittest.cc @@ -0,0 +1,281 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <memory> +#include "nss.h" +#include "pk11pub.h" +#include "pk11pqg.h" +#include "prerror.h" +#include "secoid.h" + +#include "cpputil.h" +#include "nss_scoped_ptrs.h" +#include "gtest/gtest.h" +#include "databuffer.h" +#include "pk11_keygen.h" + +namespace nss_test { + +// This deleter deletes a set of objects, unlike the deleter on +// ScopedPK11GenericObject, which only deletes one. +struct PK11GenericObjectsDeleter { + void operator()(PK11GenericObject* objs) { + if (objs) { + PK11_DestroyGenericObjects(objs); + } + } +}; + +class Pk11KeyImportTestBase : public ::testing::Test { + public: + Pk11KeyImportTestBase() = default; + virtual ~Pk11KeyImportTestBase() = default; + + void SetUp() override { + slot_.reset(PK11_GetInternalKeySlot()); + ASSERT_TRUE(slot_); + + static const uint8_t pw[] = "pw"; + SECItem pwItem = {siBuffer, toUcharPtr(pw), sizeof(pw)}; + password_.reset(SECITEM_DupItem(&pwItem)); + } + + void Test(const Pkcs11KeyPairGenerator& generator) { + // Generate a key and export it. + KeyType key_type = nullKey; + ScopedSECKEYEncryptedPrivateKeyInfo key_info; + ScopedSECItem public_value; + GenerateAndExport(generator, &key_type, &key_info, &public_value); + + // Note: NSS is currently unable export wrapped DH keys, so this doesn't + // test those beyond generate and verify. + if (key_type == dhKey) { + return; + } + ASSERT_NE(nullptr, public_value); + ASSERT_NE(nullptr, key_info); + + // Now import the encrypted key. + static const uint8_t nick[] = "nick"; + SECItem nickname = {siBuffer, toUcharPtr(nick), sizeof(nick)}; + SECKEYPrivateKey* priv_tmp; + SECStatus rv = PK11_ImportEncryptedPrivateKeyInfoAndReturnKey( + slot_.get(), key_info.get(), password_.get(), &nickname, + public_value.get(), PR_TRUE, PR_TRUE, key_type, 0, &priv_tmp, NULL); + ASSERT_EQ(SECSuccess, rv) << "PK11_ImportEncryptedPrivateKeyInfo failed " + << PORT_ErrorToName(PORT_GetError()); + ScopedSECKEYPrivateKey priv_key(priv_tmp); + ASSERT_NE(nullptr, priv_key); + + CheckForPublicKey(priv_key, public_value.get()); + } + + private: + SECItem GetPublicComponent(ScopedSECKEYPublicKey& pub_key) { + SECItem null = {siBuffer, NULL, 0}; + switch (SECKEY_GetPublicKeyType(pub_key.get())) { + case rsaKey: + case rsaPssKey: + case rsaOaepKey: + return pub_key->u.rsa.modulus; + case keaKey: + return pub_key->u.kea.publicValue; + case dsaKey: + return pub_key->u.dsa.publicValue; + case dhKey: + return pub_key->u.dh.publicValue; + case ecKey: + return pub_key->u.ec.publicValue; + case fortezzaKey: /* depricated */ + case nullKey: + /* didn't use default here so we can catch new key types at compile time + */ + break; + } + return null; + } + void CheckForPublicKey(const ScopedSECKEYPrivateKey& priv_key, + const SECItem* expected_public) { + // Verify the public key exists. + StackSECItem priv_id; + KeyType type = SECKEY_GetPrivateKeyType(priv_key.get()); + SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, priv_key.get(), + CKA_ID, &priv_id); + ASSERT_EQ(SECSuccess, rv) << "Couldn't read CKA_ID from private key: " + << PORT_ErrorToName(PORT_GetError()); + + CK_ATTRIBUTE_TYPE value_type = CKA_VALUE; + switch (type) { + case rsaKey: + value_type = CKA_MODULUS; + break; + + case dhKey: + case dsaKey: + value_type = CKA_VALUE; + break; + + case ecKey: + value_type = CKA_EC_POINT; + break; + + default: + FAIL() << "unknown key type"; + } + + // Scan public key objects until we find one with the same CKA_ID as + // priv_key + std::unique_ptr<PK11GenericObject, PK11GenericObjectsDeleter> objs( + PK11_FindGenericObjects(slot_.get(), CKO_PUBLIC_KEY)); + ASSERT_NE(nullptr, objs); + for (PK11GenericObject* obj = objs.get(); obj != nullptr; + obj = PK11_GetNextGenericObject(obj)) { + StackSECItem pub_id; + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_ID, &pub_id); + if (rv != SECSuccess) { + // Can't read CKA_ID from object. + continue; + } + if (!SECITEM_ItemsAreEqual(&priv_id, &pub_id)) { + // This isn't the object we're looking for. + continue; + } + + StackSECItem token; + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_TOKEN, &token); + ASSERT_EQ(SECSuccess, rv); + ASSERT_EQ(1U, token.len); + ASSERT_NE(0, token.data[0]); + + StackSECItem raw_value; + SECItem decoded_value; + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, value_type, &raw_value); + ASSERT_EQ(SECSuccess, rv); + SECItem value = raw_value; + + // Decode the EC_POINT and check the output against expected. + // CKA_EC_POINT isn't stable, see Bug 1520649. + ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + ASSERT_TRUE(arena); + if (value_type == CKA_EC_POINT) { + // If this fails due to the noted inconsistency, we may need to + // check the whole raw_value, or remove a leading UNCOMPRESSED_POINT tag + rv = SEC_QuickDERDecodeItem(arena.get(), &decoded_value, + SEC_ASN1_GET(SEC_OctetStringTemplate), + &raw_value); + ASSERT_EQ(SECSuccess, rv); + value = decoded_value; + } + ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &value)) + << "expected: " + << DataBuffer(expected_public->data, expected_public->len) + << std::endl + << "actual: " << DataBuffer(value.data, value.len) << std::endl; + + // Finally, convert the private to public and ensure it matches. + ScopedSECKEYPublicKey pub_key(SECKEY_ConvertToPublicKey(priv_key.get())); + ASSERT_TRUE(pub_key); + SECItem converted_public = GetPublicComponent(pub_key); + ASSERT_TRUE(converted_public.len != 0); + + ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &converted_public)) + << "expected: " + << DataBuffer(expected_public->data, expected_public->len) + << std::endl + << "actual: " + << DataBuffer(converted_public.data, converted_public.len) + << std::endl; + } + } + + void GenerateAndExport(const Pkcs11KeyPairGenerator& generator, + KeyType* key_type, + ScopedSECKEYEncryptedPrivateKeyInfo* key_info, + ScopedSECItem* public_value) { + ScopedSECKEYPrivateKey priv_key; + ScopedSECKEYPublicKey pub_key; + generator.GenerateKey(&priv_key, &pub_key); + ASSERT_TRUE(priv_key); + + // Save the public value, which we will need on import */ + SECItem* pub_val; + KeyType t = SECKEY_GetPublicKeyType(pub_key.get()); + switch (t) { + case rsaKey: + pub_val = &pub_key->u.rsa.modulus; + break; + case dhKey: + pub_val = &pub_key->u.dh.publicValue; + break; + case dsaKey: + pub_val = &pub_key->u.dsa.publicValue; + break; + case ecKey: + pub_val = &pub_key->u.ec.publicValue; + break; + default: + FAIL() << "Unknown key type"; + } + + CheckForPublicKey(priv_key, pub_val); + + *key_type = t; + // Note: NSS is currently unable export wrapped DH keys, so this doesn't + // test those beyond generate and verify. + if (t == dhKey) { + return; + } + public_value->reset(SECITEM_DupItem(pub_val)); + + // Wrap and export the key. + ScopedSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo( + slot_.get(), SEC_OID_AES_256_CBC, password_.get(), priv_key.get(), 1, + nullptr)); + ASSERT_NE(nullptr, epki) << "PK11_ExportEncryptedPrivKeyInfo failed: " + << PORT_ErrorToName(PORT_GetError()); + + key_info->swap(epki); + } + + ScopedPK11SlotInfo slot_; + ScopedSECItem password_; +}; + +class Pk11KeyImportTest + : public Pk11KeyImportTestBase, + public ::testing::WithParamInterface<CK_MECHANISM_TYPE> { + public: + Pk11KeyImportTest() = default; + virtual ~Pk11KeyImportTest() = default; +}; + +TEST_P(Pk11KeyImportTest, GenerateExportImport) { + Test(Pkcs11KeyPairGenerator(GetParam())); +} + +INSTANTIATE_TEST_CASE_P(Pk11KeyImportTest, Pk11KeyImportTest, + ::testing::Values(CKM_RSA_PKCS_KEY_PAIR_GEN, + CKM_DSA_KEY_PAIR_GEN, + CKM_DH_PKCS_KEY_PAIR_GEN)); + +class Pk11KeyImportTestEC : public Pk11KeyImportTestBase, + public ::testing::WithParamInterface<SECOidTag> { + public: + Pk11KeyImportTestEC() = default; + virtual ~Pk11KeyImportTestEC() = default; +}; + +TEST_P(Pk11KeyImportTestEC, GenerateExportImport) { + Test(Pkcs11KeyPairGenerator(CKM_EC_KEY_PAIR_GEN, GetParam())); +} + +INSTANTIATE_TEST_CASE_P(Pk11KeyImportTestEC, Pk11KeyImportTestEC, + ::testing::Values(SEC_OID_SECG_EC_SECP256R1, + SEC_OID_SECG_EC_SECP384R1, + SEC_OID_SECG_EC_SECP521R1, + SEC_OID_CURVE25519)); + +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_key_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_key_unittest.cc new file mode 100644 index 000000000..1351b53de --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_key_unittest.cc @@ -0,0 +1,80 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <memory> +#include "nss.h" +#include "pk11pub.h" +#include "pk11pqg.h" +#include "prerror.h" +#include "secoid.h" + +#include "gtest/gtest.h" +#include "nss_scoped_ptrs.h" +#include "pk11_keygen.h" + +namespace nss_test { + +class Pkcs11NullKeyTestBase : public ::testing::Test { + protected: + // This constructs a key pair, then erases the public value from the public + // key. NSS should reject this. + void Test(const Pkcs11KeyPairGenerator& generator, + CK_MECHANISM_TYPE dh_mech) { + ScopedSECKEYPrivateKey priv; + ScopedSECKEYPublicKey pub; + generator.GenerateKey(&priv, &pub); + ASSERT_TRUE(priv); + + // These don't leak because they are allocated to the arena associated with + // the public key. + SECItem* pub_val = nullptr; + switch (SECKEY_GetPublicKeyType(pub.get())) { + case rsaKey: + pub_val = &pub->u.rsa.modulus; + break; + + case dsaKey: + pub_val = &pub->u.dsa.publicValue; + break; + + case dhKey: + pub_val = &pub->u.dh.publicValue; + break; + + case ecKey: + pub_val = &pub->u.ec.publicValue; + break; + + default: + FAIL() << "Unknown key type " << SECKEY_GetPublicKeyType(pub.get()); + } + pub_val->data = nullptr; + pub_val->len = 0; + + ScopedPK11SymKey symKey(PK11_PubDeriveWithKDF( + priv.get(), pub.get(), false, nullptr, nullptr, dh_mech, + CKM_SHA512_HMAC, CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr)); + ASSERT_FALSE(symKey); + } +}; + +class Pkcs11DhNullKeyTest : public Pkcs11NullKeyTestBase {}; +TEST_F(Pkcs11DhNullKeyTest, UseNullPublicValue) { + Test(Pkcs11KeyPairGenerator(CKM_DH_PKCS_KEY_PAIR_GEN), CKM_DH_PKCS_DERIVE); +} + +class Pkcs11EcdhNullKeyTest : public Pkcs11NullKeyTestBase, + public ::testing::WithParamInterface<SECOidTag> { +}; +TEST_P(Pkcs11EcdhNullKeyTest, UseNullPublicValue) { + Test(Pkcs11KeyPairGenerator(CKM_EC_KEY_PAIR_GEN, GetParam()), + CKM_ECDH1_DERIVE); +} +INSTANTIATE_TEST_CASE_P(Pkcs11EcdhNullKeyTest, Pkcs11EcdhNullKeyTest, + ::testing::Values(SEC_OID_SECG_EC_SECP256R1, + SEC_OID_SECG_EC_SECP384R1, + SEC_OID_SECG_EC_SECP521R1, + SEC_OID_CURVE25519)); + +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_keygen.cc b/security/nss/gtests/pk11_gtest/pk11_keygen.cc new file mode 100644 index 000000000..d96cd38f6 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_keygen.cc @@ -0,0 +1,143 @@ +/* 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 "pk11_keygen.h" + +#include "pk11pub.h" +#include "pk11pqg.h" +#include "prerror.h" + +#include "gtest/gtest.h" + +namespace nss_test { + +class ParamHolder { + public: + virtual void* get() = 0; + virtual ~ParamHolder() = default; + + protected: + ParamHolder() = default; +}; + +void Pkcs11KeyPairGenerator::GenerateKey(ScopedSECKEYPrivateKey* priv_key, + ScopedSECKEYPublicKey* pub_key) const { + // This function returns if an assertion fails, so don't leak anything. + priv_key->reset(nullptr); + pub_key->reset(nullptr); + + auto params = MakeParams(); + ASSERT_NE(nullptr, params); + + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + ASSERT_TRUE(slot); + + SECKEYPublicKey* pub_tmp; + ScopedSECKEYPrivateKey priv_tmp(PK11_GenerateKeyPair( + slot.get(), mech_, params->get(), &pub_tmp, PR_FALSE, PR_TRUE, nullptr)); + ASSERT_NE(nullptr, priv_tmp) << "PK11_GenerateKeyPair failed: " + << PORT_ErrorToName(PORT_GetError()); + ASSERT_NE(nullptr, pub_tmp); + + priv_key->swap(priv_tmp); + pub_key->reset(pub_tmp); +} + +class RsaParamHolder : public ParamHolder { + public: + RsaParamHolder() : params_({1024, 0x010001}) {} + ~RsaParamHolder() = default; + + void* get() override { return ¶ms_; } + + private: + PK11RSAGenParams params_; +}; + +class PqgParamHolder : public ParamHolder { + public: + PqgParamHolder(PQGParams* params) : params_(params) {} + ~PqgParamHolder() = default; + + void* get() override { return params_.get(); } + + private: + ScopedPQGParams params_; +}; + +class DhParamHolder : public PqgParamHolder { + public: + DhParamHolder(PQGParams* params) + : PqgParamHolder(params), + params_({nullptr, params->prime, params->base}) {} + ~DhParamHolder() = default; + + void* get() override { return ¶ms_; } + + private: + SECKEYDHParams params_; +}; + +class EcParamHolder : public ParamHolder { + public: + EcParamHolder(SECOidTag curve_oid) { + SECOidData* curve = SECOID_FindOIDByTag(curve_oid); + EXPECT_NE(nullptr, curve); + + size_t plen = curve->oid.len + 2; + extra_.reset(new uint8_t[plen]); + extra_[0] = SEC_ASN1_OBJECT_ID; + extra_[1] = static_cast<uint8_t>(curve->oid.len); + memcpy(&extra_[2], curve->oid.data, curve->oid.len); + + ec_params_ = {siBuffer, extra_.get(), static_cast<unsigned int>(plen)}; + } + ~EcParamHolder() = default; + + void* get() override { return &ec_params_; } + + private: + SECKEYECParams ec_params_; + std::unique_ptr<uint8_t[]> extra_; +}; + +std::unique_ptr<ParamHolder> Pkcs11KeyPairGenerator::MakeParams() const { + switch (mech_) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + std::cerr << "Generate RSA pair" << std::endl; + return std::unique_ptr<ParamHolder>(new RsaParamHolder()); + + case CKM_DSA_KEY_PAIR_GEN: + case CKM_DH_PKCS_KEY_PAIR_GEN: { + PQGParams* pqg_params = nullptr; + PQGVerify* pqg_verify = nullptr; + const unsigned int key_size = 1024; + SECStatus rv = PK11_PQG_ParamGenV2(key_size, 0, key_size / 16, + &pqg_params, &pqg_verify); + if (rv != SECSuccess) { + ADD_FAILURE() << "PK11_PQG_ParamGenV2 failed"; + return nullptr; + } + EXPECT_NE(nullptr, pqg_verify); + EXPECT_NE(nullptr, pqg_params); + PK11_PQG_DestroyVerify(pqg_verify); + if (mech_ == CKM_DSA_KEY_PAIR_GEN) { + std::cerr << "Generate DSA pair" << std::endl; + return std::unique_ptr<ParamHolder>(new PqgParamHolder(pqg_params)); + } + std::cerr << "Generate DH pair" << std::endl; + return std::unique_ptr<ParamHolder>(new DhParamHolder(pqg_params)); + } + + case CKM_EC_KEY_PAIR_GEN: + std::cerr << "Generate EC pair on " << curve_ << std::endl; + return std::unique_ptr<ParamHolder>(new EcParamHolder(curve_)); + + default: + ADD_FAILURE() << "unknown OID " << mech_; + } + return nullptr; +} + +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_keygen.h b/security/nss/gtests/pk11_gtest/pk11_keygen.h new file mode 100644 index 000000000..05ff97210 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_keygen.h @@ -0,0 +1,34 @@ +/* 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 "nss.h" +#include "secoid.h" + +#include "nss_scoped_ptrs.h" + +namespace nss_test { + +class ParamHolder; + +class Pkcs11KeyPairGenerator { + public: + Pkcs11KeyPairGenerator(CK_MECHANISM_TYPE mech, SECOidTag curve_oid) + : mech_(mech), curve_(curve_oid) {} + Pkcs11KeyPairGenerator(CK_MECHANISM_TYPE mech) + : Pkcs11KeyPairGenerator(mech, SEC_OID_UNKNOWN) {} + + CK_MECHANISM_TYPE mechanism() const { return mech_; } + SECOidTag curve() const { return curve_; } + + void GenerateKey(ScopedSECKEYPrivateKey* priv_key, + ScopedSECKEYPublicKey* pub_key) const; + + private: + std::unique_ptr<ParamHolder> MakeParams() const; + + CK_MECHANISM_TYPE mech_; + SECOidTag curve_; +}; + +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_module_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_module_unittest.cc new file mode 100644 index 000000000..9627c823e --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_module_unittest.cc @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <memory> +#include "nss.h" +#include "pk11pub.h" +#include "prerror.h" +#include "prsystem.h" +#include "secoid.h" + +#include "nss_scoped_ptrs.h" +#include "gtest/gtest.h" +#include "databuffer.h" + +namespace nss_test { + +class Pkcs11ModuleTest : public ::testing::Test { + public: + Pkcs11ModuleTest() {} + + void SetUp() override { + ASSERT_EQ(SECSuccess, SECMOD_AddNewModule("Pkcs11ModuleTest", DLL_PREFIX + "pkcs11testmodule." DLL_SUFFIX, + 0, 0)) + << PORT_ErrorToName(PORT_GetError()); + } + + void TearDown() override { + int type; + ASSERT_EQ(SECSuccess, SECMOD_DeleteModule("Pkcs11ModuleTest", &type)); + ASSERT_EQ(SECMOD_EXTERNAL, type); + } +}; + +TEST_F(Pkcs11ModuleTest, LoadUnload) { + ScopedSECMODModule module(SECMOD_FindModule("Pkcs11ModuleTest")); + EXPECT_NE(nullptr, module); +} + +TEST_F(Pkcs11ModuleTest, ListSlots) { + ScopedPK11SlotList slots( + PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, nullptr)); + EXPECT_NE(nullptr, slots); + + PK11SlotListElement* element = PK11_GetFirstSafe(slots.get()); + EXPECT_NE(nullptr, element); + + // These tokens are always present. + const std::vector<std::string> kSlotsWithToken = { + "NSS Internal Cryptographic Services", + "NSS User Private Key and Certificate Services", + "Test PKCS11 Public Certs Slot", "Test PKCS11 Slot 二"}; + std::vector<std::string> foundSlots; + + do { + std::string name = PK11_GetSlotName(element->slot); + foundSlots.push_back(name); + std::cerr << "loaded slot: " << name << std::endl; + } while ((element = PK11_GetNextSafe(slots.get(), element, PR_FALSE)) != + nullptr); + + std::sort(foundSlots.begin(), foundSlots.end()); + EXPECT_TRUE(std::equal(kSlotsWithToken.begin(), kSlotsWithToken.end(), + foundSlots.begin())); +} + +TEST_F(Pkcs11ModuleTest, PublicCertificatesToken) { + const std::string kRegularToken = "Test PKCS11 Tokeñ 2 Label"; + const std::string kPublicCertificatesToken = "Test PKCS11 Public Certs Token"; + + ScopedPK11SlotInfo slot1(PK11_FindSlotByName(kRegularToken.c_str())); + EXPECT_NE(nullptr, slot1); + EXPECT_FALSE(PK11_IsFriendly(slot1.get())); + + ScopedPK11SlotInfo slot2( + PK11_FindSlotByName(kPublicCertificatesToken.c_str())); + EXPECT_NE(nullptr, slot2); + EXPECT_TRUE(PK11_IsFriendly(slot2.get())); +} + +} // namespace nss_test diff --git a/security/nss/gtests/pk11_gtest/pk11_pbkdf2_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_pbkdf2_unittest.cc index fc055f400..58684fc77 100644 --- a/security/nss/gtests/pk11_gtest/pk11_pbkdf2_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_pbkdf2_unittest.cc @@ -22,53 +22,102 @@ class Pkcs11Pbkdf2Test : public ::testing::Test { public: void Derive(std::vector<uint8_t>& derived, SECOidTag hash_alg) { // Shared between test vectors. - const unsigned int iterations = 4096; + const unsigned int kIterations = 4096; std::string pass("passwordPASSWORDpassword"); std::string salt("saltSALTsaltSALTsaltSALTsaltSALTsalt"); // Derivation must succeed with the right values. - EXPECT_TRUE(DeriveBytes(pass, salt, derived, hash_alg, iterations)); + EXPECT_TRUE(DeriveBytes(pass, salt, derived, hash_alg, kIterations)); // Derivation must fail when the password is bogus. - std::string bogusPass("PasswordPASSWORDpassword"); - EXPECT_FALSE(DeriveBytes(bogusPass, salt, derived, hash_alg, iterations)); + std::string bogus_pass("PasswordPASSWORDpassword"); + EXPECT_FALSE(DeriveBytes(bogus_pass, salt, derived, hash_alg, kIterations)); // Derivation must fail when the salt is bogus. - std::string bogusSalt("SaltSALTsaltSALTsaltSALTsaltSALTsalt"); - EXPECT_FALSE(DeriveBytes(pass, bogusSalt, derived, hash_alg, iterations)); + std::string bogus_salt("SaltSALTsaltSALTsaltSALTsaltSALTsalt"); + EXPECT_FALSE(DeriveBytes(pass, bogus_salt, derived, hash_alg, kIterations)); // Derivation must fail when using the wrong hash function. SECOidTag next_hash_alg = static_cast<SECOidTag>(hash_alg + 1); - EXPECT_FALSE(DeriveBytes(pass, salt, derived, next_hash_alg, iterations)); + EXPECT_FALSE(DeriveBytes(pass, salt, derived, next_hash_alg, kIterations)); - // Derivation must fail when using the wrong number of iterations. - EXPECT_FALSE(DeriveBytes(pass, salt, derived, hash_alg, iterations + 1)); + // Derivation must fail when using the wrong number of kIterations. + EXPECT_FALSE(DeriveBytes(pass, salt, derived, hash_alg, kIterations + 1)); + } + + void KeySizes(SECOidTag hash_alg) { + // These tests will only validate the controls around the key sizes. + // The resulting key is tested above, with valid key sizes. + const unsigned int kIterations = 10; + std::string pass("passwordPASSWORDpassword"); + std::string salt("saltSALTsaltSALTsaltSALTsaltSALTsalt"); + + // Derivation must fail when using key sizes bigger than MAX_KEY_LEN. + const int big_key_size = 768; + EXPECT_FALSE(KeySizeParam(pass, salt, big_key_size, hash_alg, kIterations)); + + // Zero is acceptable as key size and will be managed internally. + const int zero_key_size = 0; + EXPECT_TRUE(KeySizeParam(pass, salt, 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. + const int minus_key_size = -1; + EXPECT_FALSE( + KeySizeParam(pass, salt, minus_key_size, hash_alg, kIterations)); + + // Lower than -1 is not allowed, as -1 means no keyLen defined. + const int negative_key_size = -10; + EXPECT_FALSE( + KeySizeParam(pass, salt, negative_key_size, hash_alg, kIterations)); } private: bool DeriveBytes(std::string& pass, std::string& salt, std::vector<uint8_t>& derived, SECOidTag hash_alg, - unsigned int iterations) { - SECItem passItem = {siBuffer, ToUcharPtr(pass), - static_cast<unsigned int>(pass.length())}; - SECItem saltItem = {siBuffer, ToUcharPtr(salt), - static_cast<unsigned int>(salt.length())}; + unsigned int kIterations) { + SECItem pass_item = {siBuffer, ToUcharPtr(pass), + static_cast<unsigned int>(pass.length())}; + SECItem salt_item = {siBuffer, ToUcharPtr(salt), + static_cast<unsigned int>(salt.length())}; // Set up PBKDF2 params. ScopedSECAlgorithmID alg_id( PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, hash_alg, hash_alg, - derived.size(), iterations, &saltItem)); + derived.size(), kIterations, &salt_item)); // Derive. ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); - ScopedPK11SymKey symKey( - PK11_PBEKeyGen(slot.get(), alg_id.get(), &passItem, false, nullptr)); + ScopedPK11SymKey sym_key( + PK11_PBEKeyGen(slot.get(), alg_id.get(), &pass_item, false, nullptr)); - SECStatus rv = PK11_ExtractKeyValue(symKey.get()); + SECStatus rv = PK11_ExtractKeyValue(sym_key.get()); EXPECT_EQ(rv, SECSuccess); - SECItem* keyData = PK11_GetKeyData(symKey.get()); - return !memcmp(&derived[0], keyData->data, keyData->len); + SECItem* key_data = PK11_GetKeyData(sym_key.get()); + 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<unsigned int>(pass.length())}; + SECItem salt_item = {siBuffer, ToUcharPtr(salt), + static_cast<unsigned int>(salt.length())}; + + // Set up PBKDF2 params. + ScopedSECAlgorithmID alg_id( + PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, hash_alg, hash_alg, + key_size, kIterations, &salt_item)); + + // Try to generate a key with the defined params. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ScopedPK11SymKey sym_key( + PK11_PBEKeyGen(slot.get(), alg_id.get(), &pass_item, false, nullptr)); + + // Should be nullptr if fail. + return sym_key.get(); } }; @@ -93,4 +142,9 @@ TEST_F(Pkcs11Pbkdf2Test, DeriveKnown2) { Derive(derived, SEC_OID_HMAC_SHA256); } +TEST_F(Pkcs11Pbkdf2Test, KeyLenSizes) { + // The size controls are regardless of the algorithms. + KeySizes(SEC_OID_HMAC_SHA256); +} + } // 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 ed0573027..2024a525f 100644 --- a/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc +++ b/security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc @@ -93,6 +93,20 @@ TEST_F(Pkcs11RsaPssTest, GenerateAndSignAndVerify) { EXPECT_EQ(rv, SECFailure); } +TEST_F(Pkcs11RsaPssTest, NoLeakWithInvalidExponent) { + // Attempt to generate an RSA key with a public exponent of 1. This should + // fail, but it shouldn't leak memory. + PK11RSAGenParams rsaGenParams = {1024, 0x01}; + + // Generate RSA key pair. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + SECKEYPublicKey* pubKey = nullptr; + SECKEYPrivateKey* privKey = + PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaGenParams, + &pubKey, false, false, nullptr); + EXPECT_FALSE(privKey); + EXPECT_FALSE(pubKey); +} class Pkcs11RsaPssVectorTest : public Pkcs11RsaPssTest, public ::testing::WithParamInterface<Pkcs11SignatureTestParams> {}; diff --git a/security/nss/gtests/pk11_gtest/pk11_seed_cbc_unittest.cc b/security/nss/gtests/pk11_gtest/pk11_seed_cbc_unittest.cc new file mode 100644 index 000000000..dd90b7853 --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_seed_cbc_unittest.cc @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <memory> +#include "nss.h" +#include "pk11pub.h" +#include "secerr.h" + +#include "nss_scoped_ptrs.h" +#include "gtest/gtest.h" +#include "util.h" + +namespace nss_test { +class Pkcs11SeedCbcTest : public ::testing::Test { + protected: + enum class Action { Encrypt, Decrypt }; + + SECStatus EncryptDecryptSeed(Action action, unsigned int input_size, + unsigned int output_size) { + // Generate a random key. + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + ScopedPK11SymKey sym_key( + PK11_KeyGen(slot.get(), kMech, nullptr, 16, nullptr)); + EXPECT_TRUE(!!sym_key); + + std::vector<uint8_t> data(input_size); + std::vector<uint8_t> init_vector(16); + std::vector<uint8_t> output(output_size); + SECItem params = {siBuffer, init_vector.data(), + (unsigned int)init_vector.size()}; + + // Try to encrypt/decrypt. + 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()); + } + } + const CK_MECHANISM_TYPE kMech = CKM_SEED_CBC; +}; + +// 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)); + // 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)); +} + +TEST_F(Pkcs11SeedCbcTest, SeedCBC_InvalidArgs) { + // maxLen lower than input data. + EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Encrypt, 16, 10)); + EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Decrypt, 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)); +} + +} // namespace nss_test
\ No newline at end of file diff --git a/security/nss/gtests/pk11_gtest/pk11_signature_test.h b/security/nss/gtests/pk11_gtest/pk11_signature_test.h index 0526fea55..cd46f17d7 100644 --- a/security/nss/gtests/pk11_gtest/pk11_signature_test.h +++ b/security/nss/gtests/pk11_gtest/pk11_signature_test.h @@ -59,6 +59,9 @@ class Pk11SignatureTest : public ::testing::Test { ScopedCERTSubjectPublicKeyInfo certSpki( SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem)); + if (!certSpki) { + return nullptr; + } return ScopedSECKEYPublicKey(SECKEY_ExtractPublicKey(certSpki.get())); } |