Issue #1338 - Part 2: Update NSS to 3.48-RTM

This commit is contained in:
wolfbeast 2020-01-02 21:06:40 +01:00 • committed by Roy Tam
commit c57cac24e8
885 changed files with 1650639 additions and 59530 deletions

View file

@ -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)

View file

@ -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());
}
}

View file

@ -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

View file

@ -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;
}
}
}
}
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, 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);
}
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 */

View file

@ -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 */

View file

@ -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

View file

@ -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};
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'};
// 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 {
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);
// 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> bogusAAD(aad, aad + aad_len);
bogusAeadParams.pAAD = toUcharPtr(&bogusAAD[0]);
bogusAAD[0] ^= 0xff;
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(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);
}
}
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, CheckTestVector1) {
ENCRYPT_DECRYPT(kTestVector1);
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, CheckTestVector2) {
ENCRYPT_DECRYPT(kTestVector2);
// 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, 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

View file

@ -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

View file

@ -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())};
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot.get(), &item, nullptr, nullptr, false, false, KU_ALL, &key,
nullptr);
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
EXPECT_TRUE(slot);
if (!slot) {
return false;
}
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

View file

@ -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

View file

@ -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)),

View file

@ -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,

View file

@ -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

View file

@ -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/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/cryptohi/cryptohi.gyp:cryptohi',
'<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
'<(DEPTH)/lib/certdb/certdb.gyp:certdb',
'<(DEPTH)/lib/base/base.gyp:nssb',
'<(DEPTH)/lib/dev/dev.gyp:nssdev',
'<(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'
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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> {};

View file

@ -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

View file

@ -59,6 +59,9 @@ class Pk11SignatureTest : public ::testing::Test {
ScopedCERTSubjectPublicKeyInfo certSpki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
if (!certSpki) {
return nullptr;
}
return ScopedSECKEYPublicKey(SECKEY_ExtractPublicKey(certSpki.get()));
}