summaryrefslogtreecommitdiffstats
path: root/security/nss/gtests/pk11_gtest
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/gtests/pk11_gtest')
-rw-r--r--security/nss/gtests/pk11_gtest/manifest.mn15
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_aes_cmac_unittest.cc91
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_aes_gcm_unittest.cc128
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_aeskeywrap_unittest.cc190
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_aeskeywrappad_unittest.cc415
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_cbc_unittest.cc558
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc381
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_curve25519_unittest.cc159
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_der_private_key_import_unittest.cc82
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_des_unittest.cc65
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_ecdsa_unittest.cc5
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_ecdsa_vectors.h32
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_find_certs_unittest.cc547
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_gtest.gyp35
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_import_unittest.cc281
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_key_unittest.cc80
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_keygen.cc143
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_keygen.h34
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_module_unittest.cc84
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_pbkdf2_unittest.cc94
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_rsapss_unittest.cc14
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_seed_cbc_unittest.cc71
-rw-r--r--security/nss/gtests/pk11_gtest/pk11_signature_test.h3
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, &params, 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, &params, 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, &params, 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, &params, 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, &params, decrypted.data(),
- &decryptedLen, decrypted.size(), output.data(), outputLen);
- EXPECT_EQ(rv, SECSuccess);
- ASSERT_EQ(decryptedLen, plaintext.size());
+ rv = PK11_Decrypt(sym_key.get(), mech, &params, 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, &params, output.data(), &outputLen,
- output.size(), data.data(), data.size());
+ unsigned int output_len = 0;
+ return PK11_Encrypt(sym_key.get(), mech, &params, 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, &param, 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, &param, 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, &param, 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, &param, 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, &param, encrypted, &encrypted_len,
+ sizeof(encrypted), kInput, input_len);
+ EXPECT_EQ(SECFailure, rv);
+
+ param.len++;
+ rv = PK11_Encrypt(key.get(), CKM_AES_CBC, &param, 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, &param, encrypted, &encrypted_len,
+ sizeof(encrypted), kInput, input_len);
+ EXPECT_EQ(SECFailure, rv);
+
+ param.len++;
+ rv = PK11_Encrypt(key.get(), CKM_AES_CTS, &param, 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, &params, 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, &params, &output[0], &outputLen,
- output.size(), data, data_len);
+ rv = PK11_Encrypt(key.get(), kMech, &params, 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, &params, &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, &params, 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, &params, 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, &params, &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, &params, 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, &params, &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, &params, 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, &params, 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 &params_; }
+
+ 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 &params_; }
+
+ 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, &params, output.data(),
+ &output_len, output_size, data.data(), data.size());
+ } else {
+ return PK11_Decrypt(sym_key.get(), kMech, &params, 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()));
}