Replace NSS with Pale Moon's

This commit is contained in:
wuggy 2026-06-29 21:29:25 +01:00
commit 8c2e376f94
2870 changed files with 1762232 additions and 1374220 deletions

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,230 @@
/* -*- 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 "json_reader.h"
#include "pk11pub.h"
JsonReader::JsonReader(const std::string& n) : buf_(), available_(0), i_(0) {
f_.reset(PR_Open(n.c_str(), PR_RDONLY, 00600));
EXPECT_TRUE(f_) << "error opening vectors from: " << n;
buf_[0] = 0;
}
uint64_t JsonReader::ReadInt() {
SkipWhitespace();
uint8_t c = peek();
uint64_t v = 0;
while (c >= '0' && c <= '9') {
v = v * 10 + c - '0';
next();
c = peek();
}
return v;
}
// No input checking, no unicode, no escaping (not even \"), just read ASCII.
std::string JsonReader::ReadString() {
SkipWhitespace();
if (peek() != '"') {
return "";
}
next();
std::string s;
uint8_t c = take();
while (c != '"') {
s.push_back(c);
c = take();
}
return s;
}
std::string JsonReader::ReadLabel() {
std::string s = ReadString();
SkipWhitespace();
EXPECT_EQ(take(), ':');
return s;
}
std::vector<uint8_t> JsonReader::ReadHex() {
SkipWhitespace();
uint8_t c = take();
EXPECT_EQ(c, '"');
std::vector<uint8_t> v;
c = take();
while (c != '"') {
v.push_back(JsonReader::Hex(c) << 4 | JsonReader::Hex(take()));
c = take();
}
return v;
}
SECOidTag JsonReader::ReadHash() {
std::string s = ReadString();
if (s == "SHA-1") {
return SEC_OID_SHA1;
}
if (s == "SHA-224") {
return SEC_OID_SHA224;
}
if (s == "SHA-256") {
return SEC_OID_SHA256;
}
if (s == "SHA-384") {
return SEC_OID_SHA384;
}
if (s == "SHA-512") {
return SEC_OID_SHA512;
}
ADD_FAILURE() << "unsupported hash";
return SEC_OID_UNKNOWN;
}
bool JsonReader::NextItem(uint8_t h, uint8_t t) {
SkipWhitespace();
switch (uint8_t c = take()) {
case ',':
return true;
case '{':
case '[':
EXPECT_EQ(c, h);
SkipWhitespace();
if (peek() == t) {
next();
return false;
}
return true;
case '}':
case ']':
EXPECT_EQ(c, t);
return false;
default:
ADD_FAILURE() << "Unexpected '" << c << "'";
}
return false;
}
void JsonReader::SkipValue() {
SkipWhitespace();
uint8_t c = take();
if (c == '"') {
do {
c = take();
} while (c != '"');
} else if (c >= '0' && c <= '9') {
c = peek();
while (c >= '0' && c <= '9') {
next();
c = peek();
}
} else if (c == '[') {
do {
SkipWhitespace();
if (peek() != ']') {
SkipValue();
}
} while (NextItemArray());
} else if (c == '{') {
do {
SkipWhitespace();
if (peek() == '}') {
continue;
}
std::string n = ReadLabel();
if (n == "") {
break;
}
SkipValue();
} while (NextItem());
} else {
ADD_FAILURE() << "No idea how to skip '" << c << "'";
}
}
void JsonReader::TopUp() {
if (available_ > i_) {
return;
}
i_ = 0;
if (!f_) {
return;
}
PRInt32 res = PR_Read(f_.get(), buf_, sizeof(buf_));
if (res > 0) {
available_ = static_cast<size_t>(res);
} else {
available_ = 1;
f_.reset(nullptr);
buf_[0] = 0;
}
}
void JsonReader::SkipWhitespace() {
uint8_t c = peek();
while (c && (c == ' ' || c == '\t' || c == '\r' || c == '\n')) {
next();
c = peek();
}
}
// This only handles lowercase.
uint8_t JsonReader::Hex(uint8_t c) {
if (c >= '0' && c <= '9') {
return c - '0';
}
EXPECT_TRUE(c >= 'a' && c <= 'f');
return c - 'a' + 10;
}
extern std::string g_source_dir;
void WycheproofHeader(const std::string& name, const std::string& algorithm,
const std::string& schema,
std::function<void(JsonReader& r)> group_handler) {
std::string basename = name + "_test.json";
std::string dir = ::g_source_dir + "/../common/wycheproof/source_vectors/";
std::cout << "Reading tests from: " << basename << std::endl;
JsonReader r(dir + basename);
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "algorithm") {
ASSERT_EQ(algorithm, r.ReadString());
} else if (n == "generatorVersion" || n == "numberOfTests") {
r.SkipValue();
} else if (n == "header") {
while (r.NextItemArray()) {
std::cout << " " << r.ReadString() << std::endl;
}
} else if (n == "notes") {
while (r.NextItem()) {
std::string note = r.ReadLabel();
if (note == "") {
break;
}
std::cout << " " << note << ": " << r.ReadString() << std::endl;
}
} else if (n == "schema") {
ASSERT_EQ(schema, r.ReadString());
} else if (n == "testGroups") {
while (r.NextItemArray()) {
group_handler(r);
}
} else {
FAIL() << "unknown value in header";
}
}
}

View file

@ -0,0 +1,138 @@
/* -*- 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/. */
#ifndef PK11GTEST_JSON_H_
#define PK11GTEST_JSON_H_
#include <functional>
#include <iostream>
#include <vector>
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "secoidt.h"
// If we make a few assumptions about the file, parsing JSON can be easy.
// This is not a full parser, it only works on a narrow set of inputs.
class JsonReader {
public:
JsonReader(const std::string& n);
void next() { i_++; }
uint8_t peek() {
TopUp();
return buf_[i_];
}
uint8_t take() {
uint8_t v = peek();
next();
return v;
}
// No input checking, overflow protection, or any safety.
// Returns 0 if there isn't a number here rather than aborting.
uint64_t ReadInt();
// No input checking, no unicode, no escaping (not even \"), just read ASCII.
std::string ReadString();
std::string ReadLabel();
std::vector<uint8_t> ReadHex();
SECOidTag ReadHash();
bool NextItem(uint8_t h = '{', uint8_t t = '}');
bool NextItemArray() { return NextItem('[', ']'); }
void SkipValue();
private:
void TopUp();
void SkipWhitespace();
// This only handles lowercase.
uint8_t Hex(uint8_t c);
ScopedPRFileDesc f_;
uint8_t buf_[4096];
size_t available_;
size_t i_;
};
// The way this is expected to work is that this reads the header, then
// passes off the content of each "testGroups" member to `group_handler`.
// That function processes any attributes in that structure, calls
// `WycheproofReadTests` to load individual cases and runs those tests.
void WycheproofHeader(const std::string& name, const std::string& algorithm,
const std::string& schema,
std::function<void(JsonReader& r)> group_handler);
template <typename T>
struct id {
typedef T type;
};
template <typename T>
using nondeduced = typename id<T>::type;
// Read into a block of test cases, handling standard attributes on Wycheproof
// tests.
//
// `T` needs `uint64_t id` and `bool valid` fields.
// `attr_reader` is responsible for reading values into the test case struct.
// `acceptable` determines whether a test marked "acceptable" is valid by
// default. `process_flags` allows for processing the flags on an entry.
//
// Note that this gathers all tests into a vector rather than running tests as
// they arrive. This is necessary because the testGroup JSON struct might have
// fields that haven't been read when this list is constructed (it doesn't in
// the current files, but this is not guaranteed). Tests can only run after all
// of the group attributes have been read and processed.
template <typename T>
void WycheproofReadTests(
JsonReader& r, std::vector<T>* tests,
const std::function<nondeduced<void(T&, const std::string&, JsonReader&)>>&
attr_reader,
bool acceptable = true,
const std::function<nondeduced<void(T&, const std::string&,
const std::vector<std::string>&)>>&
process_flags = nullptr) {
while (r.NextItemArray()) {
T tc;
std::string comment;
std::string result;
std::vector<std::string> flags;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "tcId") {
tc.id = r.ReadInt();
} else if (n == "result") {
result = r.ReadString();
} else if (n == "comment") {
comment = r.ReadString();
} else if (n == "flags") {
while (r.NextItemArray()) {
flags.push_back(r.ReadString());
}
} else {
ASSERT_NO_FATAL_FAILURE(attr_reader(tc, n, r));
}
}
tc.valid = (result == "valid") || (acceptable && result == "acceptable");
if (process_flags) {
process_flags(tc, result, flags);
}
std::cout << " tc " << tc.id << ": " << comment << " [" << result;
for (auto& f : flags) {
std::cout << ", " << f;
}
std::cout << "] expect " << (tc.valid ? "success" : "failure") << std::endl;
tests->push_back(tc);
}
}
#endif // PK11GTEST_JSON_H_

View file

@ -7,27 +7,39 @@ DEPTH = ../..
MODULE = nss
CPPSRCS = \
json_reader.cc \
pk11_aes_gcm_unittest.cc \
pk11_aeskeywrap_unittest.cc \
pk11_aeskeywrapkwp_unittest.cc \
pk11_aeskeywrappad_unittest.cc \
pk11_cbc_unittest.cc \
pk11_chacha20poly1305_unittest.cc \
pk11_curve25519_unittest.cc \
pk11_der_private_key_import_unittest.cc \
pk11_des_unittest.cc \
pk11_dsa_unittest.cc \
pk11_ecdsa_unittest.cc \
pk11_ecdh_unittest.cc \
pk11_encrypt_derive_unittest.cc \
pk11_export_unittest.cc \
pk11_find_certs_unittest.cc \
pk11_hkdf_unittest.cc \
pk11_hmac_unittest.cc \
pk11_hpke_unittest.cc \
pk11_ike_unittest.cc \
pk11_import_unittest.cc \
pk11_kbkdf.cc \
pk11_keygen.cc \
pk11_key_unittest.cc \
pk11_module_unittest.cc \
pk11_pbkdf2_unittest.cc \
pk11_prf_unittest.cc \
pk11_prng_unittest.cc \
pk11_rsaencrypt_unittest.cc \
pk11_rsaoaep_unittest.cc \
pk11_rsapkcs1_unittest.cc \
pk11_rsapss_unittest.cc \
pk11_signature_test.cc \
pk11_seed_cbc_unittest.cc \
$(NULL)

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -13,11 +14,12 @@
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "testvectors/cmac-vectors.h"
#include "util.h"
namespace nss_test {
class Pkcs11AesCmacTest : public ::testing::Test {
class Pkcs11AesCmacTest : public ::testing::TestWithParam<AesCmacTestVector> {
protected:
ScopedPK11SymKey ImportKey(CK_MECHANISM_TYPE mech, SECItem *key_item) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
@ -53,8 +55,46 @@ class Pkcs11AesCmacTest : public ::testing::Test {
ASSERT_EQ(SECSuccess, ret);
ASSERT_EQ(0, SECITEM_CompareItem(&output_item, &expected_item));
}
void RunTestVector(const AesCmacTestVector vec) {
bool valid = !vec.invalid;
std::string err = "Test #" + std::to_string(vec.id) + " failed";
std::vector<uint8_t> key = hex_string_to_bytes(vec.key);
std::vector<uint8_t> tag = hex_string_to_bytes(vec.tag);
std::vector<uint8_t> msg = hex_string_to_bytes(vec.msg);
std::vector<uint8_t> output(AES_BLOCK_SIZE);
// Don't provide a null pointer, even if the input is empty.
uint8_t tmp;
SECItem key_item = {siBuffer, key.data() ? key.data() : &tmp,
static_cast<unsigned int>(key.size())};
SECItem tag_item = {siBuffer, tag.data() ? tag.data() : &tmp,
static_cast<unsigned int>(tag.size())};
SECItem msg_item = {siBuffer, msg.data() ? msg.data() : &tmp,
static_cast<unsigned int>(msg.size())};
SECItem out_item = {siBuffer, output.data() ? output.data() : &tmp,
static_cast<unsigned int>(output.size())};
ScopedPK11SymKey p11_key = ImportKey(CKM_AES_CMAC_GENERAL, &key_item);
if (vec.comment == "invalid key size") {
ASSERT_EQ(nullptr, p11_key.get()) << err;
return;
}
ASSERT_NE(nullptr, p11_key.get()) << err;
SECStatus rv = PK11_SignWithSymKey(p11_key.get(), CKM_AES_CMAC, NULL,
&out_item, &msg_item);
EXPECT_EQ(SECSuccess, rv) << err;
EXPECT_EQ(valid, 0 == SECITEM_CompareItem(&out_item, &tag_item)) << err;
}
};
TEST_P(Pkcs11AesCmacTest, TestVectors) { RunTestVector(GetParam()); }
INSTANTIATE_TEST_SUITE_P(WycheproofTestVector, Pkcs11AesCmacTest,
::testing::ValuesIn(kCmacWycheproofVectors));
// Sanity check of the PKCS #11 API only. Extensive tests for correctness of
// underling CMAC implementation conducted in the following file:
// gtests/freebl_gtest/cmac_unittests.cc
@ -87,4 +127,4 @@ TEST_F(Pkcs11AesCmacTest, InvalidKeySize) {
ScopedPK11SymKey result = ImportKey(CKM_AES_CMAC, &key_item);
ASSERT_EQ(nullptr, result.get());
}
}
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -6,6 +7,7 @@
#include <memory>
#include "nss.h"
#include "pk11pub.h"
#include "pk11priv.h"
#include "secerr.h"
#include "sechash.h"
@ -17,26 +19,24 @@
namespace nss_test {
class Pkcs11AesGcmTest : public ::testing::TestWithParam<gcm_kat_value> {
class Pkcs11AesGcmTest : public ::testing::TestWithParam<AesGcmKatValue> {
protected:
void RunTest(const gcm_kat_value val) {
std::vector<uint8_t> key = hex_string_to_bytes(val.key);
std::vector<uint8_t> iv = hex_string_to_bytes(val.iv);
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();
void RunTest(const AesGcmKatValue vec) {
std::vector<uint8_t> key = hex_string_to_bytes(vec.key);
std::vector<uint8_t> iv = hex_string_to_bytes(vec.iv);
std::vector<uint8_t> plaintext = hex_string_to_bytes(vec.plaintext);
std::vector<uint8_t> aad = hex_string_to_bytes(vec.additional_data);
std::vector<uint8_t> result = hex_string_to_bytes(vec.result);
bool invalid_ct = vec.invalid_ct;
bool invalid_iv = vec.invalid_iv;
std::string msg = "Test #" + std::to_string(vec.id) + " failed";
// Ignore GHASH-only vectors.
if (key.empty()) {
return;
}
// Prepare AEAD params.
CK_GCM_PARAMS gcm_params;
CK_NSS_GCM_PARAMS gcm_params;
gcm_params.pIv = iv.data();
gcm_params.ulIvLen = iv.size();
gcm_params.pAAD = aad.data();
@ -125,7 +125,7 @@ class Pkcs11AesGcmTest : public ::testing::TestWithParam<gcm_kat_value> {
std::vector<uint8_t> aad(0);
// Prepare AEAD params.
CK_GCM_PARAMS gcm_params;
CK_NSS_GCM_PARAMS gcm_params;
gcm_params.pIv = iv.data();
gcm_params.ulIvLen = iv.size();
gcm_params.pAAD = aad.data();
@ -141,16 +141,221 @@ class Pkcs11AesGcmTest : public ::testing::TestWithParam<gcm_kat_value> {
&output_len, output.size(), data.data(), data.size());
}
SECStatus MessageInterfaceTest(int iterations, int ivFixedBits,
CK_GENERATOR_FUNCTION ivGen,
PRBool separateTag) {
// Generate a random key.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
EXPECT_NE(nullptr, slot);
ScopedPK11SymKey sym_key(
PK11_KeyGen(slot.get(), mech, nullptr, 16, nullptr));
EXPECT_NE(nullptr, sym_key);
const int kTagSize = 16;
int cipher_simulated_size;
int output_len_message = 0;
int output_len_simulated = 0;
unsigned int output_len_v24 = 0;
std::vector<uint8_t> plainIn(17);
std::vector<uint8_t> plainOut_message(17);
std::vector<uint8_t> plainOut_simulated(17);
std::vector<uint8_t> plainOut_v24(17);
std::vector<uint8_t> iv(16);
std::vector<uint8_t> iv_init(16);
std::vector<uint8_t> iv_simulated(16);
std::vector<uint8_t> cipher_message(33);
std::vector<uint8_t> cipher_simulated(33);
std::vector<uint8_t> cipher_v24(33);
std::vector<uint8_t> aad(16);
std::vector<uint8_t> tag_message(16);
std::vector<uint8_t> tag_simulated(16);
// Prepare AEAD v2.40 params.
CK_GCM_PARAMS_V3 gcm_params;
gcm_params.pIv = iv.data();
gcm_params.ulIvLen = iv.size();
gcm_params.ulIvBits = iv.size() * 8;
gcm_params.pAAD = aad.data();
gcm_params.ulAADLen = aad.size();
gcm_params.ulTagBits = kTagSize * 8;
// Prepare AEAD MESSAGE params.
CK_GCM_MESSAGE_PARAMS gcm_message_params;
gcm_message_params.pIv = iv.data();
gcm_message_params.ulIvLen = iv.size();
gcm_message_params.ulTagBits = kTagSize * 8;
gcm_message_params.ulIvFixedBits = ivFixedBits;
gcm_message_params.ivGenerator = ivGen;
if (separateTag) {
gcm_message_params.pTag = tag_message.data();
} else {
gcm_message_params.pTag = cipher_message.data() + plainIn.size();
}
// Prepare AEAD MESSAGE params for simulated case
CK_GCM_MESSAGE_PARAMS gcm_simulated_params;
gcm_simulated_params = gcm_message_params;
if (separateTag) {
// The simulated case, we have to allocate temp bufs for separate
// tags, make sure that works in both the encrypt and the decrypt
// cases.
gcm_simulated_params.pTag = tag_simulated.data();
cipher_simulated_size = cipher_simulated.size() - kTagSize;
} else {
gcm_simulated_params.pTag = cipher_simulated.data() + plainIn.size();
cipher_simulated_size = cipher_simulated.size();
}
/* when we are using CKG_GENERATE_RANDOM, don't independently generate
* the IV in the simulated case. Since the IV's would be random, none of
* the generated results would be the same. Just use the IV we generated
* in message interface */
if (ivGen == CKG_GENERATE_RANDOM) {
gcm_simulated_params.ivGenerator = CKG_NO_GENERATE;
} else {
gcm_simulated_params.pIv = iv_simulated.data();
}
SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&gcm_params),
sizeof(gcm_params)};
SECItem empty = {siBuffer, NULL, 0};
// initialize our plain text, IV and aad.
EXPECT_EQ(PK11_GenerateRandom(plainIn.data(), plainIn.size()), SECSuccess);
EXPECT_EQ(PK11_GenerateRandom(aad.data(), aad.size()), SECSuccess);
EXPECT_EQ(PK11_GenerateRandom(iv_init.data(), iv_init.size()), SECSuccess);
iv_simulated = iv_init; // vector assignment actually copies data
iv = iv_init;
// Initialize message encrypt context
ScopedPK11Context encrypt_message_context(PK11_CreateContextBySymKey(
mech, CKA_NSS_MESSAGE | CKA_ENCRYPT, sym_key.get(), &empty));
EXPECT_NE(nullptr, encrypt_message_context);
if (!encrypt_message_context) {
return SECFailure;
}
EXPECT_FALSE(_PK11_ContextGetAEADSimulation(encrypt_message_context.get()));
// Initialize simulated encrypt context
ScopedPK11Context encrypt_simulated_context(PK11_CreateContextBySymKey(
mech, CKA_NSS_MESSAGE | CKA_ENCRYPT, sym_key.get(), &empty));
EXPECT_NE(nullptr, encrypt_simulated_context);
if (!encrypt_simulated_context) {
return SECFailure;
}
EXPECT_EQ(SECSuccess,
_PK11_ContextSetAEADSimulation(encrypt_simulated_context.get()));
// Initialize message decrypt context
ScopedPK11Context decrypt_message_context(PK11_CreateContextBySymKey(
mech, CKA_NSS_MESSAGE | CKA_DECRYPT, sym_key.get(), &empty));
EXPECT_NE(nullptr, decrypt_message_context);
if (!decrypt_message_context) {
return SECFailure;
}
EXPECT_FALSE(_PK11_ContextGetAEADSimulation(decrypt_message_context.get()));
// Initialize simulated decrypt context
ScopedPK11Context decrypt_simulated_context(PK11_CreateContextBySymKey(
mech, CKA_NSS_MESSAGE | CKA_DECRYPT, sym_key.get(), &empty));
EXPECT_NE(nullptr, decrypt_simulated_context);
if (!decrypt_simulated_context) {
return SECFailure;
}
EXPECT_EQ(SECSuccess,
_PK11_ContextSetAEADSimulation(decrypt_simulated_context.get()));
// Now walk down our iterations. Each method of calculating the operation
// should agree at each step.
for (int i = 0; i < iterations; i++) {
SECStatus rv;
/* recopy the initial vector each time */
iv_simulated = iv_init;
iv = iv_init;
// First encrypt. We don't test the error code here, because
// we may be testing error conditions with this function (namely
// do we fail if we try to generate to many Random IV's).
rv =
PK11_AEADRawOp(encrypt_message_context.get(), &gcm_message_params,
sizeof(gcm_message_params), aad.data(), aad.size(),
cipher_message.data(), &output_len_message,
cipher_message.size(), plainIn.data(), plainIn.size());
if (rv != SECSuccess) {
return rv;
}
rv =
PK11_AEADRawOp(encrypt_simulated_context.get(), &gcm_simulated_params,
sizeof(gcm_simulated_params), aad.data(), aad.size(),
cipher_simulated.data(), &output_len_simulated,
cipher_simulated_size, plainIn.data(), plainIn.size());
if (rv != SECSuccess) {
return rv;
}
// make sure simulated and message is the same
EXPECT_EQ(output_len_message, output_len_simulated);
EXPECT_EQ(0, memcmp(cipher_message.data(), cipher_simulated.data(),
output_len_message));
EXPECT_EQ(0, memcmp(gcm_message_params.pTag, gcm_simulated_params.pTag,
kTagSize));
EXPECT_EQ(0, memcmp(iv.data(), gcm_simulated_params.pIv, iv.size()));
// make sure v2.40 is the same. it inherits the generated iv from
// encrypt_message_context.
EXPECT_EQ(SECSuccess,
PK11_Encrypt(sym_key.get(), mech, &params, cipher_v24.data(),
&output_len_v24, cipher_v24.size(), plainIn.data(),
plainIn.size()));
EXPECT_EQ(output_len_message, (int)output_len_v24 - kTagSize);
EXPECT_EQ(0, memcmp(cipher_message.data(), cipher_v24.data(),
output_len_message));
EXPECT_EQ(0, memcmp(gcm_message_params.pTag,
cipher_v24.data() + output_len_message, kTagSize));
// now make sure we can decrypt
EXPECT_EQ(SECSuccess,
PK11_AEADRawOp(decrypt_message_context.get(),
&gcm_message_params, sizeof(gcm_message_params),
aad.data(), aad.size(), plainOut_message.data(),
&output_len_message, plainOut_message.size(),
cipher_message.data(), output_len_message));
EXPECT_EQ(output_len_message, (int)plainIn.size());
EXPECT_EQ(
0, memcmp(plainOut_message.data(), plainIn.data(), plainIn.size()));
EXPECT_EQ(
SECSuccess,
PK11_AEADRawOp(decrypt_simulated_context.get(), &gcm_simulated_params,
sizeof(gcm_simulated_params), aad.data(), aad.size(),
plainOut_simulated.data(), &output_len_simulated,
plainOut_simulated.size(), cipher_message.data(),
output_len_simulated));
EXPECT_EQ(output_len_simulated, (int)plainIn.size());
EXPECT_EQ(
0, memcmp(plainOut_simulated.data(), plainIn.data(), plainIn.size()));
if (separateTag) {
// in the separateTag case, we need to copy the tag back to the
// end of the cipher_message.data() before using the v2.4 interface
memcpy(cipher_message.data() + output_len_message,
gcm_message_params.pTag, kTagSize);
}
EXPECT_EQ(SECSuccess,
PK11_Decrypt(sym_key.get(), mech, &params, plainOut_v24.data(),
&output_len_v24, plainOut_v24.size(),
cipher_message.data(), output_len_v24));
EXPECT_EQ(output_len_v24, plainIn.size());
EXPECT_EQ(0, memcmp(plainOut_v24.data(), plainIn.data(), plainIn.size()));
}
return SECSuccess;
}
const CK_MECHANISM_TYPE mech = CKM_AES_GCM;
};
TEST_P(Pkcs11AesGcmTest, TestVectors) { RunTest(GetParam()); }
INSTANTIATE_TEST_CASE_P(NISTTestVector, Pkcs11AesGcmTest,
::testing::ValuesIn(kGcmKatValues));
INSTANTIATE_TEST_SUITE_P(NISTTestVector, Pkcs11AesGcmTest,
::testing::ValuesIn(kGcmKatValues));
INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11AesGcmTest,
::testing::ValuesIn(kGcmWycheproofVectors));
INSTANTIATE_TEST_SUITE_P(WycheproofTestVector, Pkcs11AesGcmTest,
::testing::ValuesIn(kGcmWycheproofVectors));
TEST_F(Pkcs11AesGcmTest, ZeroLengthIV) {
std::vector<uint8_t> iv(0);
@ -167,4 +372,57 @@ TEST_F(Pkcs11AesGcmTest, TwelveByteZeroIV) {
EXPECT_EQ(SECSuccess, EncryptWithIV(iv));
}
// basic message interface it's the most common configuration
TEST_F(Pkcs11AesGcmTest, MessageInterfaceBasic) {
EXPECT_EQ(SECSuccess,
MessageInterfaceTest(16, 0, CKG_GENERATE_COUNTER, PR_FALSE));
}
// basic interface, but return the tags in a separate buffer. This triggers
// different behaviour in the simulated case, which has to buffer the
// intermediate values in a separate buffer.
TEST_F(Pkcs11AesGcmTest, MessageInterfaceSeparateTags) {
EXPECT_EQ(SECSuccess,
MessageInterfaceTest(16, 0, CKG_GENERATE_COUNTER, PR_TRUE));
}
// test the case where we are only allowing a portion of the iv to be generated
TEST_F(Pkcs11AesGcmTest, MessageInterfaceIVMask) {
EXPECT_EQ(SECSuccess,
MessageInterfaceTest(16, 124, CKG_GENERATE_COUNTER, PR_FALSE));
}
// test the case where we using the tls1.3 iv generation
TEST_F(Pkcs11AesGcmTest, MessageInterfaceXorCounter) {
EXPECT_EQ(SECSuccess,
MessageInterfaceTest(16, 0, CKG_GENERATE_COUNTER_XOR, PR_FALSE));
}
// test the case where we overflow the counter (requires restricted iv)
// 128-124 = 4 bits;
TEST_F(Pkcs11AesGcmTest, MessageInterfaceCounterOverflow) {
EXPECT_EQ(SECFailure,
MessageInterfaceTest(17, 124, CKG_GENERATE_COUNTER, PR_FALSE));
}
// overflow the tla1.2 iv case
TEST_F(Pkcs11AesGcmTest, MessageInterfaceXorCounterOverflow) {
EXPECT_EQ(SECFailure,
MessageInterfaceTest(17, 124, CKG_GENERATE_COUNTER_XOR, PR_FALSE));
}
// test random generation of the IV (uses an aligned restricted iv)
TEST_F(Pkcs11AesGcmTest, MessageInterfaceRandomIV) {
EXPECT_EQ(SECSuccess,
MessageInterfaceTest(16, 56, CKG_GENERATE_RANDOM, PR_FALSE));
}
// test the case where we try to generate too many random IVs for the size of
// our our restricted IV (notice for counters, we can generate 16 IV with
// 4 bits, but for random we need at least 72 bits to generate 16 IVs).
// 128-56 = 72 bits
TEST_F(Pkcs11AesGcmTest, MessageInterfaceRandomOverflow) {
EXPECT_EQ(SECFailure,
MessageInterfaceTest(17, 56, CKG_GENERATE_RANDOM, PR_FALSE));
}
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -116,6 +117,6 @@ class Pkcs11AESKeyWrapTest : public ::testing::TestWithParam<keywrap_vector> {
TEST_P(Pkcs11AESKeyWrapTest, TestVectors) { WrapUnwrap(GetParam()); }
INSTANTIATE_TEST_CASE_P(Pkcs11WycheproofAESKWTest, Pkcs11AESKeyWrapTest,
::testing::ValuesIn(kWycheproofAesKWVectors));
} /* nss_test */
INSTANTIATE_TEST_SUITE_P(Pkcs11WycheproofAESKWTest, Pkcs11AESKeyWrapTest,
::testing::ValuesIn(kWycheproofAesKWVectors));
} // namespace nss_test

View file

@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <memory>
#include "nss.h"
#include "pk11pub.h"
#include "testvectors/kw-vectors.h"
#include "testvectors/kwp-vectors.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
namespace nss_test {
class Pkcs11AESKeyWrapKwpTest
: public ::testing::TestWithParam<keywrap_vector> {
protected:
CK_MECHANISM_TYPE mechanism = CKM_AES_KEY_WRAP_KWP;
void WrapUnwrap(unsigned char* kek_data, unsigned int kek_len,
unsigned char* key_data, unsigned int key_data_len,
unsigned char* expected_ciphertext,
unsigned int expected_ciphertext_len,
std::map<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), 0);
unsigned int wrapped_key_len = 0;
unsigned int unwrapped_key_len = 0;
SECStatus rv;
std::stringstream s;
s << "Test with original ID #" << test_id << " failed." << std::endl;
std::string msg = s.str();
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot) << msg;
// Import encryption key.
SECItem kek_item = {siBuffer, kek_data, kek_len};
ScopedPK11SymKey kek(PK11_ImportSymKeyWithFlags(
slot.get(), mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &kek_item,
CKF_DECRYPT, PR_FALSE, nullptr));
EXPECT_TRUE(!!kek) << msg;
// Wrap key
Action test = WRAP;
if (tests.count(test)) {
rv = PK11_Encrypt(kek.get(), mechanism, nullptr /* param */,
wrapped_key.data(), &wrapped_key_len,
wrapped_key.size(), key_data, key_data_len);
ASSERT_EQ(rv, tests[test].expect_rv) << msg;
// If we failed, check that output was not produced.
if (rv == SECFailure) {
EXPECT_TRUE(wrapped_key_len == 0);
EXPECT_TRUE(!memcmp(wrapped_key.data(), zeros.data(), wrapped_key_len));
}
if (tests[test].output_match) {
EXPECT_EQ(expected_ciphertext_len, wrapped_key_len) << msg;
EXPECT_TRUE(!memcmp(expected_ciphertext, wrapped_key.data(),
expected_ciphertext_len))
<< msg;
} else {
// If we produced output, verify that it doesn't match the vector
if (wrapped_key_len) {
EXPECT_FALSE(wrapped_key_len == expected_ciphertext_len &&
!memcmp(wrapped_key.data(), expected_ciphertext,
expected_ciphertext_len))
<< msg;
}
}
}
// Unwrap key
test = UNWRAP;
if (tests.count(test)) {
rv = PK11_Decrypt(kek.get(), mechanism, nullptr /* param */,
unwrapped_key.data(), &unwrapped_key_len,
unwrapped_key.size(), expected_ciphertext,
expected_ciphertext_len);
ASSERT_EQ(rv, tests[test].expect_rv) << msg;
// If we failed, check that output was not produced.
if (rv == SECFailure) {
EXPECT_TRUE(unwrapped_key_len == 0);
EXPECT_TRUE(
!memcmp(unwrapped_key.data(), zeros.data(), unwrapped_key_len));
}
if (tests[test].output_match) {
EXPECT_EQ(unwrapped_key_len, key_data_len) << msg;
EXPECT_TRUE(!memcmp(key_data, unwrapped_key.data(), key_data_len))
<< msg;
} else {
// If we produced output, verify that it doesn't match the vector
if (unwrapped_key_len) {
EXPECT_FALSE(
unwrapped_key_len == expected_ciphertext_len &&
!memcmp(unwrapped_key.data(), key_data, unwrapped_key_len))
<< msg;
}
}
}
}
void WrapUnwrap(keywrap_vector testvector) {
WrapUnwrap(testvector.key.data(), testvector.key.size(),
testvector.msg.data(), testvector.msg.size(),
testvector.ct.data(), testvector.ct.size(), testvector.tests,
testvector.test_id);
}
};
TEST_P(Pkcs11AESKeyWrapKwpTest, TestVectors) { WrapUnwrap(GetParam()); }
INSTANTIATE_TEST_SUITE_P(Pkcs11NistAESKWPTest, Pkcs11AESKeyWrapKwpTest,
::testing::ValuesIn(kNistAesKWPVectors));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -65,6 +66,14 @@ TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapECKey) {
true, CKK_EC, usages, usageCount, nullptr));
ASSERT_EQ(0, PORT_GetError());
ASSERT_TRUE(!!unwrapped);
// Try it with internal params allocation.
SECKEYPrivateKey* tmp = PK11_UnwrapPrivKey(
slot.get(), kek.get(), CKM_NSS_AES_KEY_WRAP_PAD, nullptr, wrapped.get(),
nullptr, &pubKey, false, true, CKK_EC, usages, usageCount, nullptr);
ASSERT_EQ(0, PORT_GetError());
ASSERT_NE(nullptr, tmp);
unwrapped.reset(tmp);
}
// Encrypt an ephemeral RSA key
@ -411,4 +420,4 @@ TEST_F(Pkcs11AESKeyWrapPadTest, WrapUnwrapRandom_ShortValidPadding) {
ASSERT_EQ(0, memcmp(buf, unwrapped_key.data(), out_len));
}
} /* nss_test */
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -8,8 +9,10 @@
#include "pk11pub.h"
#include "secerr.h"
#include "nss_scoped_ptrs.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "testvectors/cbc-vectors.h"
#include "util.h"
namespace nss_test {
@ -253,8 +256,8 @@ TEST_F(Pkcs11CbcPadTest, FailEncryptShortParam) {
unsigned int encrypted_len = 0;
size_t input_len = AES_BLOCK_SIZE;
// CK_GCM_PARAMS is the largest param struct used across AES modes
uint8_t param_buf[sizeof(CK_GCM_PARAMS)];
// CK_NSS_GCM_PARAMS is the largest param struct used across AES modes
uint8_t param_buf[sizeof(CK_NSS_GCM_PARAMS)];
SECItem param = {siBuffer, param_buf, sizeof(param_buf)};
SECItem key_item = {siBuffer, const_cast<uint8_t*>(kKeyData), 16};
@ -278,18 +281,18 @@ TEST_F(Pkcs11CbcPadTest, FailEncryptShortParam) {
sizeof(encrypted), kInput, input_len);
EXPECT_EQ(SECSuccess, rv);
// GCM should have a CK_GCM_PARAMS
param.len = sizeof(CK_GCM_PARAMS) - 1;
// GCM should have a CK_NSS_GCM_PARAMS
param.len = sizeof(CK_NSS_GCM_PARAMS) - 1;
rv = PK11_Encrypt(key.get(), CKM_AES_GCM, &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;
reinterpret_cast<CK_NSS_GCM_PARAMS*>(param.data)->pIv = param_buf;
reinterpret_cast<CK_NSS_GCM_PARAMS*>(param.data)->ulIvLen = 12;
reinterpret_cast<CK_NSS_GCM_PARAMS*>(param.data)->pAAD = nullptr;
reinterpret_cast<CK_NSS_GCM_PARAMS*>(param.data)->ulAADLen = 0;
reinterpret_cast<CK_NSS_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);
@ -550,8 +553,56 @@ TEST_P(Pkcs11CbcPadTest, EncryptDecrypt_ShortValidPadding) {
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));
INSTANTIATE_TEST_SUITE_P(EncryptDecrypt, Pkcs11CbcPadTest,
::testing::Values(CKM_AES_CBC_PAD, CKM_AES_CBC,
CKM_DES3_CBC_PAD, CKM_DES3_CBC));
class Pkcs11AesCbcWycheproofTest
: public ::testing::TestWithParam<AesCbcTestVector> {
protected:
void RunTest(const AesCbcTestVector vec) {
bool valid = vec.valid;
std::string err = "Test #" + std::to_string(vec.id) + " failed";
std::vector<uint8_t> key = hex_string_to_bytes(vec.key);
std::vector<uint8_t> iv = hex_string_to_bytes(vec.iv);
std::vector<uint8_t> ciphertext = hex_string_to_bytes(vec.ciphertext);
std::vector<uint8_t> msg = hex_string_to_bytes(vec.msg);
std::vector<uint8_t> decrypted(vec.ciphertext.size());
unsigned int decrypted_len = 0;
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
// Don't provide a null pointer, even if the length is 0. We don't want to
// fail on trivial checks.
uint8_t tmp;
SECItem iv_item = {siBuffer, iv.data() ? iv.data() : &tmp,
static_cast<unsigned int>(iv.size())};
SECItem key_item = {siBuffer, key.data() ? key.data() : &tmp,
static_cast<unsigned int>(key.size())};
PK11SymKey* pKey = PK11_ImportSymKey(slot.get(), kMech, PK11_OriginUnwrap,
CKA_ENCRYPT, &key_item, nullptr);
ASSERT_NE(nullptr, pKey);
ScopedPK11SymKey spKey = ScopedPK11SymKey(pKey);
SECStatus rv = PK11_Decrypt(spKey.get(), kMech, &iv_item, decrypted.data(),
&decrypted_len, decrypted.size(),
ciphertext.data(), ciphertext.size());
ASSERT_EQ(valid ? SECSuccess : SECFailure, rv) << err;
if (valid) {
EXPECT_EQ(msg.size(), static_cast<size_t>(decrypted_len)) << err;
EXPECT_EQ(0, memcmp(msg.data(), decrypted.data(), decrypted_len)) << err;
}
}
const CK_MECHANISM_TYPE kMech = CKM_AES_CBC_PAD;
};
TEST_P(Pkcs11AesCbcWycheproofTest, TestVectors) { RunTest(GetParam()); }
INSTANTIATE_TEST_SUITE_P(WycheproofTestVector, Pkcs11AesCbcWycheproofTest,
::testing::ValuesIn(kCbcWycheproofVectors));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -6,6 +7,7 @@
#include <memory>
#include "nss.h"
#include "pk11pub.h"
#include "pk11priv.h"
#include "sechash.h"
#include "secerr.h"
@ -17,46 +19,41 @@
namespace nss_test {
static const CK_MECHANISM_TYPE kMech = CKM_NSS_CHACHA20_POLY1305;
static const CK_MECHANISM_TYPE kMechXor = CKM_NSS_CHACHA20_CTR;
static const CK_MECHANISM_TYPE kMech = CKM_CHACHA20_POLY1305;
static const CK_MECHANISM_TYPE kMechLegacy = CKM_NSS_CHACHA20_POLY1305;
static const CK_MECHANISM_TYPE kMechXor = CKM_CHACHA20;
static const CK_MECHANISM_TYPE kMechXorLegacy = 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 kXorParamsLegacy[16] = {'c', 0, 0, 0, 'n'};
static const uint8_t kCounter[4] = {'c', 0};
static const uint8_t kNonce[12] = {'n', 0};
static const CK_CHACHA20_PARAMS kXorParams{
/* pBlockCounter */ const_cast<CK_BYTE_PTR>(kCounter),
/* blockCounterBits */ sizeof(kCounter) * 8,
/* pNonce */ const_cast<CK_BYTE_PTR>(kNonce),
/* ulNonceBits */ sizeof(kNonce) * 8,
};
static const uint8_t kData[16] = {'d'};
static const uint8_t kExpectedXor[sizeof(kData)] = {
0xd8, 0x15, 0xd3, 0xb3, 0xe9, 0x34, 0x3b, 0x7a,
0x24, 0xf6, 0x5f, 0xd7, 0x95, 0x3d, 0xd3, 0x51};
static const size_t kTagLen = 16;
class Pkcs11ChaCha20Poly1305Test
: public ::testing::TestWithParam<chaChaTestVector> {
: public ::testing::TestWithParam<ChaChaTestVector> {
public:
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,
size_t data_len, CK_MECHANISM_TYPE mech, SECItem* params,
std::vector<uint8_t>* nonce, std::vector<uint8_t>* aad,
const uint8_t* ct = nullptr, size_t ct_len = 0) {
// Prepare AEAD params.
CK_NSS_AEAD_PARAMS aead_params;
aead_params.pNonce = toUcharPtr(iv);
aead_params.ulNonceLen = iv_len;
aead_params.pAAD = toUcharPtr(aad);
aead_params.ulAADLen = aad_len;
aead_params.ulTagLen = 16;
SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&aead_params),
sizeof(aead_params)};
// Encrypt with bad parameters.
std::vector<uint8_t> encrypted(data_len + kTagLen);
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.
rv = PK11_Encrypt(key.get(), kMech, &params, encrypted.data(),
&encrypted_len, encrypted.size(), data, data_len);
SECStatus rv =
PK11_Encrypt(key.get(), mech, 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.
@ -73,6 +70,7 @@ class Pkcs11ChaCha20Poly1305Test
// Check ciphertext and tag.
if (ct) {
ASSERT_EQ(ct_len, encrypted_len);
EXPECT_TRUE(!memcmp(ct, encrypted.data(), encrypted.size() - 16));
EXPECT_TRUE(!memcmp(ct, encrypted.data(), encrypted.size()) !=
invalid_tag);
}
@ -82,7 +80,7 @@ class Pkcs11ChaCha20Poly1305Test
// 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,
rv = PK11_Decrypt(key.get(), mech, params, nullptr, &decrypt_bytes_needed,
0, encrypted.data(), encrypted_len);
EXPECT_EQ(rv, SECSuccess);
EXPECT_GT(decrypt_bytes_needed, data_len);
@ -90,9 +88,8 @@ class Pkcs11ChaCha20Poly1305Test
// 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());
rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(), &decrypted_len,
decrypted.size(), encrypted.data(), encrypted.size());
EXPECT_EQ(rv, SECSuccess);
// Check the plaintext.
@ -105,7 +102,7 @@ class Pkcs11ChaCha20Poly1305Test
decrypted_len = 0;
std::vector<uint8_t> bogus_ciphertext(encrypted);
bogus_ciphertext[0] ^= 0xff;
rv = PK11_Decrypt(key.get(), kMech, &params, decrypted.data(),
rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(),
&decrypted_len, decrypted.size(),
bogus_ciphertext.data(), encrypted_len);
EXPECT_EQ(rv, SECFailure);
@ -118,47 +115,32 @@ class Pkcs11ChaCha20Poly1305Test
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(),
rv = PK11_Decrypt(key.get(), mech, 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.
// 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);
bogus_params.data = reinterpret_cast<unsigned char*>(&bogusAeadParams);
std::vector<uint8_t> bogusIV(iv, iv + iv_len);
bogusAeadParams.pNonce = toUcharPtr(bogusIV.data());
bogusIV[0] ^= 0xff;
rv = PK11_Decrypt(key.get(), kMech, &bogus_params, decrypted.data(),
&decrypted_len, data_len, encrypted.data(),
encrypted.size());
EXPECT_EQ(rv, SECFailure);
EXPECT_EQ(0U, decrypted_len);
}
// Decrypt with bogus nonce.
// A nonce length of 0 is invalid and should be caught earlier.
ASSERT_NE(0U, nonce->size());
decrypted_len = 0;
nonce->data()[0] ^= 0xff;
rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(), &decrypted_len,
data_len, encrypted.data(), encrypted.size());
EXPECT_EQ(rv, SECFailure);
EXPECT_EQ(0U, decrypted_len);
nonce->data()[0] ^= 0xff; // restore value
// Decrypt with bogus additional data.
// Skip when AAD was empty and can't be modified.
// Alternatively we could generate random aad.
if (aad_len != 0) {
if (aad->size() != 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);
aad->data()[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(key.get(), kMech, &bogus_params, decrypted.data(),
rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(),
&decrypted_len, data_len, encrypted.data(),
encrypted.size());
EXPECT_EQ(rv, SECFailure);
@ -166,10 +148,73 @@ class Pkcs11ChaCha20Poly1305Test
}
}
void EncryptDecrypt(const chaChaTestVector testvector) {
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_ptr, size_t aad_len,
const uint8_t* iv_ptr, size_t iv_len,
const uint8_t* ct = nullptr, size_t ct_len = 0) {
std::vector<uint8_t> nonce(iv_ptr, iv_ptr + iv_len);
std::vector<uint8_t> aad(aad_ptr, aad_ptr + aad_len);
// Prepare AEAD params.
CK_SALSA20_CHACHA20_POLY1305_PARAMS aead_params;
aead_params.pNonce = toUcharPtr(nonce.data());
aead_params.ulNonceLen = nonce.size();
aead_params.pAAD = toUcharPtr(aad.data());
aead_params.ulAADLen = aad.size();
SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&aead_params),
sizeof(aead_params)};
EncryptDecrypt(key, invalid_iv, invalid_tag, data, data_len, kMech, &params,
&nonce, &aad, ct, ct_len);
}
void EncryptDecryptLegacy(const ScopedPK11SymKey& key, const bool invalid_iv,
const bool invalid_tag, const uint8_t* data,
size_t data_len, const uint8_t* aad_ptr,
size_t aad_len, const uint8_t* iv_ptr,
size_t iv_len, const uint8_t* ct = nullptr,
size_t ct_len = 0) {
std::vector<uint8_t> nonce(iv_ptr, iv_ptr + iv_len);
std::vector<uint8_t> aad(aad_ptr, aad_ptr + aad_len);
// Prepare AEAD params.
CK_NSS_AEAD_PARAMS aead_params;
aead_params.pNonce = toUcharPtr(nonce.data());
aead_params.ulNonceLen = nonce.size();
aead_params.pAAD = toUcharPtr(aad.data());
aead_params.ulAADLen = aad.size();
aead_params.ulTagLen = kTagLen;
SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&aead_params),
sizeof(aead_params)};
// Encrypt with bad parameters (TagLen is too long).
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(), kMechLegacy, &params, encrypted.data(),
&encrypted_len, encrypted.size(), data, data_len);
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(0U, encrypted_len);
// Encrypt with bad parameters (TagLen is too short).
aead_params.ulTagLen = 2;
rv = PK11_Encrypt(key.get(), kMechLegacy, &params, encrypted.data(),
&encrypted_len, encrypted.size(), data, data_len);
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(0U, encrypted_len);
// Encrypt.
aead_params.ulTagLen = kTagLen;
EncryptDecrypt(key, invalid_iv, invalid_tag, data, data_len, kMechLegacy,
&params, &nonce, &aad, ct, ct_len);
}
void EncryptDecrypt(const ChaChaTestVector testvector) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
SECItem keyItem = {siBuffer, toUcharPtr(testvector.Key.data()),
static_cast<unsigned int>(testvector.Key.size())};
SECItem keyItem = {siBuffer, toUcharPtr(testvector.key.data()),
static_cast<unsigned int>(testvector.key.size())};
// Import key.
ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), kMech, PK11_OriginUnwrap,
@ -177,11 +222,173 @@ class Pkcs11ChaCha20Poly1305Test
EXPECT_TRUE(!!key);
// Check.
EncryptDecrypt(key, testvector.invalidIV, testvector.invalidTag,
testvector.Data.data(), testvector.Data.size(),
testvector.AAD.data(), testvector.AAD.size(),
testvector.IV.data(), testvector.IV.size(),
testvector.CT.data(), testvector.CT.size());
EncryptDecrypt(key, testvector.invalid_iv, testvector.invalid_tag,
testvector.plaintext.data(), testvector.plaintext.size(),
testvector.aad.data(), testvector.aad.size(),
testvector.iv.data(), testvector.iv.size(),
testvector.ciphertext.data(), testvector.ciphertext.size());
}
void MessageInterfaceTest(CK_MECHANISM_TYPE mech, int iterations,
PRBool separateTag) {
// Generate a random key.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot);
ScopedPK11SymKey sym_key(
PK11_KeyGen(slot.get(), mech, nullptr, 32, nullptr));
ASSERT_NE(nullptr, sym_key);
int tagSize = kTagLen;
int cipher_simulated_size;
int output_len_message = 0;
int output_len_simulated = 0;
unsigned int output_len_v24 = 0;
std::vector<uint8_t> plainIn(17);
std::vector<uint8_t> plainOut_message(17);
std::vector<uint8_t> plainOut_simulated(17);
std::vector<uint8_t> plainOut_v24(17);
std::vector<uint8_t> nonce(12);
std::vector<uint8_t> cipher_message(33);
std::vector<uint8_t> cipher_simulated(33);
std::vector<uint8_t> cipher_v24(33);
std::vector<uint8_t> aad(16);
std::vector<uint8_t> tag_message(kTagLen);
std::vector<uint8_t> tag_simulated(kTagLen);
// Prepare AEAD v2.40 params.
CK_SALSA20_CHACHA20_POLY1305_PARAMS chacha_params;
chacha_params.pNonce = nonce.data();
chacha_params.ulNonceLen = nonce.size();
chacha_params.pAAD = aad.data();
chacha_params.ulAADLen = aad.size();
// Prepare AEAD MESSAGE params.
CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS chacha_message_params;
chacha_message_params.pNonce = nonce.data();
chacha_message_params.ulNonceLen = nonce.size();
if (separateTag) {
chacha_message_params.pTag = tag_message.data();
} else {
chacha_message_params.pTag = cipher_message.data() + plainIn.size();
}
// Prepare AEAD MESSAGE params for simulated case
CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS chacha_simulated_params;
chacha_simulated_params = chacha_message_params;
if (separateTag) {
// The simulated case, we have to allocate temp bufs for separate
// tags, make sure that works in both the encrypt and the decrypt
// cases.
chacha_simulated_params.pTag = tag_simulated.data();
cipher_simulated_size = cipher_simulated.size() - tagSize;
} else {
chacha_simulated_params.pTag = cipher_simulated.data() + plainIn.size();
cipher_simulated_size = cipher_simulated.size();
}
SECItem params = {siBuffer,
reinterpret_cast<unsigned char*>(&chacha_params),
sizeof(chacha_params)};
SECItem empty = {siBuffer, NULL, 0};
// initialize our plain text, IV and aad.
ASSERT_EQ(PK11_GenerateRandom(plainIn.data(), plainIn.size()), SECSuccess);
ASSERT_EQ(PK11_GenerateRandom(aad.data(), aad.size()), SECSuccess);
// Initialize message encrypt context
ScopedPK11Context encrypt_message_context(PK11_CreateContextBySymKey(
mech, CKA_NSS_MESSAGE | CKA_ENCRYPT, sym_key.get(), &empty));
ASSERT_NE(nullptr, encrypt_message_context);
ASSERT_FALSE(_PK11_ContextGetAEADSimulation(encrypt_message_context.get()));
// Initialize simulated encrypt context
ScopedPK11Context encrypt_simulated_context(PK11_CreateContextBySymKey(
mech, CKA_NSS_MESSAGE | CKA_ENCRYPT, sym_key.get(), &empty));
ASSERT_NE(nullptr, encrypt_simulated_context);
ASSERT_EQ(SECSuccess,
_PK11_ContextSetAEADSimulation(encrypt_simulated_context.get()));
// Initialize message decrypt context
ScopedPK11Context decrypt_message_context(PK11_CreateContextBySymKey(
mech, CKA_NSS_MESSAGE | CKA_DECRYPT, sym_key.get(), &empty));
ASSERT_NE(nullptr, decrypt_message_context);
ASSERT_FALSE(_PK11_ContextGetAEADSimulation(decrypt_message_context.get()));
// Initialize simulated decrypt context
ScopedPK11Context decrypt_simulated_context(PK11_CreateContextBySymKey(
mech, CKA_NSS_MESSAGE | CKA_DECRYPT, sym_key.get(), &empty));
ASSERT_NE(nullptr, decrypt_simulated_context);
EXPECT_EQ(SECSuccess,
_PK11_ContextSetAEADSimulation(decrypt_simulated_context.get()));
// Now walk down our iterations. Each method of calculating the operation
// should agree at each step.
for (int i = 0; i < iterations; i++) {
// get a unique nonce for each iteration
EXPECT_EQ(PK11_GenerateRandom(nonce.data(), nonce.size()), SECSuccess);
EXPECT_EQ(SECSuccess,
PK11_AEADRawOp(
encrypt_message_context.get(), &chacha_message_params,
sizeof(chacha_message_params), aad.data(), aad.size(),
cipher_message.data(), &output_len_message,
cipher_message.size(), plainIn.data(), plainIn.size()));
EXPECT_EQ(SECSuccess,
PK11_AEADRawOp(
encrypt_simulated_context.get(), &chacha_simulated_params,
sizeof(chacha_simulated_params), aad.data(), aad.size(),
cipher_simulated.data(), &output_len_simulated,
cipher_simulated_size, plainIn.data(), plainIn.size()));
// make sure simulated and message is the same
EXPECT_EQ(output_len_message, output_len_simulated);
EXPECT_EQ(0, memcmp(cipher_message.data(), cipher_simulated.data(),
output_len_message));
EXPECT_EQ(0, memcmp(chacha_message_params.pTag,
chacha_simulated_params.pTag, tagSize));
// make sure v2.40 is the same.
EXPECT_EQ(SECSuccess,
PK11_Encrypt(sym_key.get(), mech, &params, cipher_v24.data(),
&output_len_v24, cipher_v24.size(), plainIn.data(),
plainIn.size()));
EXPECT_EQ(output_len_message, (int)output_len_v24 - tagSize);
EXPECT_EQ(0, memcmp(cipher_message.data(), cipher_v24.data(),
output_len_message));
EXPECT_EQ(0, memcmp(chacha_message_params.pTag,
cipher_v24.data() + output_len_message, tagSize));
// now make sure we can decrypt
EXPECT_EQ(
SECSuccess,
PK11_AEADRawOp(decrypt_message_context.get(), &chacha_message_params,
sizeof(chacha_message_params), aad.data(), aad.size(),
plainOut_message.data(), &output_len_message,
plainOut_message.size(), cipher_message.data(),
output_len_message));
EXPECT_EQ(output_len_message, (int)plainIn.size());
EXPECT_EQ(
0, memcmp(plainOut_message.data(), plainIn.data(), plainIn.size()));
EXPECT_EQ(SECSuccess,
PK11_AEADRawOp(decrypt_simulated_context.get(),
&chacha_simulated_params,
sizeof(chacha_simulated_params), aad.data(),
aad.size(), plainOut_simulated.data(),
&output_len_simulated, plainOut_simulated.size(),
cipher_message.data(), output_len_simulated));
EXPECT_EQ(output_len_simulated, (int)plainIn.size());
EXPECT_EQ(
0, memcmp(plainOut_simulated.data(), plainIn.data(), plainIn.size()));
if (separateTag) {
// in the separateTag case, we need to copy the tag back to the
// end of the cipher_message.data() before using the v2.4 interface
memcpy(cipher_message.data() + output_len_message,
chacha_message_params.pTag, tagSize);
}
EXPECT_EQ(SECSuccess,
PK11_Decrypt(sym_key.get(), mech, &params, plainOut_v24.data(),
&output_len_v24, plainOut_v24.size(),
cipher_message.data(), output_len_v24));
EXPECT_EQ(output_len_v24, plainIn.size());
EXPECT_EQ(0, memcmp(plainOut_v24.data(), plainIn.data(), plainIn.size()));
}
return;
}
protected:
@ -215,10 +422,6 @@ TEST_F(Pkcs11ChaCha20Poly1305Test, GenerateEncryptDecrypt) {
}
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))};
@ -226,30 +429,66 @@ TEST_F(Pkcs11ChaCha20Poly1305Test, Xor) {
slot.get(), kMechXor, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr));
EXPECT_TRUE(!!key);
SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce),
static_cast<unsigned int>(sizeof(kCtrNonce))};
SECItem params = {siBuffer,
toUcharPtr(reinterpret_cast<const uint8_t*>(&kXorParams)),
static_cast<unsigned int>(sizeof(kXorParams))};
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));
PK11_Encrypt(key.get(), kMechXor, &params, 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)));
ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len));
EXPECT_EQ(0, memcmp(kExpectedXor, encrypted, sizeof(kExpectedXor)));
// Decrypting has the same effect.
rv = PK11_Decrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
rv = PK11_Decrypt(key.get(), kMechXor, &params, 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(kExpectedXor, encrypted, sizeof(kExpectedXor)));
// Operating in reverse too.
rv = PK11_Encrypt(key.get(), kMechXor, &params, encrypted, &encrypted_len,
sizeof(encrypted), kExpectedXor, sizeof(kExpectedXor));
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len));
EXPECT_EQ(0, memcmp(kData, encrypted, sizeof(kData)));
}
TEST_F(Pkcs11ChaCha20Poly1305Test, XorLegacy) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
SECItem keyItem = {siBuffer, toUcharPtr(kKeyData),
static_cast<unsigned int>(sizeof(kKeyData))};
ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), kMechXorLegacy,
PK11_OriginUnwrap, CKA_ENCRYPT,
&keyItem, nullptr));
EXPECT_TRUE(!!key);
SECItem ctrNonceItem = {siBuffer, toUcharPtr(kXorParamsLegacy),
static_cast<unsigned int>(sizeof(kXorParamsLegacy))};
uint8_t encrypted[sizeof(kData)];
unsigned int encrypted_len = 88; // This should be overwritten.
SECStatus rv =
PK11_Encrypt(key.get(), kMechXorLegacy, &ctrNonceItem, encrypted,
&encrypted_len, sizeof(encrypted), kData, sizeof(kData));
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len));
EXPECT_EQ(0, memcmp(kExpectedXor, encrypted, sizeof(kExpectedXor)));
// Decrypting has the same effect.
rv = PK11_Decrypt(key.get(), kMechXorLegacy, &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)));
EXPECT_EQ(0, memcmp(kExpectedXor, encrypted, sizeof(kExpectedXor)));
// Operating in reverse too.
rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
&encrypted_len, sizeof(encrypted), kExpected,
sizeof(kExpected));
rv = PK11_Encrypt(key.get(), kMechXorLegacy, &ctrNonceItem, encrypted,
&encrypted_len, sizeof(encrypted), kExpectedXor,
sizeof(kExpectedXor));
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(sizeof(kExpected), static_cast<size_t>(encrypted_len));
ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len));
EXPECT_EQ(0, memcmp(kData, encrypted, sizeof(kData)));
}
@ -257,18 +496,45 @@ TEST_F(Pkcs11ChaCha20Poly1305Test, 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));
ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMechXor, nullptr, 32, nullptr));
EXPECT_TRUE(!!key);
std::vector<uint8_t> iv(16);
SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), iv.data(), iv.size());
EXPECT_EQ(SECSuccess, rv);
SECItem ctrNonceItem = {siBuffer, toUcharPtr(iv.data()),
static_cast<unsigned int>(iv.size())};
CK_CHACHA20_PARAMS chacha_params;
chacha_params.pBlockCounter = iv.data();
chacha_params.blockCounterBits = 32;
chacha_params.pNonce = iv.data() + 4;
chacha_params.ulNonceBits = 96;
SECItem params = {
siBuffer, toUcharPtr(reinterpret_cast<const uint8_t*>(&chacha_params)),
static_cast<unsigned int>(sizeof(chacha_params))};
uint8_t encrypted[sizeof(kData)];
unsigned int encrypted_len = 88; // This should be overwritten.
rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
rv = PK11_Encrypt(key.get(), kMechXor, &params, 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, GenerateXorLegacy) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey key(
PK11_KeyGen(slot.get(), kMechXorLegacy, nullptr, 32, nullptr));
EXPECT_TRUE(!!key);
std::vector<uint8_t> iv(16);
SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), iv.data(), iv.size());
EXPECT_EQ(SECSuccess, rv);
SECItem params = {siBuffer, toUcharPtr(iv.data()),
static_cast<unsigned int>(iv.size())};
uint8_t encrypted[sizeof(kData)];
unsigned int encrypted_len = 88; // This should be overwritten.
rv = PK11_Encrypt(key.get(), kMechXorLegacy, &params, encrypted,
&encrypted_len, sizeof(encrypted), kData, sizeof(kData));
ASSERT_EQ(SECSuccess, rv);
ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len));
@ -279,28 +545,63 @@ TEST_F(Pkcs11ChaCha20Poly1305Test, XorInvalidParams) {
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};
SECItem params = {siBuffer,
toUcharPtr(reinterpret_cast<const uint8_t*>(&kXorParams)),
static_cast<unsigned int>(sizeof(kXorParams)) - 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));
PK11_Encrypt(key.get(), kMechXor, &params, 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));
params.data = nullptr;
rv = PK11_Encrypt(key.get(), kMechXor, &params, encrypted, &encrypted_len,
sizeof(encrypted), kData, sizeof(kData));
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
}
TEST_F(Pkcs11ChaCha20Poly1305Test, XorLegacyInvalidParams) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr));
EXPECT_TRUE(!!key);
SECItem params = {siBuffer, toUcharPtr(kXorParamsLegacy),
static_cast<unsigned int>(sizeof(kXorParamsLegacy)) - 1};
uint8_t encrypted[sizeof(kData)];
unsigned int encrypted_len = 88;
SECStatus rv =
PK11_Encrypt(key.get(), kMechXor, &params, encrypted, &encrypted_len,
sizeof(encrypted), kData, sizeof(kData));
EXPECT_EQ(SECFailure, rv);
params.data = nullptr;
rv = PK11_Encrypt(key.get(), kMechXor, &params, 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_SUITE_P(NSSTestVector, Pkcs11ChaCha20Poly1305Test,
::testing::ValuesIn(kChaCha20Vectors));
INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11ChaCha20Poly1305Test,
::testing::ValuesIn(kChaCha20WycheproofVectors));
INSTANTIATE_TEST_SUITE_P(WycheproofTestVector, Pkcs11ChaCha20Poly1305Test,
::testing::ValuesIn(kChaCha20WycheproofVectors));
// basic message interface it's the most common configuration
TEST_F(Pkcs11ChaCha20Poly1305Test, ChaCha201305MessageInterfaceBasic) {
MessageInterfaceTest(CKM_CHACHA20_POLY1305, 16, PR_FALSE);
}
// basic interface, but return the tags in a separate buffer. This triggers
// different behaviour in the simulated case, which has to buffer the
// intermediate values in a separate buffer.
TEST_F(Pkcs11ChaCha20Poly1305Test,
ChaCha20Poly1305MessageInterfaceSeparateTags) {
MessageInterfaceTest(CKM_CHACHA20_POLY1305, 16, PR_TRUE);
}
} // namespace nss_test

View file

@ -3,6 +3,7 @@
// You can obtain one at http://mozilla.org/MPL/2.0/.
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include <assert.h>
#include <limits.h>
@ -21,30 +22,29 @@ namespace nss_test {
// cipher context with data that is not cipher block aligned.
//
static SECStatus GetBytes(PK11Context* ctx, uint8_t* bytes, size_t len) {
static SECStatus GetBytes(const ScopedPK11Context& ctx, size_t len) {
std::vector<uint8_t> in(len, 0);
uint8_t outbuf[128];
PORT_Assert(len <= sizeof(outbuf));
int outlen;
SECStatus rv = PK11_CipherOp(ctx, bytes, &outlen, len, &in[0], len);
SECStatus rv = PK11_CipherOp(ctx.get(), outbuf, &outlen, len, in.data(), len);
if (static_cast<size_t>(outlen) != len) {
return SECFailure;
EXPECT_EQ(rv, SECFailure);
}
return rv;
}
TEST(Pkcs11CipherOp, SingleCtxMultipleUnalignedCipherOps) {
PK11SlotInfo* slot;
PK11SymKey* key;
PK11Context* ctx;
NSSInitContext* globalctx =
ScopedNSSInitContext globalctx(
NSS_InitContext("", "", "", "", NULL,
NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB |
NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT);
NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT));
ASSERT_TRUE(globalctx);
const CK_MECHANISM_TYPE cipher = CKM_AES_CTR;
slot = PK11_GetInternalSlot();
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(slot);
// Use arbitrary bytes for the AES key
@ -61,20 +61,69 @@ TEST(Pkcs11CipherOp, SingleCtxMultipleUnalignedCipherOps) {
SECItem paramItem = {siBuffer, reinterpret_cast<unsigned char*>(&param),
sizeof(CK_AES_CTR_PARAMS)};
key = PK11_ImportSymKey(slot, cipher, PK11_OriginUnwrap, CKA_ENCRYPT,
&keyItem, NULL);
ctx = PK11_CreateContextBySymKey(cipher, CKA_ENCRYPT, key, &paramItem);
ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), cipher, PK11_OriginUnwrap,
CKA_ENCRYPT, &keyItem, NULL));
ASSERT_TRUE(key);
ScopedPK11Context ctx(
PK11_CreateContextBySymKey(cipher, CKA_ENCRYPT, key.get(), &paramItem));
ASSERT_TRUE(ctx);
uint8_t outbuf[128];
ASSERT_EQ(GetBytes(ctx, outbuf, 7), SECSuccess);
ASSERT_EQ(GetBytes(ctx, outbuf, 17), SECSuccess);
ASSERT_EQ(GetBytes(ctx, 7), SECSuccess);
ASSERT_EQ(GetBytes(ctx, 17), SECSuccess);
}
PK11_FreeSymKey(key);
PK11_FreeSlot(slot);
PK11_DestroyContext(ctx, PR_TRUE);
NSS_ShutdownContext(globalctx);
// A context can't be used for Chacha20 as the underlying
// PK11_CipherOp operation is calling the C_EncryptUpdate function for
// which multi-part is disabled for ChaCha20 in counter mode.
void ChachaMulti(CK_MECHANISM_TYPE cipher, SECItem* param) {
ScopedNSSInitContext globalctx(
NSS_InitContext("", "", "", "", NULL,
NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB |
NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT));
ASSERT_TRUE(globalctx);
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(slot);
// Use arbitrary bytes for the ChaCha20 key and IV
uint8_t key_bytes[32];
for (size_t i = 0; i < 32; i++) {
key_bytes[i] = i;
}
SECItem keyItem = {siBuffer, key_bytes, sizeof(key_bytes)};
ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), cipher, PK11_OriginUnwrap,
CKA_ENCRYPT, &keyItem, NULL));
ASSERT_TRUE(key);
ScopedSECItem param_item(PK11_ParamFromIV(cipher, param));
ASSERT_TRUE(param_item);
ScopedPK11Context ctx(PK11_CreateContextBySymKey(
cipher, CKA_ENCRYPT, key.get(), param_item.get()));
ASSERT_TRUE(ctx);
ASSERT_EQ(GetBytes(ctx, 7), SECFailure);
}
TEST(Pkcs11CipherOp, ChachaMultiLegacy) {
uint8_t iv_bytes[16];
for (size_t i = 0; i < 16; i++) {
iv_bytes[i] = i;
}
SECItem param_item = {siBuffer, iv_bytes, sizeof(iv_bytes)};
ChachaMulti(CKM_NSS_CHACHA20_CTR, &param_item);
}
TEST(Pkcs11CipherOp, ChachaMulti) {
uint8_t iv_bytes[16];
for (size_t i = 0; i < 16; i++) {
iv_bytes[i] = i;
}
CK_CHACHA20_PARAMS chacha_params = {iv_bytes, 32, iv_bytes + 4, 96};
SECItem param_item = {siBuffer, reinterpret_cast<uint8_t*>(&chacha_params),
sizeof(chacha_params)};
ChachaMulti(CKM_CHACHA20, &param_item);
}
} // namespace nss_test

View file

@ -2,20 +2,21 @@
* 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 <algorithm>
#include <memory>
#include "nss.h"
#include "pk11pub.h"
#include "prerror.h"
#include "cpputil.h"
#include "nss_scoped_ptrs.h"
#include "json_reader.h"
#include "testvectors/curve25519-vectors.h"
#include "gtest/gtest.h"
namespace nss_test {
class Pkcs11Curve25519Test
: public ::testing::TestWithParam<curve25519_testvector> {
class Pkcs11Curve25519TestBase {
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,
@ -105,22 +106,127 @@ class Pkcs11Curve25519Test
rv = PK11_DeleteTokenPrivateKey(priv_key_tok, true);
EXPECT_EQ(SECSuccess, rv);
}
};
}
void Derive(const EcdhTestVector& testvector) {
std::cout << "Running test: " << testvector.id << std::endl;
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_P(Pkcs11Curve25519Test, TestVectors) { Derive(GetParam()); }
class Pkcs11Curve25519Wycheproof : public Pkcs11Curve25519TestBase,
public ::testing::Test {
protected:
void RunGroup(JsonReader& r) {
std::vector<EcdhTestVector> tests;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "curve") {
ASSERT_EQ("curve25519", r.ReadString());
} else if (n == "type") {
ASSERT_EQ("XdhComp", r.ReadString());
} else if (n == "tests") {
WycheproofReadTests(r, &tests, ReadTestAttr, true,
Pkcs11Curve25519Wycheproof::FilterInvalid);
} else {
FAIL() << "unknown group label: " << n;
}
}
INSTANTIATE_TEST_CASE_P(NSSTestVector, Pkcs11Curve25519Test,
::testing::ValuesIn(kCurve25519Vectors));
for (auto& t : tests) {
Derive(t);
}
}
INSTANTIATE_TEST_CASE_P(WycheproofTestVector, Pkcs11Curve25519Test,
::testing::ValuesIn(kCurve25519WycheproofVectors));
private:
static void FilterInvalid(EcdhTestVector& t, const std::string& result,
const std::vector<std::string>& flags) {
static const std::vector<uint8_t> kNonCanonPublic1 = {
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, 0xda, 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,
};
static const std::vector<uint8_t> kNonCanonPublic2 = {
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, 0xdb, 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,
};
if (result == "acceptable" &&
(std::find_if(flags.begin(), flags.end(),
[](const std::string& flag) {
return flag == "SmallPublicKey" ||
flag == "ZeroSharedSecret";
}) != flags.end() ||
t.public_key == kNonCanonPublic1 ||
t.public_key == kNonCanonPublic2)) {
t.valid = false;
}
}
static void ReadTestAttr(EcdhTestVector& t, const std::string& n,
JsonReader& r) {
// Static PKCS#8 and SPKI wrappers for the raw keys from Wycheproof.
static const std::vector<uint8_t> kPrivatePrefix = {
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};
// The public key section of the PKCS#8 wrapper is filled up with 0's, which
// is not correct, but acceptable for the tests at this moment because
// validity of the public key is not checked.
// It's still necessary because of
// https://searchfox.org/nss/rev/7bc70a3317b800aac07bad83e74b6c79a9ec5bff/lib/pk11wrap/pk11pk12.c#171
static const std::vector<uint8_t> kPrivateSuffix = {
0xa1, 0x23, 0x03, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const std::vector<uint8_t> kPublicPrefix = {
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};
if (n == "public") {
t.public_key = kPublicPrefix;
std::vector<uint8_t> pub = r.ReadHex();
t.public_key.insert(t.public_key.end(), pub.begin(), pub.end());
} else if (n == "private") {
t.private_key = kPrivatePrefix;
std::vector<uint8_t> priv = r.ReadHex();
t.private_key.insert(t.private_key.end(), priv.begin(), priv.end());
t.private_key.insert(t.private_key.end(), kPrivateSuffix.begin(),
kPrivateSuffix.end());
} else if (n == "shared") {
t.secret = r.ReadHex();
} else {
FAIL() << "unsupported test case field: " << n;
}
}
};
TEST_F(Pkcs11Curve25519Wycheproof, Run) {
WycheproofHeader("x25519", "XDH", "xdh_comp_schema.json",
[this](JsonReader& r) { RunGroup(r); });
}
class Pkcs11Curve25519ParamTest
: public Pkcs11Curve25519TestBase,
public ::testing::TestWithParam<EcdhTestVector> {};
TEST_P(Pkcs11Curve25519ParamTest, TestVectors) { Derive(GetParam()); }
INSTANTIATE_TEST_SUITE_P(NSSTestVector, Pkcs11Curve25519ParamTest,
::testing::ValuesIn(kCurve25519Vectors));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -94,9 +95,10 @@ class DERPrivateKeyImportTest : public ::testing::Test {
std::to_string(rand());
SECItem item = {siBuffer, const_cast<unsigned char*>(data.data()),
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())};
SECItem nick = {
siBuffer,
reinterpret_cast<unsigned char*>(const_cast<char*>(nick_str.data())),
static_cast<unsigned int>(nick_str.length())};
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
EXPECT_TRUE(slot);

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

View file

@ -0,0 +1,81 @@
/* -*- 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 "prerror.h"
#include "pk11pub.h"
#include "sechash.h"
#include "cryptohi.h"
#include "cpputil.h"
#include "databuffer.h"
#include "pk11_signature_test.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "testvectors/dsa-vectors.h"
namespace nss_test {
CK_MECHANISM_TYPE
DsaHashToComboMech(SECOidTag hash) {
switch (hash) {
case SEC_OID_SHA1:
return CKM_DSA_SHA1;
case SEC_OID_SHA224:
return CKM_DSA_SHA224;
case SEC_OID_SHA256:
return CKM_DSA_SHA256;
case SEC_OID_SHA384:
return CKM_DSA_SHA384;
case SEC_OID_SHA512:
return CKM_DSA_SHA512;
default:
break;
}
return CKM_INVALID_MECHANISM;
}
class Pkcs11DsaTestBase : public Pk11SignatureTest {
protected:
Pkcs11DsaTestBase(SECOidTag hashOid)
: Pk11SignatureTest(CKM_DSA, hashOid, DsaHashToComboMech(hashOid)) {}
void Verify(const DsaTestVector vec) {
/* DSA vectors encode the signature in DER, we need to unwrap it before
* we can send the raw signatures to PKCS #11. */
DataBuffer pubKeyBuffer(vec.public_key.data(), vec.public_key.size());
ScopedSECKEYPublicKey nssPubKey(ImportPublicKey(pubKeyBuffer));
SECItem sigItem = {siBuffer, toUcharPtr(vec.sig.data()),
static_cast<unsigned int>(vec.sig.size())};
ScopedSECItem decodedSigItem(
DSAU_DecodeDerSigToLen(&sigItem, SECKEY_SignatureLen(nssPubKey.get())));
if (!decodedSigItem) {
ASSERT_FALSE(vec.valid) << "Failed to decode DSA signature Error: "
<< PORT_ErrorToString(PORT_GetError()) << "\n";
return;
}
Pkcs11SignatureTestParams params = {
DataBuffer(), pubKeyBuffer, DataBuffer(vec.msg.data(), vec.msg.size()),
DataBuffer(decodedSigItem.get()->data, decodedSigItem.get()->len)};
Pk11SignatureTest::Verify(params, (bool)vec.valid);
}
};
class Pkcs11DsaTest : public Pkcs11DsaTestBase,
public ::testing::WithParamInterface<DsaTestVector> {
public:
Pkcs11DsaTest() : Pkcs11DsaTestBase(GetParam().hash_oid) {}
};
TEST_P(Pkcs11DsaTest, WycheproofVectors) { Verify(GetParam()); }
INSTANTIATE_TEST_SUITE_P(DsaTest, Pkcs11DsaTest,
::testing::ValuesIn(kDsaWycheproofVectors));
} // namespace nss_test

View file

@ -0,0 +1,237 @@
/* 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 <algorithm>
#include <memory>
#include "nss.h"
#include "pk11pub.h"
#include "prerror.h"
#include "cpputil.h"
#include "json_reader.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "testvectors_base/test-structs.h"
namespace nss_test {
class Pkcs11EcdhTest : public ::testing::Test {
protected:
void Derive(const std::string& curve, const EcdhTestVector& vec) {
std::cout << "Run test " << vec.id << std::endl;
SECItem spki_item = {siBuffer, toUcharPtr(vec.public_key.data()),
static_cast<unsigned int>(vec.public_key.size())};
ScopedCERTSubjectPublicKeyInfo cert_spki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
if (vec.valid) {
ASSERT_TRUE(!!cert_spki);
} else if (!cert_spki) {
ASSERT_TRUE(vec.invalid_asn);
return;
}
ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get()));
if (vec.valid) {
ASSERT_TRUE(!!pub_key);
} else if (!pub_key) {
return;
}
ScopedSECKEYPrivateKey priv_key = ImportPrivateKey(curve, vec);
if (vec.valid) {
ASSERT_TRUE(priv_key);
} else if (!priv_key) {
return;
}
ScopedPK11SymKey sym_key(
PK11_PubDeriveWithKDF(priv_key.get(), pub_key.get(), false, nullptr,
nullptr, CKM_ECDH1_DERIVE, CKM_SHA512_HMAC,
CKA_DERIVE, 0, CKD_NULL, nullptr, nullptr));
if (vec.valid) {
ASSERT_TRUE(!!sym_key);
SECStatus rv = PK11_ExtractKeyValue(sym_key.get());
EXPECT_EQ(SECSuccess, rv);
SECItem expect_item = {siBuffer, toUcharPtr(vec.secret.data()),
static_cast<unsigned int>(vec.secret.size())};
SECItem* derived_key = PK11_GetKeyData(sym_key.get());
EXPECT_EQ(0, SECITEM_CompareItem(derived_key, &expect_item));
} else if (!vec.invalid_asn) {
// Invalid encodings could produce an output if we get here, so only
// check when the encoding is valid.
ASSERT_FALSE(!!sym_key);
}
};
static void ReadTestAttr(EcdhTestVector& t, const std::string& n,
JsonReader& r) {
if (n == "public") {
t.public_key = r.ReadHex();
} else if (n == "private") {
t.private_key = r.ReadHex();
} else if (n == "shared") {
t.secret = r.ReadHex();
} else {
FAIL() << "unsupported test case field: " << n;
}
}
void RunGroup(JsonReader& r) {
std::vector<EcdhTestVector> tests;
std::string curve;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "curve") {
curve = r.ReadString();
} else if (n == "encoding") {
ASSERT_EQ("asn", r.ReadString());
} else if (n == "type") {
ASSERT_EQ("EcdhTest", r.ReadString());
} else if (n == "tests") {
WycheproofReadTests(r, &tests, ReadTestAttr, false,
[](EcdhTestVector& t, const std::string&,
const std::vector<std::string>& flags) {
t.invalid_asn =
std::find(flags.begin(), flags.end(),
"InvalidAsn") != flags.end();
});
} else {
FAIL() << "unknown group label: " << n;
}
}
for (auto& t : tests) {
Derive(curve, t);
}
}
void Run(const std::string& file) {
WycheproofHeader(file, "ECDH", "ecdh_test_schema.json",
[this](JsonReader& r) { RunGroup(r); });
}
private:
void OidForCurve(const std::string& curve, std::vector<uint8_t>* der) {
SECOidTag tag;
if (curve == "secp256r1") {
tag = SEC_OID_SECG_EC_SECP256R1;
} else if (curve == "secp384r1") {
tag = SEC_OID_SECG_EC_SECP384R1;
} else if (curve == "secp521r1") {
tag = SEC_OID_SECG_EC_SECP521R1;
} else {
FAIL() << "unknown curve: " << curve;
}
SECOidData* oid_data = SECOID_FindOIDByTag(tag);
ASSERT_TRUE(oid_data);
der->push_back(SEC_ASN1_OBJECT_ID);
der->push_back(oid_data->oid.len);
der->insert(der->end(), oid_data->oid.data,
oid_data->oid.data + oid_data->oid.len);
}
// Construct a garbage public value for the given curve.
// NSS needs a value for this, but it doesn't care what it is.
void PublicValue(const std::string& curve, std::vector<uint8_t>* der) {
size_t len;
if (curve == "secp256r1") {
len = 32;
} else if (curve == "secp384r1") {
len = 48;
} else if (curve == "secp521r1") {
len = 64;
} else {
FAIL() << "unknown curve: " << curve;
}
der->push_back(0x04);
for (size_t i = 0; i < len * 2; ++i) {
der->push_back(0x00);
}
}
void InsertLength(std::vector<uint8_t>* der, size_t offset) {
size_t len = der->size() - offset;
ASSERT_GT(256u, len) << "unsupported length for DER";
if (len > 127) {
der->insert(der->begin() + offset, 0x81);
offset++;
}
der->insert(der->begin() + offset, static_cast<uint8_t>(len));
}
// A very hacking PKCS#8 encoder that is sufficient to dupe NSS into
// thinking that it is a valid EC private key.
std::vector<uint8_t> BuildDerPrivateKey(const std::string& curve,
const EcdhTestVector& vec) {
std::vector<uint8_t> der;
std::vector<size_t> length_inserts;
der.push_back(0x30);
length_inserts.push_back(der.size());
der.insert(der.end(), {0x02, 0x01, 0x00, 0x30});
size_t oid_length_insert = der.size();
der.insert(der.end(),
{0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01});
OidForCurve(curve, &der);
InsertLength(&der, oid_length_insert);
der.push_back(0x04);
length_inserts.push_back(der.size());
der.push_back(0x30);
length_inserts.push_back(der.size());
der.insert(der.end(), {0x02, 0x01, 0x01, 0x04});
der.push_back(vec.private_key.size());
der.insert(der.end(), vec.private_key.begin(), vec.private_key.end());
der.push_back(0xa1);
length_inserts.push_back(der.size());
der.push_back(0x03);
length_inserts.push_back(der.size());
der.push_back(0x00);
PublicValue(curve, &der);
for (auto i = length_inserts.rbegin(); i != length_inserts.rend(); ++i) {
InsertLength(&der, *i);
}
return der;
}
ScopedSECKEYPrivateKey ImportPrivateKey(const std::string& curve,
const EcdhTestVector& vec) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
EXPECT_TRUE(slot);
if (!slot) {
return nullptr;
}
std::vector<uint8_t> der = BuildDerPrivateKey(curve, vec);
SECItem der_item = {siBuffer, const_cast<uint8_t*>(der.data()),
static_cast<unsigned int>(der.size())};
SECKEYPrivateKey* key = nullptr;
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot.get(), &der_item, nullptr, nullptr, false, true, KU_KEY_AGREEMENT,
&key, nullptr);
if (vec.valid) {
EXPECT_EQ(SECSuccess, rv)
<< "unable to load private key DER for test " << vec.id << ": "
<< PORT_ErrorToString(PORT_GetError());
}
return ScopedSECKEYPrivateKey(key);
}
};
TEST_F(Pkcs11EcdhTest, P256) { Run("ecdh_secp256r1"); }
TEST_F(Pkcs11EcdhTest, P384) { Run("ecdh_secp384r1"); }
TEST_F(Pkcs11EcdhTest, P521) { Run("ecdh_secp521r1"); }
} // namespace nss_test

View file

@ -6,19 +6,43 @@
#include "nss.h"
#include "pk11pub.h"
#include "sechash.h"
#include "cryptohi.h"
#include "cpputil.h"
#include "gtest/gtest.h"
#include "json_reader.h"
#include "nss_scoped_ptrs.h"
#include "testvectors/curve25519-vectors.h"
#include "pk11_ecdsa_vectors.h"
#include "pk11_signature_test.h"
#include "pk11_keygen.h"
namespace nss_test {
CK_MECHANISM_TYPE
EcHashToComboMech(SECOidTag hash) {
switch (hash) {
case SEC_OID_SHA1:
return CKM_ECDSA_SHA1;
case SEC_OID_SHA224:
return CKM_ECDSA_SHA224;
case SEC_OID_SHA256:
return CKM_ECDSA_SHA256;
case SEC_OID_SHA384:
return CKM_ECDSA_SHA384;
case SEC_OID_SHA512:
return CKM_ECDSA_SHA512;
default:
break;
}
return CKM_INVALID_MECHANISM;
}
class Pkcs11EcdsaTestBase : public Pk11SignatureTest {
protected:
Pkcs11EcdsaTestBase(SECOidTag hash_oid)
: Pk11SignatureTest(CKM_ECDSA, hash_oid) {}
: Pk11SignatureTest(CKM_ECDSA, hash_oid, EcHashToComboMech(hash_oid)) {}
};
struct Pkcs11EcdsaTestParams {
@ -39,6 +63,10 @@ TEST_P(Pkcs11EcdsaTest, SignAndVerify) {
SignAndVerify(GetParam().sig_params_);
}
TEST_P(Pkcs11EcdsaTest, ImportExport) {
ImportExport(GetParam().sig_params_.pkcs8_);
}
static const Pkcs11EcdsaTestParams kEcdsaVectors[] = {
{SEC_OID_SHA256,
{DataBuffer(kP256Pkcs8, sizeof(kP256Pkcs8)),
@ -61,8 +89,8 @@ static const Pkcs11EcdsaTestParams kEcdsaVectors[] = {
DataBuffer(kP521Data, sizeof(kP521Data)),
DataBuffer(kP521Signature, sizeof(kP521Signature))}}};
INSTANTIATE_TEST_CASE_P(EcdsaSignVerify, Pkcs11EcdsaTest,
::testing::ValuesIn(kEcdsaVectors));
INSTANTIATE_TEST_SUITE_P(EcdsaSignVerify, Pkcs11EcdsaTest,
::testing::ValuesIn(kEcdsaVectors));
class Pkcs11EcdsaSha256Test : public Pkcs11EcdsaTestBase {
public:
@ -84,7 +112,8 @@ TEST_F(Pkcs11EcdsaSha256Test, ImportOnlyAlgorithmParams) {
sizeof(kP256Pkcs8OnlyAlgorithmParams));
DataBuffer data(kP256Data, sizeof(kP256Data));
DataBuffer sig;
EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig));
DataBuffer sig2;
EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig, &sig2));
};
// Importing a private key in PKCS#8 format must succeed when the outer AlgID
@ -95,7 +124,8 @@ TEST_F(Pkcs11EcdsaSha256Test, ImportMatchingCurveOIDAndAlgorithmParams) {
sizeof(kP256Pkcs8MatchingCurveOIDAndAlgorithmParams));
DataBuffer data(kP256Data, sizeof(kP256Data));
DataBuffer sig;
EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig));
DataBuffer sig2;
EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig, &sig2));
};
// Importing a private key in PKCS#8 format must succeed when the outer AlgID
@ -106,7 +136,8 @@ TEST_F(Pkcs11EcdsaSha256Test, ImportDissimilarCurveOIDAndAlgorithmParams) {
sizeof(kP256Pkcs8DissimilarCurveOIDAndAlgorithmParams));
DataBuffer data(kP256Data, sizeof(kP256Data));
DataBuffer sig;
EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig));
DataBuffer sig2;
EXPECT_TRUE(ImportPrivateKeyAndSignHashedData(k, data, &sig, &sig2));
};
// Importing a private key in PKCS#8 format must fail when the outer ASN.1
@ -172,4 +203,127 @@ TEST_F(Pkcs11EcdsaSha256Test, ImportSpkiPointNotOnCurve) {
EXPECT_EQ(handle, static_cast<decltype(handle)>(CK_INVALID_HANDLE));
}
class Pkcs11EcdsaWycheproofTest : public ::testing::Test {
protected:
void Run(const std::string& name) {
WycheproofHeader(name, "ECDSA", "ecdsa_verify_schema.json",
[this](JsonReader& r) { RunGroup(r); });
}
private:
void RunGroup(JsonReader& r) {
std::vector<EcdsaTestVector> tests;
std::vector<uint8_t> public_key;
SECOidTag hash_oid = SEC_OID_UNKNOWN;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "key" || n == "keyPem") {
r.SkipValue();
} else if (n == "keyDer") {
public_key = r.ReadHex();
} else if (n == "sha") {
hash_oid = r.ReadHash();
} else if (n == "type") {
ASSERT_EQ("EcdsaVerify", r.ReadString());
} else if (n == "tests") {
WycheproofReadTests(r, &tests, ReadTestAttr);
} else {
FAIL() << "unknown label in group: " << n;
}
}
for (auto& t : tests) {
std::cout << "Running test " << t.id << std::endl;
t.public_key = public_key;
t.hash_oid = hash_oid;
Derive(t);
}
}
static void ReadTestAttr(EcdsaTestVector& t, const std::string& n,
JsonReader& r) {
if (n == "msg") {
t.msg = r.ReadHex();
} else if (n == "sig") {
t.sig = r.ReadHex();
} else {
FAIL() << "unknown test key: " << n;
}
}
void Derive(const EcdsaTestVector& vec) {
SECItem spki_item = {siBuffer, toUcharPtr(vec.public_key.data()),
static_cast<unsigned int>(vec.public_key.size())};
SECItem sig_item = {siBuffer, toUcharPtr(vec.sig.data()),
static_cast<unsigned int>(vec.sig.size())};
DataBuffer hash;
hash.Allocate(static_cast<size_t>(HASH_ResultLenByOidTag(vec.hash_oid)));
SECStatus rv = PK11_HashBuf(vec.hash_oid, toUcharPtr(hash.data()),
toUcharPtr(vec.msg.data()), vec.msg.size());
ASSERT_EQ(rv, SECSuccess);
SECItem hash_item = {siBuffer, toUcharPtr(hash.data()),
static_cast<unsigned int>(hash.len())};
ScopedCERTSubjectPublicKeyInfo cert_spki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
ASSERT_TRUE(cert_spki);
ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get()));
ASSERT_TRUE(pub_key);
rv = VFY_VerifyDigestDirect(&hash_item, pub_key.get(), &sig_item,
SEC_OID_ANSIX962_EC_PUBLIC_KEY, vec.hash_oid,
nullptr);
EXPECT_EQ(rv, vec.valid ? SECSuccess : SECFailure);
};
};
TEST_F(Pkcs11EcdsaWycheproofTest, P256) { Run("ecdsa_secp256r1_sha256"); }
TEST_F(Pkcs11EcdsaWycheproofTest, P256Sha512) { Run("ecdsa_secp256r1_sha512"); }
TEST_F(Pkcs11EcdsaWycheproofTest, P384) { Run("ecdsa_secp384r1_sha384"); }
TEST_F(Pkcs11EcdsaWycheproofTest, P384Sha512) { Run("ecdsa_secp384r1_sha512"); }
TEST_F(Pkcs11EcdsaWycheproofTest, P521) { Run("ecdsa_secp521r1_sha512"); }
class Pkcs11EcdsaRoundtripTest
: public Pkcs11EcdsaTestBase,
public ::testing::WithParamInterface<SECOidTag> {
public:
Pkcs11EcdsaRoundtripTest() : Pkcs11EcdsaTestBase(SEC_OID_SHA256) {}
protected:
void GenerateExportImportSignVerify(SECOidTag tag) {
Pkcs11KeyPairGenerator generator(CKM_EC_KEY_PAIR_GEN, tag);
ScopedSECKEYPrivateKey priv;
ScopedSECKEYPublicKey pub;
generator.GenerateKey(&priv, &pub, false);
DataBuffer exported;
ExportPrivateKey(&priv, exported);
if (tag != SEC_OID_CURVE25519) {
DataBuffer sig;
DataBuffer sig2;
DataBuffer data(kP256Data, sizeof(kP256Data));
ASSERT_TRUE(
ImportPrivateKeyAndSignHashedData(exported, data, &sig, &sig2));
Verify(pub, data, sig);
}
}
};
TEST_P(Pkcs11EcdsaRoundtripTest, GenerateExportImportSignVerify) {
GenerateExportImportSignVerify(GetParam());
}
INSTANTIATE_TEST_SUITE_P(Pkcs11EcdsaRoundtripTest, Pkcs11EcdsaRoundtripTest,
::testing::Values(SEC_OID_SECG_EC_SECP256R1,
SEC_OID_SECG_EC_SECP384R1,
SEC_OID_SECG_EC_SECP521R1,
SEC_OID_CURVE25519));
} // namespace nss_test

View file

@ -72,10 +72,12 @@ class EncryptDeriveTest
return CKM_CAMELLIA_ECB_ENCRYPT_DATA;
case CKM_CAMELLIA_CBC:
return CKM_CAMELLIA_CBC_ENCRYPT_DATA;
#ifndef NSS_DISABLE_DEPRECATED_SEED
case CKM_SEED_ECB:
return CKM_SEED_ECB_ENCRYPT_DATA;
case CKM_SEED_CBC:
return CKM_SEED_CBC_ENCRYPT_DATA;
#endif
default:
ADD_FAILURE() << "Unknown mechanism";
break;
@ -93,7 +95,9 @@ class EncryptDeriveTest
case CKM_DES3_ECB:
case CKM_AES_ECB:
case CKM_CAMELLIA_ECB:
#ifndef NSS_DISABLE_DEPRECATED_SEED
case CKM_SEED_ECB:
#endif
string_data.pData = toUcharPtr(kInput);
string_data.ulLen = keysize();
param.data = reinterpret_cast<uint8_t*>(&string_data);
@ -110,7 +114,9 @@ class EncryptDeriveTest
case CKM_AES_CBC:
case CKM_CAMELLIA_CBC:
#ifndef NSS_DISABLE_DEPRECATED_SEED
case CKM_SEED_CBC:
#endif
aes_data.pData = toUcharPtr(kInput);
aes_data.length = keysize();
PORT_Memcpy(aes_data.iv, kIv, keysize());
@ -132,14 +138,18 @@ class EncryptDeriveTest
case CKM_DES3_ECB:
case CKM_AES_ECB:
case CKM_CAMELLIA_ECB:
#ifndef NSS_DISABLE_DEPRECATED_SEED
case CKM_SEED_ECB:
#endif
// No parameter needed here.
break;
case CKM_DES3_CBC:
case CKM_AES_CBC:
case CKM_CAMELLIA_CBC:
#ifndef NSS_DISABLE_DEPRECATED_SEED
case CKM_SEED_CBC:
#endif
param.data = toUcharPtr(kIv);
param.len = keysize();
break;
@ -185,12 +195,22 @@ class EncryptDeriveTest
TEST_P(EncryptDeriveTest, Test) { TestEncryptDerive(); }
static const CK_MECHANISM_TYPE kEncryptDeriveMechanisms[] = {
CKM_DES3_ECB, CKM_DES3_CBC, CKM_AES_ECB, CKM_AES_ECB, CKM_AES_CBC,
CKM_CAMELLIA_ECB, CKM_CAMELLIA_CBC, CKM_SEED_ECB, CKM_SEED_CBC};
static const CK_MECHANISM_TYPE kEncryptDeriveMechanisms[] = {CKM_DES3_ECB,
CKM_DES3_CBC,
CKM_AES_ECB,
CKM_AES_ECB,
CKM_AES_CBC,
CKM_CAMELLIA_ECB,
CKM_CAMELLIA_CBC
#ifndef NSS_DISABLE_DEPRECATED_SEED
,
CKM_SEED_ECB,
CKM_SEED_CBC
#endif
};
INSTANTIATE_TEST_CASE_P(EncryptDeriveTests, EncryptDeriveTest,
::testing::ValuesIn(kEncryptDeriveMechanisms));
INSTANTIATE_TEST_SUITE_P(EncryptDeriveTests, EncryptDeriveTest,
::testing::ValuesIn(kEncryptDeriveMechanisms));
// This class handles the case where 3DES takes a 192-bit key
// where all 24 octets will be used.
@ -204,7 +224,7 @@ TEST_P(EncryptDerive3Test, Test) { TestEncryptDerive(); }
static const CK_MECHANISM_TYPE kDES3EncryptDeriveMechanisms[] = {CKM_DES3_ECB,
CKM_DES3_CBC};
INSTANTIATE_TEST_CASE_P(Encrypt3DeriveTests, EncryptDerive3Test,
::testing::ValuesIn(kDES3EncryptDeriveMechanisms));
INSTANTIATE_TEST_SUITE_P(Encrypt3DeriveTests, EncryptDerive3Test,
::testing::ValuesIn(kDES3EncryptDeriveMechanisms));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=4 et sw=4 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -141,11 +142,11 @@ class PK11FindCertsTestBase : public ::testing::Test {
mod_spec.append(test_name);
mod_spec.append("'");
m_slot = SECMOD_OpenUserDB(mod_spec.c_str());
ASSERT_NE(m_slot, nullptr);
ASSERT_NE(nullptr, m_slot);
}
virtual void TearDown() {
ASSERT_EQ(SECMOD_CloseUserDB(m_slot), SECSuccess);
ASSERT_EQ(SECSuccess, SECMOD_CloseUserDB(m_slot));
PK11_FreeSlot(m_slot);
std::string test_cert_db_path(test_cert_db_dir_.GetPath());
ASSERT_EQ(0, unlink((test_cert_db_path + "/cert9.db").c_str()));
@ -158,6 +159,41 @@ class PK11FindCertsTestBase : public ::testing::Test {
class PK11FindRawCertsBySubjectTest : public PK11FindCertsTestBase {};
TEST_F(PK11FindCertsTestBase, CertAddListWithData) {
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
ASSERT_TRUE(slot);
SECItem cert1_item = {siBuffer, const_cast<uint8_t*>(kTestCert1DER.data()),
(unsigned int)kTestCert1DER.size()};
SECItem cert2_item = {siBuffer, const_cast<uint8_t*>(kTestCert2DER.data()),
(unsigned int)kTestCert2DER.size()};
// Make certificates. ScopedCERTCertList will own.
ScopedCERTCertList list(CERT_NewCertList());
ASSERT_TRUE(list);
CERTCertificate* cert1 = CERT_NewTempCertificate(
CERT_GetDefaultCertDB(), &cert1_item, nullptr, false, false);
CERTCertificate* cert2 = CERT_NewTempCertificate(
CERT_GetDefaultCertDB(), &cert2_item, nullptr, false, false);
ASSERT_NE(nullptr, cert1);
ASSERT_NE(nullptr, cert2);
ASSERT_NE(cert1, cert2);
SECStatus rv = CERT_AddCertToListHeadWithData(list.get(), cert1, cert1);
EXPECT_EQ(SECSuccess, rv);
rv = CERT_AddCertToListTailWithData(list.get(), cert2, cert2);
EXPECT_EQ(SECSuccess, rv);
CERTCertListNode* node = CERT_LIST_HEAD(list.get());
ASSERT_NE(nullptr, node);
EXPECT_EQ(node->cert, cert1);
EXPECT_EQ(node->appData, cert1);
node = CERT_LIST_TAIL(list.get());
ASSERT_NE(nullptr, node);
EXPECT_EQ(node->cert, cert2);
EXPECT_EQ(node->appData, cert2);
}
// If we don't have any certificates, we shouldn't get any when we search for
// them.
TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsImportedNoCertsFound) {
@ -167,8 +203,8 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsImportedNoCertsFound) {
CERTCertificateList* certificates = nullptr;
SECStatus rv =
PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates);
EXPECT_EQ(rv, SECSuccess);
EXPECT_EQ(certificates, nullptr);
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(nullptr, certificates);
}
// If we have one certificate but it has an unrelated subject DN, we shouldn't
@ -178,9 +214,9 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestOneCertImportedNoCertsFound) {
SECItem cert_item = {siBuffer,
const_cast<unsigned char*>(kUnrelatedTestCertDER.data()),
(unsigned int)kUnrelatedTestCertDER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false),
SECSuccess);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false));
SECItem subject_item = {
siBuffer, const_cast<unsigned char*>(kTestCertSubjectDER.data()),
@ -188,8 +224,8 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestOneCertImportedNoCertsFound) {
CERTCertificateList* certificates = nullptr;
SECStatus rv =
PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates);
EXPECT_EQ(rv, SECSuccess);
EXPECT_EQ(certificates, nullptr);
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(nullptr, certificates);
}
TEST_F(PK11FindRawCertsBySubjectTest, TestMultipleMatchingCertsFound) {
@ -197,23 +233,23 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestMultipleMatchingCertsFound) {
SECItem cert1_item = {siBuffer,
const_cast<unsigned char*>(kTestCert1DER.data()),
(unsigned int)kTestCert1DER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false),
SECSuccess);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false));
char cert2_nickname[] = "Test Cert 2";
SECItem cert2_item = {siBuffer,
const_cast<unsigned char*>(kTestCert2DER.data()),
(unsigned int)kTestCert2DER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE,
cert2_nickname, false),
SECSuccess);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE,
cert2_nickname, false));
char unrelated_cert_nickname[] = "Unrelated Test Cert";
SECItem unrelated_cert_item = {
siBuffer, const_cast<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);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &unrelated_cert_item, CK_INVALID_HANDLE,
unrelated_cert_nickname, false));
CERTCertificateList* certificates = nullptr;
SECItem subject_item = {
@ -221,10 +257,10 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestMultipleMatchingCertsFound) {
(unsigned int)kTestCertSubjectDER.size()};
SECStatus rv =
PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates);
EXPECT_EQ(rv, SECSuccess);
ASSERT_NE(certificates, nullptr);
EXPECT_EQ(SECSuccess, rv);
ASSERT_NE(nullptr, certificates);
ScopedCERTCertificateList scoped_certificates(certificates);
ASSERT_EQ(scoped_certificates->len, 2);
ASSERT_EQ(2, scoped_certificates->len);
std::vector<uint8_t> found_cert1(
scoped_certificates->certs[0].data,
@ -244,9 +280,9 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsOnInternalSlots) {
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);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false));
SECItem subject_item = {
siBuffer, const_cast<unsigned char*>(kTestCertSubjectDER.data()),
@ -255,15 +291,15 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestNoCertsOnInternalSlots) {
ScopedPK11SlotInfo internal_key_slot(PK11_GetInternalKeySlot());
SECStatus rv = PK11_FindRawCertsWithSubject(
internal_key_slot.get(), &subject_item, &internal_key_slot_certificates);
EXPECT_EQ(rv, SECSuccess);
EXPECT_EQ(internal_key_slot_certificates, nullptr);
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(nullptr, internal_key_slot_certificates);
CERTCertificateList* internal_slot_certificates = nullptr;
ScopedPK11SlotInfo internal_slot(PK11_GetInternalSlot());
rv = PK11_FindRawCertsWithSubject(internal_slot.get(), &subject_item,
&internal_slot_certificates);
EXPECT_EQ(rv, SECSuccess);
EXPECT_EQ(internal_slot_certificates, nullptr);
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(nullptr, internal_slot_certificates);
}
// issuer:test cert
@ -304,10 +340,9 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestFindEmptySubject) {
SECItem empty_subject_cert_item = {
siBuffer, const_cast<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);
ASSERT_EQ(SECSuccess, PK11_ImportDERCert(m_slot, &empty_subject_cert_item,
CK_INVALID_HANDLE,
empty_subject_cert_nickname, false));
SECItem subject_item = {siBuffer,
const_cast<unsigned char*>(kEmptySubjectDER.data()),
@ -315,15 +350,15 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestFindEmptySubject) {
CERTCertificateList* certificates = nullptr;
SECStatus rv =
PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates);
EXPECT_EQ(rv, SECSuccess);
ASSERT_NE(certificates, nullptr);
EXPECT_EQ(SECSuccess, rv);
ASSERT_NE(nullptr, certificates);
ScopedCERTCertificateList scoped_certificates(certificates);
ASSERT_EQ(scoped_certificates->len, 1);
ASSERT_EQ(1, scoped_certificates->len);
std::vector<uint8_t> found_cert(
scoped_certificates->certs[0].data,
scoped_certificates->certs[0].data + scoped_certificates->certs[0].len);
EXPECT_EQ(found_cert, kEmptySubjectCertDER);
EXPECT_EQ(kEmptySubjectCertDER, found_cert);
}
// Searching for a zero-length subject doesn't make sense (the minimum subject
@ -334,16 +369,16 @@ TEST_F(PK11FindRawCertsBySubjectTest, TestSearchForNullSubject) {
SECItem cert1_item = {siBuffer,
const_cast<unsigned char*>(kTestCert1DER.data()),
(unsigned int)kTestCert1DER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false),
SECSuccess);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false));
SECItem subject_item = {siBuffer, nullptr, 0};
CERTCertificateList* certificates = nullptr;
SECStatus rv =
PK11_FindRawCertsWithSubject(m_slot, &subject_item, &certificates);
EXPECT_EQ(rv, SECSuccess);
EXPECT_EQ(certificates, nullptr);
EXPECT_EQ(SECSuccess, rv);
EXPECT_EQ(nullptr, certificates);
}
class PK11GetCertsMatchingPrivateKeyTest : public PK11FindCertsTestBase {};
@ -403,15 +438,15 @@ const std::vector<uint8_t> kTestCertWithOtherKeyDER = {
// 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()),
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);
ASSERT_EQ(SECSuccess, PK11_ImportDERPrivateKeyInfoAndReturnKey(
m_slot, &private_key_info, nullptr, nullptr, false,
false, KU_ALL, &priv_key, nullptr));
ASSERT_NE(nullptr, priv_key);
ScopedSECKEYPrivateKey scoped_priv_key(priv_key);
ScopedCERTCertList certs(
PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get()));
@ -421,24 +456,24 @@ TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestNoCertsAtAll) {
// 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()),
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);
ASSERT_EQ(SECSuccess, PK11_ImportDERPrivateKeyInfoAndReturnKey(
m_slot, &private_key_info, nullptr, nullptr, false,
false, KU_ALL, &priv_key, nullptr));
ASSERT_NE(nullptr, priv_key);
ScopedSECKEYPrivateKey scoped_priv_key(priv_key);
char cert_nickname[] = "Test Cert With Other Key";
SECItem cert_item = {
siBuffer, const_cast<unsigned char*>(kTestCertWithOtherKeyDER.data()),
(unsigned int)kTestCertWithOtherKeyDER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false),
SECSuccess);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false));
ScopedCERTCertList certs(
PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get()));
@ -448,8 +483,8 @@ TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestNoCertsForKey) {
void CheckCertListForSubjects(
ScopedCERTCertList& list,
const std::vector<const char*>& expected_subjects) {
ASSERT_NE(list.get(), nullptr);
ASSERT_NE(expected_subjects.size(), 0ul);
ASSERT_NE(nullptr, list.get());
ASSERT_NE(0ul, expected_subjects.size());
for (const auto& expected_subject : expected_subjects) {
size_t list_length = 0;
bool found = false;
@ -462,39 +497,39 @@ void CheckCertListForSubjects(
}
}
ASSERT_TRUE(found);
ASSERT_EQ(list_length, expected_subjects.size());
ASSERT_EQ(expected_subjects.size(), list_length);
}
}
// 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()),
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);
ASSERT_EQ(SECSuccess, PK11_ImportDERPrivateKeyInfoAndReturnKey(
m_slot, &private_key_info, nullptr, nullptr, false,
false, KU_ALL, &priv_key, nullptr));
ASSERT_NE(nullptr, priv_key);
ScopedSECKEYPrivateKey scoped_priv_key(priv_key);
char cert1_nickname[] = "Test Cert 1";
SECItem cert1_item = {siBuffer,
const_cast<unsigned char*>(kTestCert1DER.data()),
(unsigned int)kTestCert1DER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false),
SECSuccess);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false));
char cert_nickname[] = "Test Cert With Other Key";
SECItem cert_item = {
siBuffer, const_cast<unsigned char*>(kTestCertWithOtherKeyDER.data()),
(unsigned int)kTestCertWithOtherKeyDER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false),
SECSuccess);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false));
ScopedCERTCertList certs(
PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get()));
@ -504,43 +539,92 @@ TEST_F(PK11GetCertsMatchingPrivateKeyTest, TestOneCertForKey) {
// 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()),
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);
ASSERT_EQ(SECSuccess, PK11_ImportDERPrivateKeyInfoAndReturnKey(
m_slot, &private_key_info, nullptr, nullptr, false,
false, KU_ALL, &priv_key, nullptr));
ASSERT_NE(nullptr, priv_key);
ScopedSECKEYPrivateKey scoped_priv_key(priv_key);
char cert1_nickname[] = "Test Cert 1";
SECItem cert1_item = {siBuffer,
const_cast<unsigned char*>(kTestCert1DER.data()),
(unsigned int)kTestCert1DER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false),
SECSuccess);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert1_item, CK_INVALID_HANDLE,
cert1_nickname, false));
char cert2_nickname[] = "Test Cert 2 (same key, different subject)";
SECItem cert2_item = {
siBuffer, const_cast<unsigned char*>(kUnrelatedTestCertDER.data()),
(unsigned int)kUnrelatedTestCertDER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE,
cert2_nickname, false),
SECSuccess);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert2_item, CK_INVALID_HANDLE,
cert2_nickname, false));
char cert_nickname[] = "Test Cert With Other Key";
SECItem cert_item = {
siBuffer, const_cast<unsigned char*>(kTestCertWithOtherKeyDER.data()),
(unsigned int)kTestCertWithOtherKeyDER.size()};
ASSERT_EQ(PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false),
SECSuccess);
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false));
ScopedCERTCertList certs(
PK11_GetCertsMatchingPrivateKey(scoped_priv_key.get()));
CheckCertListForSubjects(certs, {"CN=test cert", "CN=unrelated subject DN"});
}
class PK11FindEncodedCertInSlotTest : public PK11FindCertsTestBase {};
TEST_F(PK11FindEncodedCertInSlotTest, TestFindEncodedCert) {
char cert_nickname[] = "Test Cert";
SECItem cert_item = {siBuffer,
const_cast<unsigned char*>(kTestCert1DER.data()),
(unsigned int)kTestCert1DER.size()};
ASSERT_EQ(SECSuccess,
PK11_ImportDERCert(m_slot, &cert_item, CK_INVALID_HANDLE,
cert_nickname, false));
// This certificate was just imported, so finding it by its encoded value
// should succeed.
CK_OBJECT_HANDLE cert_handle_in_slot =
PK11_FindEncodedCertInSlot(m_slot, &cert_item, nullptr);
// CK_INVALID_HANDLE is #defined to be the literal 0, which the compiler
// interprets as a signed value, which then causes a warning-as-an-error
// about comparing values of different signs.
ASSERT_NE(static_cast<CK_ULONG>(CK_INVALID_HANDLE), cert_handle_in_slot);
// The certificate should not exist on the internal slot, so this should
// return CK_INVALID_HANDLE.
ScopedPK11SlotInfo internal_slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, internal_slot);
CK_OBJECT_HANDLE cert_handle_in_internal_slot =
PK11_FindEncodedCertInSlot(internal_slot.get(), &cert_item, nullptr);
ASSERT_EQ(static_cast<CK_ULONG>(CK_INVALID_HANDLE),
cert_handle_in_internal_slot);
// The certificate should not exist on the internal key slot, so this should
// return CK_INVALID_HANDLE.
ScopedPK11SlotInfo internal_key_slot(PK11_GetInternalKeySlot());
ASSERT_NE(nullptr, internal_key_slot);
CK_OBJECT_HANDLE cert_handle_in_internal_key_slot =
PK11_FindEncodedCertInSlot(internal_key_slot.get(), &cert_item, nullptr);
ASSERT_EQ(static_cast<CK_ULONG>(CK_INVALID_HANDLE),
cert_handle_in_internal_key_slot);
// This certificate hasn't been imported to any token, so looking for it
// should return CK_INVALID_HANDLE.
SECItem unknown_cert_item = {siBuffer,
const_cast<unsigned char*>(kTestCert2DER.data()),
(unsigned int)kTestCert2DER.size()};
CK_OBJECT_HANDLE unknown_cert_handle_in_slot =
PK11_FindEncodedCertInSlot(m_slot, &unknown_cert_item, nullptr);
ASSERT_EQ(static_cast<CK_ULONG>(CK_INVALID_HANDLE),
unknown_cert_handle_in_slot);
}
} // namespace nss_test

View file

@ -11,9 +11,11 @@
'target_name': 'pk11_gtest',
'type': 'executable',
'sources': [
'json_reader.cc',
'pk11_aes_cmac_unittest.cc',
'pk11_aes_gcm_unittest.cc',
'pk11_aeskeywrap_unittest.cc',
'pk11_aeskeywrapkwp_unittest.cc',
'pk11_aeskeywrappad_unittest.cc',
'pk11_cbc_unittest.cc',
'pk11_chacha20poly1305_unittest.cc',
@ -21,19 +23,29 @@
'pk11_curve25519_unittest.cc',
'pk11_der_private_key_import_unittest.cc',
'pk11_des_unittest.cc',
'pk11_dsa_unittest.cc',
'pk11_ecdsa_unittest.cc',
'pk11_ecdh_unittest.cc',
'pk11_encrypt_derive_unittest.cc',
'pk11_find_certs_unittest.cc',
'pk11_hkdf_unittest.cc',
'pk11_hmac_unittest.cc',
'pk11_hpke_unittest.cc',
'pk11_ike_unittest.cc',
'pk11_import_unittest.cc',
'pk11_kbkdf.cc',
'pk11_keygen.cc',
'pk11_key_unittest.cc',
'pk11_module_unittest.cc',
'pk11_pbkdf2_unittest.cc',
'pk11_prf_unittest.cc',
'pk11_prng_unittest.cc',
'pk11_rsaencrypt_unittest.cc',
'pk11_rsaoaep_unittest.cc',
'pk11_rsapkcs1_unittest.cc',
'pk11_rsapss_unittest.cc',
'pk11_seed_cbc_unittest.cc',
'pk11_signature_test.cc',
'<(DEPTH)/gtests/common/gtests.cc'
],
'dependencies': [

View file

@ -0,0 +1,199 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <memory>
#include "blapi.h"
#include "gtest/gtest.h"
#include "nss.h"
#include "nss_scoped_ptrs.h"
#include "pk11pub.h"
#include "secerr.h"
#include "sechash.h"
#include "util.h"
#include "testvectors/hkdf-sha1-vectors.h"
#include "testvectors/hkdf-sha256-vectors.h"
#include "testvectors/hkdf-sha384-vectors.h"
#include "testvectors/hkdf-sha512-vectors.h"
namespace nss_test {
enum class HkdfTestType {
legacy, /* CKM_NSS_HKDF_SHA... */
derive, /* CKM_HKDF_DERIVE, ikm as secret key, salt as data. */
deriveDataKey, /* CKM_HKDF_DERIVE, ikm as data, salt as data. */
saltDerive, /* CKM_HKDF_DERIVE, [ikm, salt] as secret key, salt as key. */
saltDeriveDataKey, /* CKM_HKDF_DERIVE, [ikm, salt] as data, salt as key. */
hkdfData /* CKM_HKDF_DATA, ikm as data, salt as data. */
};
static const HkdfTestType kHkdfTestTypesAll[] = {
HkdfTestType::legacy,
HkdfTestType::derive,
HkdfTestType::deriveDataKey,
HkdfTestType::saltDerive,
HkdfTestType::saltDeriveDataKey,
HkdfTestType::hkdfData,
};
class Pkcs11HkdfTest
: public ::testing::TestWithParam<
std::tuple<HkdfTestVector, HkdfTestType, CK_MECHANISM_TYPE>> {
protected:
CK_MECHANISM_TYPE Pk11MechToVendorMech(CK_MECHANISM_TYPE pk11_mech) {
switch (pk11_mech) {
case CKM_SHA_1:
return CKM_NSS_HKDF_SHA1;
case CKM_SHA256:
return CKM_NSS_HKDF_SHA256;
case CKM_SHA384:
return CKM_NSS_HKDF_SHA384;
case CKM_SHA512:
return CKM_NSS_HKDF_SHA512;
default:
ADD_FAILURE() << "Unknown hash mech";
return CKM_INVALID_MECHANISM;
}
}
ScopedPK11SymKey ImportKey(SECItem &ikm_item, bool import_as_data) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
ADD_FAILURE() << "Can't get slot";
return nullptr;
}
ScopedPK11SymKey ikm;
if (import_as_data) {
ikm.reset(PK11_ImportDataKey(slot.get(), CKM_HKDF_KEY_GEN,
PK11_OriginUnwrap, CKA_SIGN, &ikm_item,
nullptr));
} else {
ikm.reset(PK11_ImportSymKey(slot.get(), CKM_GENERIC_SECRET_KEY_GEN,
PK11_OriginUnwrap, CKA_SIGN, &ikm_item,
nullptr));
}
return ikm;
}
void RunWycheproofTest(const HkdfTestVector &vec, HkdfTestType test_type,
CK_MECHANISM_TYPE hash_mech) {
std::string msg = "Test #" + std::to_string(vec.id) + " failed";
std::vector<uint8_t> vec_ikm = hex_string_to_bytes(vec.ikm);
std::vector<uint8_t> vec_okm = hex_string_to_bytes(vec.okm);
std::vector<uint8_t> vec_info = hex_string_to_bytes(vec.info);
std::vector<uint8_t> vec_salt = hex_string_to_bytes(vec.salt);
SECItem ikm_item = {siBuffer, vec_ikm.data(),
static_cast<unsigned int>(vec_ikm.size())};
SECItem okm_item = {siBuffer, vec_okm.data(),
static_cast<unsigned int>(vec_okm.size())};
SECItem salt_item = {siBuffer, vec_salt.data(),
static_cast<unsigned int>(vec_salt.size())};
CK_MECHANISM_TYPE derive_mech = CKM_HKDF_DERIVE;
ScopedPK11SymKey salt_key = nullptr;
ScopedPK11SymKey ikm = nullptr;
// Legacy vendor mech params
CK_NSS_HKDFParams nss_hkdf_params = {
true, vec_salt.data(), static_cast<unsigned int>(vec_salt.size()),
true, vec_info.data(), static_cast<unsigned int>(vec_info.size())};
// PKCS #11 v3.0
CK_HKDF_PARAMS hkdf_params = {
true,
true,
hash_mech,
vec_salt.size() ? CKF_HKDF_SALT_DATA : CKF_HKDF_SALT_NULL,
vec_salt.size() ? vec_salt.data() : nullptr,
static_cast<unsigned int>(vec_salt.size()),
CK_INVALID_HANDLE,
vec_info.data(),
static_cast<unsigned int>(vec_info.size())};
SECItem params_item = {siBuffer, (unsigned char *)&hkdf_params,
sizeof(hkdf_params)};
switch (test_type) {
case HkdfTestType::legacy:
derive_mech = Pk11MechToVendorMech(hash_mech);
params_item.data = (uint8_t *)&nss_hkdf_params;
params_item.len = sizeof(nss_hkdf_params);
ikm = ImportKey(ikm_item, false);
break;
case HkdfTestType::derive:
ikm = ImportKey(ikm_item, false);
break;
case HkdfTestType::deriveDataKey:
ikm = ImportKey(ikm_item, true);
break;
case HkdfTestType::saltDerive:
ikm = ImportKey(ikm_item, false);
salt_key = ImportKey(salt_item, false);
break;
case HkdfTestType::saltDeriveDataKey:
ikm = ImportKey(ikm_item, true);
salt_key = ImportKey(salt_item, true);
break;
case HkdfTestType::hkdfData:
derive_mech = CKM_HKDF_DATA;
ikm = ImportKey(ikm_item, true);
break;
default:
ADD_FAILURE() << msg;
return;
}
ASSERT_NE(nullptr, ikm) << msg;
if (test_type == HkdfTestType::saltDerive ||
test_type == HkdfTestType::saltDeriveDataKey) {
ASSERT_NE(nullptr, salt_key) << msg;
hkdf_params.ulSaltType = CKF_HKDF_SALT_KEY;
hkdf_params.ulSaltLen = 0;
hkdf_params.pSalt = NULL;
hkdf_params.hSaltKey = PK11_GetSymKeyHandle(salt_key.get());
}
ScopedPK11SymKey okm = ScopedPK11SymKey(
PK11_Derive(ikm.get(), derive_mech, &params_item,
CKM_GENERIC_SECRET_KEY_GEN, CKA_DERIVE, vec.size));
if (vec.valid) {
ASSERT_NE(nullptr, okm.get()) << msg;
ASSERT_EQ(SECSuccess, PK11_ExtractKeyValue(okm.get())) << msg;
ASSERT_EQ(0, SECITEM_CompareItem(&okm_item, PK11_GetKeyData(okm.get())))
<< msg;
} else {
ASSERT_EQ(nullptr, okm.get()) << msg;
}
}
};
TEST_P(Pkcs11HkdfTest, WycheproofVectors) {
RunWycheproofTest(std::get<0>(GetParam()), std::get<1>(GetParam()),
std::get<2>(GetParam()));
}
INSTANTIATE_TEST_SUITE_P(
HkdfSha1, Pkcs11HkdfTest,
::testing::Combine(::testing::ValuesIn(kHkdfSha1WycheproofVectors),
::testing::ValuesIn(kHkdfTestTypesAll),
::testing::Values(CKM_SHA_1)));
INSTANTIATE_TEST_SUITE_P(
HkdfSha256, Pkcs11HkdfTest,
::testing::Combine(::testing::ValuesIn(kHkdfSha256WycheproofVectors),
::testing::ValuesIn(kHkdfTestTypesAll),
::testing::Values(CKM_SHA256)));
INSTANTIATE_TEST_SUITE_P(
HkdfSha384, Pkcs11HkdfTest,
::testing::Combine(::testing::ValuesIn(kHkdfSha384WycheproofVectors),
::testing::ValuesIn(kHkdfTestTypesAll),
::testing::Values(CKM_SHA384)));
INSTANTIATE_TEST_SUITE_P(
HkdfSha512, Pkcs11HkdfTest,
::testing::Combine(::testing::ValuesIn(kHkdfSha512WycheproofVectors),
::testing::ValuesIn(kHkdfTestTypesAll),
::testing::Values(CKM_SHA512)));
} // namespace nss_test

View file

@ -0,0 +1,74 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <memory>
#include <tuple>
#include "nss.h"
#include "pk11pub.h"
#include "secerr.h"
#include "sechash.h"
#include "blapi.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "testvectors/hmac-sha256-vectors.h"
#include "testvectors/hmac-sha384-vectors.h"
#include "testvectors/hmac-sha512-vectors.h"
#include "util.h"
namespace nss_test {
class Pkcs11HmacTest : public ::testing::TestWithParam<
std::tuple<HmacTestVector, CK_MECHANISM_TYPE>> {
protected:
void RunTestVector(const HmacTestVector &vec, CK_MECHANISM_TYPE mech) {
std::string err = "Test #" + std::to_string(vec.id) + " failed";
std::vector<uint8_t> vec_key = hex_string_to_bytes(vec.key);
std::vector<uint8_t> vec_mac = hex_string_to_bytes(vec.tag);
std::vector<uint8_t> vec_msg = hex_string_to_bytes(vec.msg);
std::vector<uint8_t> output(vec_mac.size());
// Don't provide a null pointer, even if the input is empty.
uint8_t tmp;
SECItem key = {siBuffer, vec_key.data() ? vec_key.data() : &tmp,
static_cast<unsigned int>(vec_key.size())};
SECItem mac = {siBuffer, vec_mac.data() ? vec_mac.data() : &tmp,
static_cast<unsigned int>(vec_mac.size())};
SECItem msg = {siBuffer, vec_msg.data() ? vec_msg.data() : &tmp,
static_cast<unsigned int>(vec_msg.size())};
SECItem out = {siBuffer, output.data() ? output.data() : &tmp,
static_cast<unsigned int>(output.size())};
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(nullptr, slot) << err;
ScopedPK11SymKey p11_key(PK11_ImportSymKey(
slot.get(), mech, PK11_OriginUnwrap, CKA_SIGN, &key, nullptr));
ASSERT_NE(nullptr, p11_key.get()) << err;
SECStatus rv = PK11_SignWithSymKey(p11_key.get(), mech, NULL, &out, &msg);
EXPECT_EQ(SECSuccess, rv) << err;
EXPECT_EQ(!vec.invalid, 0 == SECITEM_CompareItem(&out, &mac)) << err;
}
};
TEST_P(Pkcs11HmacTest, WycheproofVectors) {
RunTestVector(std::get<0>(GetParam()), std::get<1>(GetParam()));
}
INSTANTIATE_TEST_SUITE_P(
HmacSha256, Pkcs11HmacTest,
::testing::Combine(::testing::ValuesIn(kHmacSha256WycheproofVectors),
::testing::Values(CKM_SHA256_HMAC)));
INSTANTIATE_TEST_SUITE_P(
HmacSha384, Pkcs11HmacTest,
::testing::Combine(::testing::ValuesIn(kHmacSha384WycheproofVectors),
::testing::Values(CKM_SHA384_HMAC)));
INSTANTIATE_TEST_SUITE_P(
HmacSha512, Pkcs11HmacTest,
::testing::Combine(::testing::ValuesIn(kHmacSha512WycheproofVectors),
::testing::Values(CKM_SHA512_HMAC)));
} // namespace nss_test

View file

@ -0,0 +1,891 @@
/* -*- 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 "blapi.h"
#include "gtest/gtest.h"
#include "json_reader.h"
#include "nss.h"
#include "nss_scoped_ptrs.h"
#include "pk11hpke.h"
#include "pk11pub.h"
#include "secerr.h"
#include "sechash.h"
#include "util.h"
extern std::string g_source_dir;
namespace nss_test {
/* See note in pk11pub.h. */
#include "cpputil.h"
class HpkeTest {
protected:
void CheckEquality(const std::vector<uint8_t> &expected, SECItem *actual) {
if (!actual) {
EXPECT_TRUE(expected.empty());
return;
}
std::vector<uint8_t> vact(actual->data, actual->data + actual->len);
EXPECT_EQ(expected, vact);
}
void CheckEquality(SECItem *expected, SECItem *actual) {
EXPECT_EQ(!!expected, !!actual);
if (expected && actual) {
EXPECT_EQ(expected->len, actual->len);
if (expected->len == actual->len) {
EXPECT_EQ(0, memcmp(expected->data, actual->data, actual->len));
}
}
}
void CheckEquality(const std::vector<uint8_t> &expected, PK11SymKey *actual) {
if (!actual) {
EXPECT_TRUE(expected.empty());
return;
}
SECStatus rv = PK11_ExtractKeyValue(actual);
EXPECT_EQ(SECSuccess, rv);
if (rv != SECSuccess) {
return;
}
SECItem *rawkey = PK11_GetKeyData(actual);
CheckEquality(expected, rawkey);
}
void CheckEquality(PK11SymKey *expected, PK11SymKey *actual) {
if (!actual || !expected) {
EXPECT_EQ(!!expected, !!actual);
return;
}
SECStatus rv = PK11_ExtractKeyValue(expected);
EXPECT_EQ(SECSuccess, rv);
if (rv != SECSuccess) {
return;
}
SECItem *raw = PK11_GetKeyData(expected);
ASSERT_NE(nullptr, raw);
ASSERT_NE(nullptr, raw->data);
std::vector<uint8_t> expected_vec(raw->data, raw->data + raw->len);
CheckEquality(expected_vec, actual);
}
void Seal(const ScopedHpkeContext &cx, const std::vector<uint8_t> &aad_vec,
const std::vector<uint8_t> &pt_vec,
std::vector<uint8_t> *out_sealed) {
SECItem aad_item = {siBuffer, toUcharPtr(aad_vec.data()),
static_cast<unsigned int>(aad_vec.size())};
SECItem pt_item = {siBuffer, toUcharPtr(pt_vec.data()),
static_cast<unsigned int>(pt_vec.size())};
SECItem *sealed_item = nullptr;
EXPECT_EQ(SECSuccess,
PK11_HPKE_Seal(cx.get(), &aad_item, &pt_item, &sealed_item));
ASSERT_NE(nullptr, sealed_item);
ScopedSECItem sealed(sealed_item);
out_sealed->assign(sealed->data, sealed->data + sealed->len);
}
void Open(const ScopedHpkeContext &cx, const std::vector<uint8_t> &aad_vec,
const std::vector<uint8_t> &ct_vec,
std::vector<uint8_t> *out_opened) {
SECItem aad_item = {siBuffer, toUcharPtr(aad_vec.data()),
static_cast<unsigned int>(aad_vec.size())};
SECItem ct_item = {siBuffer, toUcharPtr(ct_vec.data()),
static_cast<unsigned int>(ct_vec.size())};
SECItem *opened_item = nullptr;
EXPECT_EQ(SECSuccess,
PK11_HPKE_Open(cx.get(), &aad_item, &ct_item, &opened_item));
ASSERT_NE(nullptr, opened_item);
ScopedSECItem opened(opened_item);
out_opened->assign(opened->data, opened->data + opened->len);
}
void SealOpen(const ScopedHpkeContext &sender,
const ScopedHpkeContext &receiver,
const std::vector<uint8_t> &msg,
const std::vector<uint8_t> &aad,
const std::vector<uint8_t> *expect) {
std::vector<uint8_t> sealed;
std::vector<uint8_t> opened;
Seal(sender, aad, msg, &sealed);
if (expect) {
EXPECT_EQ(*expect, sealed);
}
Open(receiver, aad, sealed, &opened);
EXPECT_EQ(msg, opened);
}
void ExportSecret(const ScopedHpkeContext &receiver,
ScopedPK11SymKey &exported) {
std::vector<uint8_t> context = {'c', 't', 'x', 't'};
SECItem context_item = {siBuffer, context.data(),
static_cast<unsigned int>(context.size())};
PK11SymKey *tmp_exported = nullptr;
ASSERT_EQ(SECSuccess, PK11_HPKE_ExportSecret(receiver.get(), &context_item,
64, &tmp_exported));
exported.reset(tmp_exported);
}
void ExportImportRecvContext(ScopedHpkeContext &scoped_cx,
PK11SymKey *wrapping_key) {
SECItem *tmp_exported = nullptr;
EXPECT_EQ(SECSuccess, PK11_HPKE_ExportContext(scoped_cx.get(), wrapping_key,
&tmp_exported));
EXPECT_NE(nullptr, tmp_exported);
ScopedSECItem context(tmp_exported);
scoped_cx.reset();
HpkeContext *tmp_imported =
PK11_HPKE_ImportContext(context.get(), wrapping_key);
EXPECT_NE(nullptr, tmp_imported);
scoped_cx.reset(tmp_imported);
}
bool GenerateKeyPair(ScopedSECKEYPublicKey &pub_key,
ScopedSECKEYPrivateKey &priv_key) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
ADD_FAILURE() << "Couldn't get slot";
return false;
}
unsigned char param_buf[65];
SECItem ecdsa_params = {siBuffer, param_buf, sizeof(param_buf)};
SECOidData *oid_data = SECOID_FindOIDByTag(SEC_OID_CURVE25519);
if (!oid_data) {
ADD_FAILURE() << "Couldn't get oid_data";
return false;
}
ecdsa_params.data[0] = SEC_ASN1_OBJECT_ID;
ecdsa_params.data[1] = oid_data->oid.len;
memcpy(ecdsa_params.data + 2, oid_data->oid.data, oid_data->oid.len);
ecdsa_params.len = oid_data->oid.len + 2;
SECKEYPublicKey *pub_tmp;
SECKEYPrivateKey *priv_tmp;
priv_tmp =
PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsa_params,
&pub_tmp, PR_FALSE, PR_TRUE, nullptr);
if (!pub_tmp || !priv_tmp) {
ADD_FAILURE() << "PK11_GenerateKeyPair failed";
return false;
}
pub_key.reset(pub_tmp);
priv_key.reset(priv_tmp);
return true;
}
void SetUpEphemeralContexts(ScopedHpkeContext &sender,
ScopedHpkeContext &receiver,
HpkeModeId mode = HpkeModeBase,
HpkeKemId kem = HpkeDhKemX25519Sha256,
HpkeKdfId kdf = HpkeKdfHkdfSha256,
HpkeAeadId aead = HpkeAeadAes128Gcm) {
// Generate a PSK, if the mode calls for it.
PRUint8 psk_id_buf[] = {'p', 's', 'k', '-', 'i', 'd'};
SECItem psk_id = {siBuffer, psk_id_buf, sizeof(psk_id_buf)};
SECItem *psk_id_item = (mode == HpkeModePsk) ? &psk_id : nullptr;
ScopedPK11SymKey psk;
if (mode == HpkeModePsk) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(slot);
PK11SymKey *tmp_psk =
PK11_KeyGen(slot.get(), CKM_HKDF_DERIVE, nullptr, 16, nullptr);
ASSERT_NE(nullptr, tmp_psk);
psk.reset(tmp_psk);
}
std::vector<uint8_t> info = {'t', 'e', 's', 't', '-', 'i', 'n', 'f', 'o'};
SECItem info_item = {siBuffer, info.data(),
static_cast<unsigned int>(info.size())};
sender.reset(PK11_HPKE_NewContext(kem, kdf, aead, psk.get(), psk_id_item));
receiver.reset(
PK11_HPKE_NewContext(kem, kdf, aead, psk.get(), psk_id_item));
ASSERT_TRUE(sender);
ASSERT_TRUE(receiver);
ScopedSECKEYPublicKey pub_key_r;
ScopedSECKEYPrivateKey priv_key_r;
ASSERT_TRUE(GenerateKeyPair(pub_key_r, priv_key_r));
EXPECT_EQ(SECSuccess, PK11_HPKE_SetupS(sender.get(), nullptr, nullptr,
pub_key_r.get(), &info_item));
const SECItem *enc = PK11_HPKE_GetEncapPubKey(sender.get());
EXPECT_NE(nullptr, enc);
EXPECT_EQ(SECSuccess, PK11_HPKE_SetupR(
receiver.get(), pub_key_r.get(), priv_key_r.get(),
const_cast<SECItem *>(enc), &info_item));
}
};
struct HpkeEncryptVector {
std::vector<uint8_t> pt;
std::vector<uint8_t> aad;
std::vector<uint8_t> ct;
static std::vector<HpkeEncryptVector> ReadVec(JsonReader &r) {
std::vector<HpkeEncryptVector> all;
while (r.NextItemArray()) {
HpkeEncryptVector enc;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "plaintext") {
enc.pt = r.ReadHex();
} else if (n == "aad") {
enc.aad = r.ReadHex();
} else if (n == "ciphertext") {
enc.ct = r.ReadHex();
} else {
r.SkipValue();
}
}
all.push_back(enc);
}
return all;
}
};
struct HpkeExportVector {
std::vector<uint8_t> ctxt;
size_t len;
std::vector<uint8_t> exported;
static std::vector<HpkeExportVector> ReadVec(JsonReader &r) {
std::vector<HpkeExportVector> all;
while (r.NextItemArray()) {
HpkeExportVector exp;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "exporter_context") {
exp.ctxt = r.ReadHex();
} else if (n == "L") {
exp.len = r.ReadInt();
} else if (n == "exported_value") {
exp.exported = r.ReadHex();
} else {
r.SkipValue();
}
}
all.push_back(exp);
}
return all;
}
};
struct HpkeVector {
uint32_t test_id;
HpkeModeId mode;
HpkeKemId kem_id;
HpkeKdfId kdf_id;
HpkeAeadId aead_id;
std::vector<uint8_t> info;
std::vector<uint8_t> pkcs8_e;
std::vector<uint8_t> pkcs8_r;
std::vector<uint8_t> psk;
std::vector<uint8_t> psk_id;
std::vector<uint8_t> enc;
std::vector<uint8_t> key;
std::vector<uint8_t> nonce;
std::vector<HpkeEncryptVector> encryptions;
std::vector<HpkeExportVector> exports;
static std::vector<uint8_t> Pkcs8(const std::vector<uint8_t> &sk,
const std::vector<uint8_t> &pk) {
// Only X25519 format.
std::vector<uint8_t> v(105);
v.assign({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});
v.insert(v.end(), sk.begin(), sk.end());
v.insert(v.end(), {0xa1, 0x23, 0x03, 0x21, 0x00});
v.insert(v.end(), pk.begin(), pk.end());
return v;
}
static std::vector<HpkeVector> Read(JsonReader &r) {
std::vector<HpkeVector> all_tests;
uint32_t test_id = 0;
while (r.NextItemArray()) {
HpkeVector vec = {0};
uint32_t fields = 0;
enum class RequiredFields {
mode,
kem,
kdf,
aead,
skEm,
skRm,
pkEm,
pkRm,
all
};
std::vector<uint8_t> sk_e, pk_e, sk_r, pk_r;
test_id++;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "mode") {
vec.mode = static_cast<HpkeModeId>(r.ReadInt());
fields |= 1 << static_cast<uint32_t>(RequiredFields::mode);
} else if (n == "kem_id") {
vec.kem_id = static_cast<HpkeKemId>(r.ReadInt());
fields |= 1 << static_cast<uint32_t>(RequiredFields::kem);
} else if (n == "kdf_id") {
vec.kdf_id = static_cast<HpkeKdfId>(r.ReadInt());
fields |= 1 << static_cast<uint32_t>(RequiredFields::kdf);
} else if (n == "aead_id") {
vec.aead_id = static_cast<HpkeAeadId>(r.ReadInt());
fields |= 1 << static_cast<uint32_t>(RequiredFields::aead);
} else if (n == "info") {
vec.info = r.ReadHex();
} else if (n == "skEm") {
sk_e = r.ReadHex();
fields |= 1 << static_cast<uint32_t>(RequiredFields::skEm);
} else if (n == "pkEm") {
pk_e = r.ReadHex();
fields |= 1 << static_cast<uint32_t>(RequiredFields::pkEm);
} else if (n == "skRm") {
sk_r = r.ReadHex();
fields |= 1 << static_cast<uint32_t>(RequiredFields::skRm);
} else if (n == "pkRm") {
pk_r = r.ReadHex();
fields |= 1 << static_cast<uint32_t>(RequiredFields::pkRm);
} else if (n == "psk") {
vec.psk = r.ReadHex();
} else if (n == "psk_id") {
vec.psk_id = r.ReadHex();
} else if (n == "enc") {
vec.enc = r.ReadHex();
} else if (n == "key") {
vec.key = r.ReadHex();
} else if (n == "base_nonce") {
vec.nonce = r.ReadHex();
} else if (n == "encryptions") {
vec.encryptions = HpkeEncryptVector::ReadVec(r);
} else if (n == "exports") {
vec.exports = HpkeExportVector::ReadVec(r);
} else {
r.SkipValue();
}
}
if (fields != (1 << static_cast<uint32_t>(RequiredFields::all)) - 1) {
std::cerr << "Skipping entry " << test_id << " for missing fields"
<< std::endl;
continue;
}
// Skip modes and configurations we don't support.
if (vec.mode != HpkeModeBase && vec.mode != HpkeModePsk) {
continue;
}
SECStatus rv =
PK11_HPKE_ValidateParameters(vec.kem_id, vec.kdf_id, vec.aead_id);
if (rv != SECSuccess) {
continue;
}
vec.test_id = test_id;
vec.pkcs8_e = HpkeVector::Pkcs8(sk_e, pk_e);
vec.pkcs8_r = HpkeVector::Pkcs8(sk_r, pk_r);
all_tests.push_back(vec);
}
return all_tests;
}
};
class TestVectors : public HpkeTest, public ::testing::Test {
struct Endpoint {
bool init(const HpkeVector &vec, const std::vector<uint8_t> &sk_data) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
ADD_FAILURE() << "No slot";
return false;
}
cx_ = Endpoint::MakeContext(slot, vec);
SECItem item = {siBuffer, toUcharPtr(sk_data.data()),
static_cast<unsigned int>(sk_data.size())};
SECKEYPrivateKey *sk = nullptr;
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot.get(), &item, nullptr, nullptr, false, false, KU_ALL, &sk,
nullptr);
if (rv != SECSuccess) {
ADD_FAILURE() << "Failed to import secret";
return false;
}
sk_.reset(sk);
SECKEYPublicKey *pk = SECKEY_ConvertToPublicKey(sk_.get());
pk_.reset(pk);
return cx_ && sk_ && pk_;
}
static ScopedHpkeContext MakeContext(const ScopedPK11SlotInfo &slot,
const HpkeVector &vec) {
ScopedPK11SymKey psk = Endpoint::ReadPsk(slot, vec);
SECItem psk_id_item = {siBuffer, toUcharPtr(vec.psk_id.data()),
static_cast<unsigned int>(vec.psk_id.size())};
SECItem *psk_id = psk ? &psk_id_item : nullptr;
return ScopedHpkeContext(PK11_HPKE_NewContext(
vec.kem_id, vec.kdf_id, vec.aead_id, psk.get(), psk_id));
}
static ScopedPK11SymKey ReadPsk(const ScopedPK11SlotInfo &slot,
const HpkeVector &vec) {
ScopedPK11SymKey psk;
if (!vec.psk.empty()) {
SECItem psk_item = {siBuffer, toUcharPtr(vec.psk.data()),
static_cast<unsigned int>(vec.psk.size())};
PK11SymKey *psk_key =
PK11_ImportSymKey(slot.get(), CKM_HKDF_KEY_GEN, PK11_OriginUnwrap,
CKA_WRAP, &psk_item, nullptr);
EXPECT_NE(nullptr, psk_key);
psk.reset(psk_key);
}
return psk;
}
ScopedHpkeContext cx_;
ScopedSECKEYPublicKey pk_;
ScopedSECKEYPrivateKey sk_;
};
protected:
void TestExports(const HpkeVector &vec, const Endpoint &sender,
const Endpoint &receiver) {
for (auto &exp : vec.exports) {
SECItem context_item = {siBuffer, toUcharPtr(exp.ctxt.data()),
static_cast<unsigned int>(exp.ctxt.size())};
PK11SymKey *actual_r = nullptr;
PK11SymKey *actual_s = nullptr;
ASSERT_EQ(SECSuccess,
PK11_HPKE_ExportSecret(sender.cx_.get(), &context_item, exp.len,
&actual_s));
ASSERT_EQ(SECSuccess,
PK11_HPKE_ExportSecret(receiver.cx_.get(), &context_item,
exp.len, &actual_r));
ScopedPK11SymKey scoped_act_s(actual_s);
ScopedPK11SymKey scoped_act_r(actual_r);
CheckEquality(exp.exported, scoped_act_s.get());
CheckEquality(exp.exported, scoped_act_r.get());
}
}
void TestEncryptions(const HpkeVector &vec, const Endpoint &sender,
const Endpoint &receiver) {
for (auto &enc : vec.encryptions) {
SealOpen(sender.cx_, receiver.cx_, enc.pt, enc.aad, &enc.ct);
}
}
void SetupS(const ScopedHpkeContext &cx, const ScopedSECKEYPublicKey &pkE,
const ScopedSECKEYPrivateKey &skE,
const ScopedSECKEYPublicKey &pkR,
const std::vector<uint8_t> &info) {
SECItem info_item = {siBuffer, toUcharPtr(info.data()),
static_cast<unsigned int>(info.size())};
EXPECT_EQ(SECSuccess, PK11_HPKE_SetupS(cx.get(), pkE.get(), skE.get(),
pkR.get(), &info_item));
}
void SetupR(const ScopedHpkeContext &cx, const ScopedSECKEYPublicKey &pkR,
const ScopedSECKEYPrivateKey &skR,
const std::vector<uint8_t> &enc,
const std::vector<uint8_t> &info) {
SECItem enc_item = {siBuffer, toUcharPtr(enc.data()),
static_cast<unsigned int>(enc.size())};
SECItem info_item = {siBuffer, toUcharPtr(info.data()),
static_cast<unsigned int>(info.size())};
EXPECT_EQ(SECSuccess, PK11_HPKE_SetupR(cx.get(), pkR.get(), skR.get(),
&enc_item, &info_item));
}
void SetupSenderReceiver(const HpkeVector &vec, const Endpoint &sender,
const Endpoint &receiver) {
SetupS(sender.cx_, sender.pk_, sender.sk_, receiver.pk_, vec.info);
uint8_t buf[32]; // Curve25519 only, fixed size.
SECItem encap_item = {siBuffer, const_cast<uint8_t *>(buf), sizeof(buf)};
ASSERT_EQ(SECSuccess, PK11_HPKE_Serialize(sender.pk_.get(), encap_item.data,
&encap_item.len, encap_item.len));
CheckEquality(vec.enc, &encap_item);
SetupR(receiver.cx_, receiver.pk_, receiver.sk_, vec.enc, vec.info);
}
void RunTestVector(const HpkeVector &vec) {
Endpoint sender;
ASSERT_TRUE(sender.init(vec, vec.pkcs8_e));
Endpoint receiver;
ASSERT_TRUE(receiver.init(vec, vec.pkcs8_r));
SetupSenderReceiver(vec, sender, receiver);
TestEncryptions(vec, sender, receiver);
TestExports(vec, sender, receiver);
}
};
TEST_F(TestVectors, HpkeVectors) {
JsonReader r(::g_source_dir + "/hpke-vectors.json");
auto all_tests = HpkeVector::Read(r);
for (auto &vec : all_tests) {
std::cout << "HPKE vector " << vec.test_id << std::endl;
RunTestVector(vec);
}
}
class ModeParameterizedTest
: public HpkeTest,
public ::testing::TestWithParam<
std::tuple<HpkeModeId, HpkeKemId, HpkeKdfId, HpkeAeadId>> {};
static const HpkeModeId kHpkeModesAll[] = {HpkeModeBase, HpkeModePsk};
static const HpkeKemId kHpkeKemIdsAll[] = {HpkeDhKemX25519Sha256};
static const HpkeKdfId kHpkeKdfIdsAll[] = {HpkeKdfHkdfSha256, HpkeKdfHkdfSha384,
HpkeKdfHkdfSha512};
static const HpkeAeadId kHpkeAeadIdsAll[] = {HpkeAeadAes128Gcm,
HpkeAeadChaCha20Poly1305};
INSTANTIATE_TEST_SUITE_P(
Pk11Hpke, ModeParameterizedTest,
::testing::Combine(::testing::ValuesIn(kHpkeModesAll),
::testing::ValuesIn(kHpkeKemIdsAll),
::testing::ValuesIn(kHpkeKdfIdsAll),
::testing::ValuesIn(kHpkeAeadIdsAll)));
TEST_F(ModeParameterizedTest, BadEncapsulatedPubKey) {
ScopedHpkeContext sender(
PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256,
HpkeAeadAes128Gcm, nullptr, nullptr));
ScopedHpkeContext receiver(
PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256,
HpkeAeadAes128Gcm, nullptr, nullptr));
SECItem empty = {siBuffer, nullptr, 0};
uint8_t buf[100];
SECItem short_encap = {siBuffer, buf, 1};
SECItem long_encap = {siBuffer, buf, sizeof(buf)};
SECKEYPublicKey *tmp_pub_key;
ScopedSECKEYPublicKey pub_key;
ScopedSECKEYPrivateKey priv_key;
ASSERT_TRUE(GenerateKeyPair(pub_key, priv_key));
// Decapsulating an empty buffer should fail.
EXPECT_EQ(SECFailure, PK11_HPKE_Deserialize(sender.get(), empty.data,
empty.len, &tmp_pub_key));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// Decapsulating anything short will succeed, but the setup will fail.
EXPECT_EQ(SECSuccess, PK11_HPKE_Deserialize(sender.get(), short_encap.data,
short_encap.len, &tmp_pub_key));
ScopedSECKEYPublicKey bad_pub_key(tmp_pub_key);
EXPECT_EQ(SECFailure,
PK11_HPKE_SetupS(receiver.get(), pub_key.get(), priv_key.get(),
bad_pub_key.get(), &empty));
EXPECT_EQ(SEC_ERROR_INVALID_KEY, PORT_GetError());
// Test the same for a receiver.
EXPECT_EQ(SECFailure, PK11_HPKE_SetupR(sender.get(), pub_key.get(),
priv_key.get(), &empty, &empty));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(SECFailure, PK11_HPKE_SetupR(sender.get(), pub_key.get(),
priv_key.get(), &short_encap, &empty));
EXPECT_EQ(SEC_ERROR_INVALID_KEY, PORT_GetError());
// Encapsulated key too long
EXPECT_EQ(SECSuccess, PK11_HPKE_Deserialize(sender.get(), long_encap.data,
long_encap.len, &tmp_pub_key));
bad_pub_key.reset(tmp_pub_key);
EXPECT_EQ(SECFailure,
PK11_HPKE_SetupS(receiver.get(), pub_key.get(), priv_key.get(),
bad_pub_key.get(), &empty));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
EXPECT_EQ(SECFailure, PK11_HPKE_SetupR(sender.get(), pub_key.get(),
priv_key.get(), &long_encap, &empty));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
TEST_P(ModeParameterizedTest, ContextExportImportEncrypt) {
std::vector<uint8_t> msg = {'s', 'e', 'c', 'r', 'e', 't'};
std::vector<uint8_t> aad = {'a', 'a', 'd'};
ScopedHpkeContext sender;
ScopedHpkeContext receiver;
SetUpEphemeralContexts(sender, receiver, std::get<0>(GetParam()),
std::get<1>(GetParam()), std::get<2>(GetParam()),
std::get<3>(GetParam()));
SealOpen(sender, receiver, msg, aad, nullptr);
ExportImportRecvContext(receiver, nullptr);
SealOpen(sender, receiver, msg, aad, nullptr);
}
TEST_P(ModeParameterizedTest, ContextExportImportExport) {
ScopedHpkeContext sender;
ScopedHpkeContext receiver;
ScopedPK11SymKey sender_export;
ScopedPK11SymKey receiver_export;
ScopedPK11SymKey receiver_reexport;
SetUpEphemeralContexts(sender, receiver, std::get<0>(GetParam()),
std::get<1>(GetParam()), std::get<2>(GetParam()),
std::get<3>(GetParam()));
ExportSecret(sender, sender_export);
ExportSecret(receiver, receiver_export);
CheckEquality(sender_export.get(), receiver_export.get());
ExportImportRecvContext(receiver, nullptr);
ExportSecret(receiver, receiver_reexport);
CheckEquality(receiver_export.get(), receiver_reexport.get());
}
TEST_P(ModeParameterizedTest, ContextExportImportWithWrap) {
std::vector<uint8_t> msg = {'s', 'e', 'c', 'r', 'e', 't'};
std::vector<uint8_t> aad = {'a', 'a', 'd'};
// Generate a wrapping key, then use it for export.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(slot);
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
ScopedHpkeContext sender;
ScopedHpkeContext receiver;
SetUpEphemeralContexts(sender, receiver, std::get<0>(GetParam()),
std::get<1>(GetParam()), std::get<2>(GetParam()),
std::get<3>(GetParam()));
SealOpen(sender, receiver, msg, aad, nullptr);
ExportImportRecvContext(receiver, kek.get());
SealOpen(sender, receiver, msg, aad, nullptr);
}
TEST_P(ModeParameterizedTest, ExportSenderContext) {
std::vector<uint8_t> msg = {'s', 'e', 'c', 'r', 'e', 't'};
std::vector<uint8_t> aad = {'a', 'a', 'd'};
ScopedHpkeContext sender;
ScopedHpkeContext receiver;
SetUpEphemeralContexts(sender, receiver, std::get<0>(GetParam()),
std::get<1>(GetParam()), std::get<2>(GetParam()),
std::get<3>(GetParam()));
SECItem *tmp_exported = nullptr;
EXPECT_EQ(SECFailure,
PK11_HPKE_ExportContext(sender.get(), nullptr, &tmp_exported));
EXPECT_EQ(nullptr, tmp_exported);
EXPECT_EQ(SEC_ERROR_NOT_A_RECIPIENT, PORT_GetError());
}
TEST_P(ModeParameterizedTest, ContextUnwrapBadKey) {
std::vector<uint8_t> msg = {'s', 'e', 'c', 'r', 'e', 't'};
std::vector<uint8_t> aad = {'a', 'a', 'd'};
// Generate a wrapping key, then use it for export.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(slot);
ScopedPK11SymKey kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, kek);
ScopedPK11SymKey not_kek(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
ASSERT_NE(nullptr, not_kek);
ScopedHpkeContext sender;
ScopedHpkeContext receiver;
SetUpEphemeralContexts(sender, receiver, std::get<0>(GetParam()),
std::get<1>(GetParam()), std::get<2>(GetParam()),
std::get<3>(GetParam()));
SECItem *tmp_exported = nullptr;
EXPECT_EQ(SECSuccess,
PK11_HPKE_ExportContext(receiver.get(), kek.get(), &tmp_exported));
EXPECT_NE(nullptr, tmp_exported);
ScopedSECItem context(tmp_exported);
EXPECT_EQ(nullptr, PK11_HPKE_ImportContext(context.get(), not_kek.get()));
EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
}
TEST_P(ModeParameterizedTest, EphemeralKeys) {
std::vector<uint8_t> msg = {'s', 'e', 'c', 'r', 'e', 't'};
std::vector<uint8_t> aad = {'a', 'a', 'd'};
SECItem msg_item = {siBuffer, msg.data(),
static_cast<unsigned int>(msg.size())};
SECItem aad_item = {siBuffer, aad.data(),
static_cast<unsigned int>(aad.size())};
ScopedHpkeContext sender;
ScopedHpkeContext receiver;
SetUpEphemeralContexts(sender, receiver, std::get<0>(GetParam()),
std::get<1>(GetParam()), std::get<2>(GetParam()),
std::get<3>(GetParam()));
SealOpen(sender, receiver, msg, aad, nullptr);
// Seal for negative tests
SECItem *tmp_sealed = nullptr;
SECItem *tmp_unsealed = nullptr;
EXPECT_EQ(SECSuccess,
PK11_HPKE_Seal(sender.get(), &aad_item, &msg_item, &tmp_sealed));
ASSERT_NE(nullptr, tmp_sealed);
ScopedSECItem sealed(tmp_sealed);
// Drop AAD
EXPECT_EQ(SECFailure, PK11_HPKE_Open(receiver.get(), nullptr, sealed.get(),
&tmp_unsealed));
EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
EXPECT_EQ(nullptr, tmp_unsealed);
// Modify AAD
aad_item.data[0] ^= 0xff;
EXPECT_EQ(SECFailure, PK11_HPKE_Open(receiver.get(), &aad_item, sealed.get(),
&tmp_unsealed));
EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
EXPECT_EQ(nullptr, tmp_unsealed);
aad_item.data[0] ^= 0xff;
// Modify ciphertext
sealed->data[0] ^= 0xff;
EXPECT_EQ(SECFailure, PK11_HPKE_Open(receiver.get(), &aad_item, sealed.get(),
&tmp_unsealed));
EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
EXPECT_EQ(nullptr, tmp_unsealed);
sealed->data[0] ^= 0xff;
EXPECT_EQ(SECSuccess, PK11_HPKE_Open(receiver.get(), &aad_item, sealed.get(),
&tmp_unsealed));
EXPECT_NE(nullptr, tmp_unsealed);
ScopedSECItem unsealed(tmp_unsealed);
CheckEquality(&msg_item, unsealed.get());
}
TEST_F(ModeParameterizedTest, InvalidContextParams) {
HpkeContext *cx =
PK11_HPKE_NewContext(static_cast<HpkeKemId>(0xff), HpkeKdfHkdfSha256,
HpkeAeadChaCha20Poly1305, nullptr, nullptr);
EXPECT_EQ(nullptr, cx);
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
cx = PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, static_cast<HpkeKdfId>(0xff),
HpkeAeadChaCha20Poly1305, nullptr, nullptr);
EXPECT_EQ(nullptr, cx);
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
cx = PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256,
static_cast<HpkeAeadId>(0xff), nullptr, nullptr);
EXPECT_EQ(nullptr, cx);
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
TEST_F(ModeParameterizedTest, InvalidReceiverKeyType) {
ScopedHpkeContext sender(
PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256,
HpkeAeadChaCha20Poly1305, nullptr, nullptr));
ASSERT_TRUE(!!sender);
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
ADD_FAILURE() << "No slot";
return;
}
// Give the client an RSA key
PK11RSAGenParams rsa_param;
rsa_param.keySizeInBits = 1024;
rsa_param.pe = 65537L;
SECKEYPublicKey *pub_tmp;
ScopedSECKEYPublicKey pub_key;
ScopedSECKEYPrivateKey priv_key(
PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, &rsa_param,
&pub_tmp, PR_FALSE, PR_FALSE, nullptr));
ASSERT_NE(nullptr, priv_key);
ASSERT_NE(nullptr, pub_tmp);
pub_key.reset(pub_tmp);
SECItem info_item = {siBuffer, nullptr, 0};
EXPECT_EQ(SECFailure, PK11_HPKE_SetupS(sender.get(), nullptr, nullptr,
pub_key.get(), &info_item));
EXPECT_EQ(SEC_ERROR_BAD_KEY, PORT_GetError());
// Try with an unexpected curve
StackSECItem ecParams;
SECOidData *oidData = SECOID_FindOIDByTag(SEC_OID_ANSIX962_EC_PRIME256V1);
ASSERT_NE(oidData, nullptr);
if (!SECITEM_AllocItem(nullptr, &ecParams, (2 + oidData->oid.len))) {
FAIL() << "Couldn't allocate memory for OID.";
}
ecParams.data[0] = SEC_ASN1_OBJECT_ID;
ecParams.data[1] = oidData->oid.len;
memcpy(ecParams.data + 2, oidData->oid.data, oidData->oid.len);
priv_key.reset(PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN,
&ecParams, &pub_tmp, PR_FALSE, PR_FALSE,
nullptr));
ASSERT_NE(nullptr, priv_key);
ASSERT_NE(nullptr, pub_tmp);
pub_key.reset(pub_tmp);
EXPECT_EQ(SECFailure, PK11_HPKE_SetupS(sender.get(), nullptr, nullptr,
pub_key.get(), &info_item));
EXPECT_EQ(SEC_ERROR_BAD_KEY, PORT_GetError());
}
TEST_F(ModeParameterizedTest, SetupLargeInfoLen) {
ScopedHpkeContext sender(
PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256,
HpkeAeadAes128Gcm, nullptr, nullptr));
ASSERT_TRUE(sender);
ScopedSECKEYPublicKey pub_key_r;
ScopedSECKEYPrivateKey priv_key_r;
ASSERT_TRUE(GenerateKeyPair(pub_key_r, priv_key_r));
// info->len near UINT_MAX must be rejected before reaching
// pk11_hpke_MakeExtractLabel
uint8_t info_data = 0;
SECItem oversized_info = {siBuffer, &info_data, 0xFFFFFFE7U};
EXPECT_EQ(SECFailure, PK11_HPKE_SetupS(sender.get(), nullptr, nullptr,
pub_key_r.get(), &oversized_info));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
// SetupR is also affected; use a valid sender to obtain enc first
ScopedHpkeContext sender2(
PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256,
HpkeAeadAes128Gcm, nullptr, nullptr));
ASSERT_TRUE(sender2);
SECItem valid_info = {siBuffer, &info_data, 1};
EXPECT_EQ(SECSuccess, PK11_HPKE_SetupS(sender2.get(), nullptr, nullptr,
pub_key_r.get(), &valid_info));
const SECItem *enc = PK11_HPKE_GetEncapPubKey(sender2.get());
ASSERT_NE(nullptr, enc);
ScopedHpkeContext receiver(
PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256,
HpkeAeadAes128Gcm, nullptr, nullptr));
ASSERT_TRUE(receiver);
EXPECT_EQ(SECFailure,
PK11_HPKE_SetupR(receiver.get(), pub_key_r.get(), priv_key_r.get(),
const_cast<SECItem *>(enc), &oversized_info));
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
}
} // namespace nss_test

View file

@ -0,0 +1,197 @@
/* -*- 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 "blapi.h"
#include "gtest/gtest.h"
#include "nss.h"
#include "nss_scoped_ptrs.h"
#include "pk11pub.h"
#include "secerr.h"
#include "sechash.h"
#include "util.h"
#include "databuffer.h"
#include "testvectors/ike-sha1-vectors.h"
#include "testvectors/ike-sha256-vectors.h"
#include "testvectors/ike-sha384-vectors.h"
#include "testvectors/ike-sha512-vectors.h"
#include "testvectors/ike-aesxcbc-vectors.h"
namespace nss_test {
class Pkcs11IkeTest : public ::testing::TestWithParam<
std::tuple<IkeTestVector, CK_MECHANISM_TYPE>> {
protected:
ScopedPK11SymKey ImportKey(SECItem &ikm_item) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
ADD_FAILURE() << "Can't get slot";
return nullptr;
}
ScopedPK11SymKey ikm(
PK11_ImportSymKey(slot.get(), CKM_GENERIC_SECRET_KEY_GEN,
PK11_OriginUnwrap, CKA_DERIVE, &ikm_item, nullptr));
return ikm;
}
void RunVectorTest(const IkeTestVector &vec, CK_MECHANISM_TYPE prf_mech) {
std::string msg = "Test #" + std::to_string(vec.id) + " failed";
std::vector<uint8_t> vec_ikm = hex_string_to_bytes(vec.ikm);
std::vector<uint8_t> vec_okm = hex_string_to_bytes(vec.okm);
std::vector<uint8_t> vec_gxykm = hex_string_to_bytes(vec.gxykm);
std::vector<uint8_t> vec_prevkm = hex_string_to_bytes(vec.prevkm);
std::vector<uint8_t> vec_Ni = hex_string_to_bytes(vec.Ni);
std::vector<uint8_t> vec_Nr = hex_string_to_bytes(vec.Nr);
std::vector<uint8_t> vec_seed_data = hex_string_to_bytes(vec.seed_data);
SECItem ikm_item = {siBuffer, vec_ikm.data(),
static_cast<unsigned int>(vec_ikm.size())};
SECItem okm_item = {siBuffer, vec_okm.data(),
static_cast<unsigned int>(vec_okm.size())};
SECItem prevkm_item = {siBuffer, vec_prevkm.data(),
static_cast<unsigned int>(vec_prevkm.size())};
SECItem gxykm_item = {siBuffer, vec_gxykm.data(),
static_cast<unsigned int>(vec_gxykm.size())};
CK_MECHANISM_TYPE derive_mech = CKM_NSS_IKE_PRF_DERIVE;
ScopedPK11SymKey gxy_key = nullptr;
ScopedPK11SymKey prev_key = nullptr;
ScopedPK11SymKey ikm = ImportKey(ikm_item);
// IKE_PRF structure (used in cases 1, 2 and 3)
CK_NSS_IKE_PRF_DERIVE_PARAMS nss_ike_prf_params = {
prf_mech,
CK_FALSE,
CK_FALSE,
vec_Ni.data(),
static_cast<CK_ULONG>(vec_Ni.size()),
vec_Nr.data(),
static_cast<CK_ULONG>(vec_Nr.size()),
CK_INVALID_HANDLE};
// IKE_V1_PRF, used to derive session keys.
CK_NSS_IKE1_PRF_DERIVE_PARAMS nss_ike_v1_prf_params = {
prf_mech, false,
CK_INVALID_HANDLE, CK_INVALID_HANDLE,
vec_Ni.data(), static_cast<CK_ULONG>(vec_Ni.size()),
vec_Nr.data(), static_cast<CK_ULONG>(vec_Nr.size()),
vec.key_number};
// IKE_V1_APP_B, do quick mode (all session keys in one call).
CK_NSS_IKE1_APP_B_PRF_DERIVE_PARAMS nss_ike_app_b_prf_params_quick = {
prf_mech, CK_FALSE, CK_INVALID_HANDLE, vec_seed_data.data(),
static_cast<CK_ULONG>(vec_seed_data.size())};
// IKE_V1_APP_B, used for long session keys in ike_v1
CK_MECHANISM_TYPE nss_ike_app_b_prf_params = prf_mech;
// IKE_PRF_PLUS, used to generate session keys in ike v2
CK_NSS_IKE_PRF_PLUS_DERIVE_PARAMS nss_ike_prf_plus_params = {
prf_mech, CK_FALSE, CK_INVALID_HANDLE, vec_seed_data.data(),
static_cast<CK_ULONG>(vec_seed_data.size())};
SECItem params_item = {siBuffer, (unsigned char *)&nss_ike_prf_params,
sizeof(nss_ike_prf_params)};
switch (vec.test_type) {
case IkeTestType::ikeGxy:
nss_ike_prf_params.bDataAsKey = true;
break;
case IkeTestType::ikeV1Psk:
break;
case IkeTestType::ikeV2Rekey:
nss_ike_prf_params.bRekey = true;
gxy_key = ImportKey(gxykm_item);
nss_ike_prf_params.hNewKey = PK11_GetSymKeyHandle(gxy_key.get());
break;
case IkeTestType::ikeV1:
derive_mech = CKM_NSS_IKE1_PRF_DERIVE;
params_item.data = (unsigned char *)&nss_ike_v1_prf_params;
params_item.len = sizeof(nss_ike_v1_prf_params);
gxy_key = ImportKey(gxykm_item);
nss_ike_v1_prf_params.hKeygxy = PK11_GetSymKeyHandle(gxy_key.get());
if (prevkm_item.len != 0) {
prev_key = ImportKey(prevkm_item);
nss_ike_v1_prf_params.bHasPrevKey = true;
nss_ike_v1_prf_params.hPrevKey = PK11_GetSymKeyHandle(prev_key.get());
}
break;
case IkeTestType::ikeV1AppB:
derive_mech = CKM_NSS_IKE1_APP_B_PRF_DERIVE;
params_item.data = (unsigned char *)&nss_ike_app_b_prf_params;
params_item.len = sizeof(nss_ike_app_b_prf_params);
break;
case IkeTestType::ikeV1AppBQuick:
derive_mech = CKM_NSS_IKE1_APP_B_PRF_DERIVE;
params_item.data = (unsigned char *)&nss_ike_app_b_prf_params_quick;
params_item.len = sizeof(nss_ike_app_b_prf_params_quick);
if (gxykm_item.len != 0) {
gxy_key = ImportKey(gxykm_item);
nss_ike_app_b_prf_params_quick.bHasKeygxy = true;
nss_ike_app_b_prf_params_quick.hKeygxy =
PK11_GetSymKeyHandle(gxy_key.get());
}
break;
case IkeTestType::ikePlus:
derive_mech = CKM_NSS_IKE_PRF_PLUS_DERIVE;
params_item.data = (unsigned char *)&nss_ike_prf_plus_params;
params_item.len = sizeof(nss_ike_prf_plus_params);
break;
default:
ADD_FAILURE() << msg;
return;
}
ASSERT_NE(nullptr, ikm) << msg;
ScopedPK11SymKey okm = ScopedPK11SymKey(
PK11_Derive(ikm.get(), derive_mech, &params_item,
CKM_GENERIC_SECRET_KEY_GEN, CKA_DERIVE, vec.size));
if (vec.valid) {
ASSERT_NE(nullptr, okm.get()) << msg;
ASSERT_EQ(SECSuccess, PK11_ExtractKeyValue(okm.get())) << msg;
SECItem *outItem = PK11_GetKeyData(okm.get());
SECItem nullItem = {siBuffer, NULL, 0};
if (outItem == NULL) {
outItem = &nullItem;
}
ASSERT_EQ(0, SECITEM_CompareItem(&okm_item, PK11_GetKeyData(okm.get())))
<< msg << std::endl
<< " expect:" << DataBuffer(okm_item.data, okm_item.len) << std::endl
<< " calc'd:" << DataBuffer(outItem->data, outItem->len) << std::endl;
} else {
ASSERT_EQ(nullptr, okm.get()) << msg;
}
}
};
TEST_P(Pkcs11IkeTest, IkeproofVectors) {
RunVectorTest(std::get<0>(GetParam()), std::get<1>(GetParam()));
}
INSTANTIATE_TEST_SUITE_P(
IkeSha1, Pkcs11IkeTest,
::testing::Combine(::testing::ValuesIn(kIkeSha1ProofVectors),
::testing::Values(CKM_SHA_1_HMAC)));
INSTANTIATE_TEST_SUITE_P(
IkeSha256, Pkcs11IkeTest,
::testing::Combine(::testing::ValuesIn(kIkeSha256ProofVectors),
::testing::Values(CKM_SHA256_HMAC)));
INSTANTIATE_TEST_SUITE_P(
IkeSha384, Pkcs11IkeTest,
::testing::Combine(::testing::ValuesIn(kIkeSha384ProofVectors),
::testing::Values(CKM_SHA384_HMAC)));
INSTANTIATE_TEST_SUITE_P(
IkeSha512, Pkcs11IkeTest,
::testing::Combine(::testing::ValuesIn(kIkeSha512ProofVectors),
::testing::Values(CKM_SHA512_HMAC)));
INSTANTIATE_TEST_SUITE_P(
IkeAESXCBC, Pkcs11IkeTest,
::testing::Combine(::testing::ValuesIn(kIkeAesXcbcProofVectors),
::testing::Values(CKM_AES_XCBC_MAC)));
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -255,10 +256,10 @@ 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));
INSTANTIATE_TEST_SUITE_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> {
@ -271,10 +272,10 @@ 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));
INSTANTIATE_TEST_SUITE_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,136 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <memory>
#include "nss.h"
#include "pk11pub.h"
#include "secerr.h"
#include "sechash.h"
#include "stdio.h"
#include "blapi.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "util.h"
namespace nss_test {
class Pkcs11KbkdfTest : public ::testing::Test {
protected:
ScopedPK11SymKey ImportKey(CK_MECHANISM_TYPE mech, SECItem *key_item) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
ADD_FAILURE() << "Can't get slot";
return nullptr;
}
ScopedPK11SymKey result(PK11_ImportSymKey(
slot.get(), mech, PK11_OriginUnwrap, CKA_SIGN, key_item, nullptr));
return result;
}
void RunKDF(CK_MECHANISM_TYPE kdfMech, CK_SP800_108_KDF_PARAMS_PTR kdfParams,
CK_BYTE_PTR inputKey, unsigned int inputKeyLen,
CK_BYTE_PTR expectedKey, unsigned int expectedKeyLen,
CK_BYTE_PTR expectedAdditional,
unsigned int expectedAdditionalLen) {
SECItem keyItem = {siBuffer, inputKey, inputKeyLen};
ScopedPK11SymKey p11Key = ImportKey(kdfParams->prfType, &keyItem);
ASSERT_NE(kdfParams, nullptr);
SECItem paramsItem = {siBuffer, (unsigned char *)kdfParams,
sizeof(*kdfParams)};
ScopedPK11SymKey result(PK11_Derive(p11Key.get(), kdfMech, &paramsItem,
CKM_SHA512_HMAC, CKA_SIGN,
expectedKeyLen));
ASSERT_NE(result, nullptr);
ASSERT_EQ(PK11_ExtractKeyValue(result.get()), SECSuccess);
/* We don't need to free this -- it is just a reference... */
SECItem *actualItem = PK11_GetKeyData(result.get());
ASSERT_NE(actualItem, nullptr);
SECItem expectedItem = {siBuffer, expectedKey, expectedKeyLen};
ASSERT_EQ(SECITEM_CompareItem(actualItem, &expectedItem), 0);
/* Extract the additional key. */
if (expectedAdditional == NULL || kdfParams->ulAdditionalDerivedKeys != 1) {
return;
}
ScopedPK11SlotInfo slot(PK11_GetSlotFromKey(result.get()));
CK_OBJECT_HANDLE_PTR keyHandle = kdfParams->pAdditionalDerivedKeys[0].phKey;
ScopedPK11SymKey additionalKey(
PK11_SymKeyFromHandle(slot.get(), result.get(), PK11_OriginDerive,
CKM_SHA512_HMAC, *keyHandle, PR_FALSE, NULL));
ASSERT_EQ(PK11_ExtractKeyValue(additionalKey.get()), SECSuccess);
/* We don't need to free this -- it is just a reference... */
actualItem = PK11_GetKeyData(additionalKey.get());
ASSERT_NE(actualItem, nullptr);
expectedItem = {siBuffer, expectedAdditional, expectedAdditionalLen};
ASSERT_EQ(SECITEM_CompareItem(actualItem, &expectedItem), 0);
}
};
TEST_F(Pkcs11KbkdfTest, TestAdditionalKey) {
/* Test number 11 of NIST CAVP vectors for Counter mode KDF, with counter
* after a fixed input (AES/128 CMAC). Resulting key (of size 256 bits)
* split into two 128-bit chunks since that aligns with a PRF invocation
* boundary. */
CK_BYTE inputKey[] = {0x23, 0xeb, 0x06, 0x5b, 0xe1, 0x27, 0xa8, 0x81,
0xe3, 0x5a, 0x65, 0x14, 0xd4, 0x35, 0x67, 0x9f};
CK_BYTE expectedKey[] = {0xea, 0x4e, 0xbb, 0xb4, 0xef, 0xff, 0x4b, 0x01,
0x68, 0x40, 0x12, 0xed, 0x8f, 0xf9, 0xc6, 0x4e};
CK_BYTE expectedAdditional[] = {0x70, 0xae, 0x38, 0x19, 0x7c, 0x36,
0x44, 0x5a, 0x6c, 0x80, 0x4a, 0x0e,
0x44, 0x81, 0x9a, 0xc3};
CK_SP800_108_COUNTER_FORMAT iterator = {CK_FALSE, 8};
CK_BYTE fixedData[] = {
0xe6, 0x79, 0x86, 0x1a, 0x61, 0x34, 0x65, 0xa6, 0x73, 0x85, 0x37, 0x26,
0x71, 0xb1, 0x07, 0xe6, 0xb8, 0x95, 0xa2, 0xf6, 0x40, 0x43, 0xc9, 0x34,
0xff, 0x42, 0x56, 0xa7, 0xe6, 0x3c, 0xfb, 0x8b, 0xfa, 0xcc, 0x21, 0x24,
0x25, 0x1c, 0x90, 0xfa, 0x67, 0x0d, 0x45, 0x74, 0x5c, 0x1c, 0x35, 0xda,
0x9b, 0x6e, 0x05, 0xaf, 0x77, 0xea, 0x9c, 0x4a, 0xd4, 0x86, 0xfd, 0x1a};
CK_PRF_DATA_PARAM dataParams[] = {
{CK_SP800_108_BYTE_ARRAY, fixedData,
sizeof(fixedData) / sizeof(*fixedData)},
{CK_SP800_108_ITERATION_VARIABLE, &iterator, sizeof(iterator)}};
CK_KEY_TYPE ckGeneric = CKK_GENERIC_SECRET;
CK_OBJECT_CLASS ckClass = CKO_SECRET_KEY;
CK_ULONG derivedLength = 16;
CK_ATTRIBUTE derivedTemplate[] = {
{CKA_CLASS, &ckClass, sizeof(ckClass)},
{CKA_KEY_TYPE, &ckGeneric, sizeof(ckGeneric)},
{CKA_VALUE_LEN, &derivedLength, sizeof(derivedLength)}};
CK_OBJECT_HANDLE keyHandle;
CK_DERIVED_KEY derivedKey = {
derivedTemplate, sizeof(derivedTemplate) / sizeof(*derivedTemplate),
&keyHandle};
CK_SP800_108_KDF_PARAMS kdfParams = {CKM_AES_CMAC,
sizeof(dataParams) / sizeof(*dataParams),
dataParams, 1, &derivedKey};
RunKDF(CKM_SP800_108_COUNTER_KDF, &kdfParams, inputKey,
sizeof(inputKey) / sizeof(*inputKey), expectedKey,
sizeof(expectedKey) / sizeof(*expectedKey), expectedAdditional,
sizeof(expectedAdditional) / sizeof(*expectedAdditional));
}
// Close the namespace
} // namespace nss_test

View file

@ -71,10 +71,10 @@ 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));
INSTANTIATE_TEST_SUITE_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

@ -22,7 +22,8 @@ class ParamHolder {
};
void Pkcs11KeyPairGenerator::GenerateKey(ScopedSECKEYPrivateKey* priv_key,
ScopedSECKEYPublicKey* pub_key) const {
ScopedSECKEYPublicKey* pub_key,
bool sensitive) const {
// This function returns if an assertion fails, so don't leak anything.
priv_key->reset(nullptr);
pub_key->reset(nullptr);
@ -34,10 +35,11 @@ void Pkcs11KeyPairGenerator::GenerateKey(ScopedSECKEYPrivateKey* priv_key,
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());
ScopedSECKEYPrivateKey priv_tmp(
PK11_GenerateKeyPair(slot.get(), mech_, params->get(), &pub_tmp, PR_FALSE,
sensitive ? PR_TRUE : PR_FALSE, nullptr));
ASSERT_NE(nullptr, priv_tmp)
<< "PK11_GenerateKeyPair failed: " << PORT_ErrorToName(PORT_GetError());
ASSERT_NE(nullptr, pub_tmp);
priv_key->swap(priv_tmp);

View file

@ -22,7 +22,7 @@ class Pkcs11KeyPairGenerator {
SECOidTag curve() const { return curve_; }
void GenerateKey(ScopedSECKEYPrivateKey* priv_key,
ScopedSECKEYPublicKey* pub_key) const;
ScopedSECKEYPublicKey* pub_key, bool sensitive = true) const;
private:
std::unique_ptr<ParamHolder> MakeParams() const;

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -21,9 +22,9 @@ class Pkcs11ModuleTest : public ::testing::Test {
Pkcs11ModuleTest() {}
void SetUp() override {
ASSERT_EQ(SECSuccess, SECMOD_AddNewModule("Pkcs11ModuleTest", DLL_PREFIX
"pkcs11testmodule." DLL_SUFFIX,
0, 0))
ASSERT_EQ(SECSuccess, SECMOD_AddNewModule(
"Pkcs11ModuleTest",
DLL_PREFIX "pkcs11testmodule." DLL_SUFFIX, 0, 0))
<< PORT_ErrorToName(PORT_GetError());
}
@ -42,10 +43,10 @@ TEST_F(Pkcs11ModuleTest, LoadUnload) {
TEST_F(Pkcs11ModuleTest, ListSlots) {
ScopedPK11SlotList slots(
PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, nullptr));
EXPECT_NE(nullptr, slots);
ASSERT_NE(nullptr, slots);
PK11SlotListElement* element = PK11_GetFirstSafe(slots.get());
EXPECT_NE(nullptr, element);
PK11SlotListElement *element = PK11_GetFirstSafe(slots.get());
ASSERT_NE(nullptr, element);
// These tokens are always present.
const std::vector<std::string> kSlotsWithToken = {
@ -71,13 +72,87 @@ TEST_F(Pkcs11ModuleTest, PublicCertificatesToken) {
const std::string kPublicCertificatesToken = "Test PKCS11 Public Certs Token";
ScopedPK11SlotInfo slot1(PK11_FindSlotByName(kRegularToken.c_str()));
EXPECT_NE(nullptr, slot1);
ASSERT_NE(nullptr, slot1);
EXPECT_FALSE(PK11_IsFriendly(slot1.get()));
ScopedPK11SlotInfo slot2(
PK11_FindSlotByName(kPublicCertificatesToken.c_str()));
EXPECT_NE(nullptr, slot2);
ASSERT_NE(nullptr, slot2);
EXPECT_TRUE(PK11_IsFriendly(slot2.get()));
}
TEST_F(Pkcs11ModuleTest, PublicCertificatesTokenLookup) {
const std::string kCertUrl =
"pkcs11:id=%10%11%12%13%14%15%16%17%18%19%1a%1b%1c%1d%1e%1f";
ScopedCERTCertList certsByUrl(
PK11_FindCertsFromURI(kCertUrl.c_str(), nullptr));
EXPECT_NE(nullptr, certsByUrl.get());
size_t count = 0;
CERTCertificate *certByUrl = nullptr;
for (CERTCertListNode *node = CERT_LIST_HEAD(certsByUrl);
!CERT_LIST_END(node, certsByUrl); node = CERT_LIST_NEXT(node)) {
if (count == 0) {
certByUrl = node->cert;
}
count++;
}
EXPECT_EQ(1UL, count);
EXPECT_NE(nullptr, certByUrl);
EXPECT_EQ(
0, strcmp(certByUrl->nickname, "Test PKCS11 Public Certs Token:cert2"));
}
TEST_F(Pkcs11ModuleTest, PublicCertificatesTokenLookupNoMatch) {
const std::string kCertUrl =
"pkcs11:id=%00%01%02%03%04%05%06%07%08%09%0a%0b%0c%0d%0e%0e";
ScopedCERTCertList certsByUrl(
PK11_FindCertsFromURI(kCertUrl.c_str(), nullptr));
EXPECT_EQ(nullptr, certsByUrl.get());
}
#if defined(_WIN32)
#include <windows.h>
class Pkcs11NonAsciiTest : public ::testing::Test {
WCHAR nonAsciiModuleName[MAX_PATH];
public:
Pkcs11NonAsciiTest() {}
void SetUp() override {
WCHAR originalModuleName[MAX_PATH];
LPWSTR filePart;
DWORD count = SearchPathW(NULL, L"pkcs11testmodule.dll", NULL, MAX_PATH,
nonAsciiModuleName, &filePart);
ASSERT_TRUE(count);
wcscpy(originalModuleName, nonAsciiModuleName);
wcscpy(filePart, L"pkcs11testmodule\u2665.dll");
BOOL result = CopyFileW(originalModuleName, nonAsciiModuleName, TRUE);
ASSERT_TRUE(result);
ASSERT_EQ(SECSuccess,
SECMOD_AddNewModule(
"Pkcs11NonAsciiTest",
DLL_PREFIX "pkcs11testmodule\xE2\x99\xA5." DLL_SUFFIX, 0, 0))
<< PORT_ErrorToName(PORT_GetError());
}
void TearDown() override {
int type;
ASSERT_EQ(SECSuccess, SECMOD_DeleteModule("Pkcs11NonAsciiTest", &type));
ASSERT_EQ(SECMOD_EXTERNAL, type);
BOOL result = DeleteFileW(nonAsciiModuleName);
ASSERT_TRUE(result);
}
};
TEST_F(Pkcs11NonAsciiTest, LoadUnload) {
ScopedSECMODModule module(SECMOD_FindModule("Pkcs11NonAsciiTest"));
EXPECT_NE(nullptr, module);
}
#endif // defined(_WIN32)
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -50,6 +51,7 @@ class Pkcs11Pbkdf2Test : public ::testing::Test {
const unsigned int kIterations = 10;
std::string pass("passwordPASSWORDpassword");
std::string salt("saltSALTsaltSALTsaltSALTsaltSALTsalt");
std::string salt_empty("");
// Derivation must fail when using key sizes bigger than MAX_KEY_LEN.
const int big_key_size = 768;
@ -59,6 +61,10 @@ class Pkcs11Pbkdf2Test : public ::testing::Test {
const int zero_key_size = 0;
EXPECT_TRUE(KeySizeParam(pass, salt, zero_key_size, hash_alg, kIterations));
// Zero is acceptable as salt size and will be managed internally.
EXPECT_TRUE(
KeySizeParam(pass, salt_empty, zero_key_size, hash_alg, kIterations));
// -1 will be set to 0 internally and this means that the key size will be
// obtained from the template. If the template doesn't have this defined,
// it must fail.
@ -70,6 +76,12 @@ class Pkcs11Pbkdf2Test : public ::testing::Test {
const int negative_key_size = -10;
EXPECT_FALSE(
KeySizeParam(pass, salt, negative_key_size, hash_alg, kIterations));
// Malformed inputs are handled without crashing
EXPECT_FALSE(
MalformedPass(pass, salt, big_key_size, hash_alg, kIterations));
EXPECT_FALSE(
MalformedSalt(pass, salt, big_key_size, hash_alg, kIterations));
}
private:
@ -98,13 +110,8 @@ class Pkcs11Pbkdf2Test : public ::testing::Test {
return !memcmp(&derived[0], key_data->data, key_data->len);
}
bool KeySizeParam(std::string& pass, std::string& salt, const int key_size,
SECOidTag hash_alg, unsigned int kIterations) {
SECItem pass_item = {siBuffer, ToUcharPtr(pass),
static_cast<unsigned int>(pass.length())};
SECItem salt_item = {siBuffer, ToUcharPtr(salt),
static_cast<unsigned int>(salt.length())};
bool GenerateKey(SECItem pass_item, SECItem salt_item, const int key_size,
SECOidTag hash_alg, unsigned int kIterations) {
// Set up PBKDF2 params.
ScopedSECAlgorithmID alg_id(
PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, hash_alg, hash_alg,
@ -118,6 +125,34 @@ class Pkcs11Pbkdf2Test : public ::testing::Test {
// Should be nullptr if fail.
return sym_key.get();
}
bool KeySizeParam(std::string& pass, std::string& salt, const int key_size,
SECOidTag hash_alg, unsigned int kIterations) {
SECItem pass_item = {siBuffer, ToUcharPtr(pass),
static_cast<unsigned int>(pass.length())};
SECItem salt_item = {siBuffer, ToUcharPtr(salt),
static_cast<unsigned int>(salt.length())};
return GenerateKey(pass_item, salt_item, key_size, hash_alg, kIterations);
}
bool MalformedSalt(std::string& pass, std::string& salt, const int key_size,
SECOidTag hash_alg, unsigned int kIterations) {
SECItem pass_item = {siBuffer, ToUcharPtr(pass),
static_cast<unsigned int>(pass.length())};
SECItem salt_item = {siBuffer, nullptr, 0};
return GenerateKey(pass_item, salt_item, key_size, hash_alg, kIterations);
}
bool MalformedPass(std::string& pass, std::string& salt, const int key_size,
SECOidTag hash_alg, unsigned int kIterations) {
SECItem pass_item = {siBuffer, nullptr, 0};
SECItem salt_item = {siBuffer, ToUcharPtr(salt),
static_cast<unsigned int>(salt.length())};
return GenerateKey(pass_item, salt_item, key_size, hash_alg, kIterations);
}
};
// RFC 6070 <http://tools.ietf.org/html/rfc6070>

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

View file

@ -0,0 +1,204 @@
/* -*- 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 <algorithm>
#include <cstdint>
#include "cpputil.h"
#include "cryptohi.h"
#include "json_reader.h"
#include "gtest/gtest.h"
#include "limits.h"
#include "nss.h"
#include "nss_scoped_ptrs.h"
#include "pk11pub.h"
#include "databuffer.h"
#include "testvectors/rsa_signature-vectors.h"
#include "testvectors/rsaencrypt_bb2048-vectors.h"
#include "testvectors/rsaencrypt_bb3072-vectors.h"
namespace nss_test {
class RsaDecryptWycheproofTest : public ::testing::Test {
protected:
void Run(const std::string& name) {
WycheproofHeader(name, "RSAES-PKCS1-v1_5",
"rsaes_pkcs1_decrypt_schema.json",
[this](JsonReader& r) { RunGroup(r); });
}
void TestDecrypt(const RsaDecryptTestVector& vec) {
SECItem pkcs8_item = {siBuffer, toUcharPtr(vec.priv_key.data()),
static_cast<unsigned int>(vec.priv_key.size())};
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
EXPECT_NE(nullptr, slot);
SECKEYPrivateKey* key = nullptr;
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot.get(), &pkcs8_item, nullptr, nullptr, false, false, KU_ALL, &key,
nullptr);
ASSERT_EQ(SECSuccess, rv);
ASSERT_NE(nullptr, key);
ScopedSECKEYPrivateKey priv_key(key);
// Decrypt
std::vector<uint8_t> decrypted(PR_MAX(1, vec.ct.size()));
unsigned int decrypted_len = 0;
rv = PK11_PrivDecryptPKCS1(priv_key.get(), decrypted.data(), &decrypted_len,
decrypted.size(), vec.ct.data(), vec.ct.size());
decrypted.resize(decrypted_len);
if (vec.valid) {
ASSERT_EQ(SECSuccess, rv);
EXPECT_EQ(vec.msg, decrypted);
} else if (vec.invalid_padding) {
// If the padding is bad, decryption should succeed and produce
// (pseudo)random output.
ASSERT_EQ(SECSuccess, rv);
ASSERT_NE(vec.msg, decrypted);
} else {
ASSERT_EQ(SECFailure, rv)
<< "Returned:" << DataBuffer(decrypted.data(), decrypted.size());
}
};
private:
void RunGroup(JsonReader& r) {
std::vector<RsaDecryptTestVector> tests;
std::vector<uint8_t> private_key;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "d" || n == "e" || n == "keysize" || n == "n" ||
n == "privateKeyJwk" || n == "privateKeyPem") {
r.SkipValue();
} else if (n == "privateKeyPkcs8") {
private_key = r.ReadHex();
} else if (n == "type") {
ASSERT_EQ("RsaesPkcs1Decrypt", r.ReadString());
} else if (n == "tests") {
WycheproofReadTests(r, &tests, ReadTestAttr, false,
[](RsaDecryptTestVector& t, const std::string&,
const std::vector<std::string>& flags) {
t.invalid_padding =
std::find(flags.begin(), flags.end(),
"InvalidPkcs1Padding") !=
flags.end();
});
} else {
FAIL() << "unknown label in group: " << n;
}
}
for (auto& t : tests) {
std::cout << "Running test " << t.id << std::endl;
t.priv_key = private_key;
TestDecrypt(t);
}
}
static void ReadTestAttr(RsaDecryptTestVector& t, const std::string& n,
JsonReader& r) {
if (n == "msg") {
t.msg = r.ReadHex();
} else if (n == "ct") {
t.ct = r.ReadHex();
} else {
FAIL() << "unsupported test case field: " << n;
}
}
};
TEST_F(RsaDecryptWycheproofTest, Rsa2048) { Run("rsa_pkcs1_2048"); }
TEST_F(RsaDecryptWycheproofTest, Rsa3072) { Run("rsa_pkcs1_3072"); }
TEST_F(RsaDecryptWycheproofTest, Rsa4096) { Run("rsa_pkcs1_4096"); }
TEST_F(RsaDecryptWycheproofTest, Bb2048) {
for (auto& t : kRsaBb2048Vectors) {
RsaDecryptTestVector copy = t;
copy.priv_key = kRsaBb2048;
TestDecrypt(copy);
}
}
TEST_F(RsaDecryptWycheproofTest, Bb2049) {
for (auto& t : kRsaBb2049Vectors) {
RsaDecryptTestVector copy = t;
copy.priv_key = kRsaBb2049;
TestDecrypt(copy);
}
}
TEST_F(RsaDecryptWycheproofTest, Bb3072) {
for (auto& t : kRsaBb3072Vectors) {
RsaDecryptTestVector copy = t;
copy.priv_key = kRsaBb3072;
TestDecrypt(copy);
}
}
TEST(RsaEncryptTest, MessageLengths) {
const uint8_t spki[] = {
0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81,
0x89, 0x02, 0x81, 0x81, 0x00, 0xf8, 0xb8, 0x6c, 0x83, 0xb4, 0xbc, 0xd9,
0xa8, 0x57, 0xc0, 0xa5, 0xb4, 0x59, 0x76, 0x8c, 0x54, 0x1d, 0x79, 0xeb,
0x22, 0x52, 0x04, 0x7e, 0xd3, 0x37, 0xeb, 0x41, 0xfd, 0x83, 0xf9, 0xf0,
0xa6, 0x85, 0x15, 0x34, 0x75, 0x71, 0x5a, 0x84, 0xa8, 0x3c, 0xd2, 0xef,
0x5a, 0x4e, 0xd3, 0xde, 0x97, 0x8a, 0xdd, 0xff, 0xbb, 0xcf, 0x0a, 0xaa,
0x86, 0x92, 0xbe, 0xb8, 0x50, 0xe4, 0xcd, 0x6f, 0x80, 0x33, 0x30, 0x76,
0x13, 0x8f, 0xca, 0x7b, 0xdc, 0xec, 0x5a, 0xca, 0x63, 0xc7, 0x03, 0x25,
0xef, 0xa8, 0x8a, 0x83, 0x58, 0x76, 0x20, 0xfa, 0x16, 0x77, 0xd7, 0x79,
0x92, 0x63, 0x01, 0x48, 0x1a, 0xd8, 0x7b, 0x67, 0xf1, 0x52, 0x55, 0x49,
0x4e, 0xd6, 0x6e, 0x4a, 0x5c, 0xd7, 0x7a, 0x37, 0x36, 0x0c, 0xde, 0xdd,
0x8f, 0x44, 0xe8, 0xc2, 0xa7, 0x2c, 0x2b, 0xb5, 0xaf, 0x64, 0x4b, 0x61,
0x07, 0x02, 0x03, 0x01, 0x00, 0x01,
};
// Import public key (use pre-generated for performance).
SECItem spki_item = {siBuffer, toUcharPtr(spki), sizeof(spki)};
ScopedCERTSubjectPublicKeyInfo cert_spki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
ASSERT_TRUE(cert_spki);
ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get()));
ASSERT_TRUE(pub_key);
int mod_len = SECKEY_PublicKeyStrength(pub_key.get());
ASSERT_TRUE(mod_len > 0);
std::vector<uint8_t> ctxt(mod_len);
unsigned int ctxt_len;
std::vector<uint8_t> msg(mod_len, 0xff);
// Test with valid inputs
SECStatus rv =
PK11_PubEncrypt(pub_key.get(), CKM_RSA_PKCS, nullptr, ctxt.data(),
&ctxt_len, mod_len, msg.data(), 1, nullptr);
ASSERT_EQ(SECSuccess, rv);
// Maximum message length is mod_len - miniumum padding (8B) - flags (3B)
unsigned int max_msg_len = mod_len - 8 - 3;
rv = PK11_PubEncrypt(pub_key.get(), CKM_RSA_PKCS, nullptr, ctxt.data(),
&ctxt_len, mod_len, msg.data(), max_msg_len, nullptr);
ASSERT_EQ(SECSuccess, rv);
// Test one past maximum length
rv =
PK11_PubEncrypt(pub_key.get(), CKM_RSA_PKCS, nullptr, ctxt.data(),
&ctxt_len, mod_len, msg.data(), max_msg_len + 1, nullptr);
ASSERT_EQ(SECFailure, rv);
// Make sure the the length will not overflow - i.e.
// (padLen = modulusLen - (UINT_MAX + MINIMUM_PAD_LEN)) may overflow and
// result in a value that appears valid.
rv = PK11_PubEncrypt(pub_key.get(), CKM_RSA_PKCS, nullptr, ctxt.data(),
&ctxt_len, UINT_MAX, msg.data(), UINT_MAX, nullptr);
ASSERT_EQ(SECFailure, rv);
}
} // namespace nss_test

View file

@ -0,0 +1,285 @@
/* -*- 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 <stdint.h>
#include "cpputil.h"
#include "cryptohi.h"
#include "json_reader.h"
#include "gtest/gtest.h"
#include "limits.h"
#include "nss.h"
#include "nss_scoped_ptrs.h"
#include "pk11pub.h"
#include "testvectors_base/test-structs.h"
namespace nss_test {
struct RsaOaepTestVector {
uint32_t id;
std::vector<uint8_t> msg;
std::vector<uint8_t> ct;
std::vector<uint8_t> label;
bool valid;
};
class RsaOaepWycheproofTest : public ::testing::Test {
protected:
void Run(const std::string& file) {
WycheproofHeader(file, "RSAES-OAEP", "rsaes_oaep_decrypt_schema.json",
[this](JsonReader& r) { RunGroup(r); });
}
void TestDecrypt(ScopedSECKEYPrivateKey& priv_key, SECOidTag hash_oid,
CK_RSA_PKCS_MGF_TYPE mgf_hash,
const RsaOaepTestVector& vec) {
// Set up the OAEP parameters.
CK_RSA_PKCS_OAEP_PARAMS oaepParams;
oaepParams.source = CKZ_DATA_SPECIFIED;
oaepParams.pSourceData = const_cast<unsigned char*>(vec.label.data());
oaepParams.ulSourceDataLen = vec.label.size();
oaepParams.mgf = mgf_hash;
oaepParams.hashAlg = HashOidToHashMech(hash_oid);
SECItem params_item = {siBuffer,
toUcharPtr(reinterpret_cast<uint8_t*>(&oaepParams)),
static_cast<unsigned int>(sizeof(oaepParams))};
// Decrypt.
std::vector<uint8_t> decrypted(PR_MAX(1, vec.ct.size()));
unsigned int decrypted_len = 0;
SECStatus rv = PK11_PrivDecrypt(
priv_key.get(), CKM_RSA_PKCS_OAEP, &params_item, decrypted.data(),
&decrypted_len, decrypted.size(), vec.ct.data(), vec.ct.size());
if (vec.valid) {
EXPECT_EQ(SECSuccess, rv);
decrypted.resize(decrypted_len);
EXPECT_EQ(vec.msg, decrypted);
} else {
EXPECT_EQ(SECFailure, rv);
}
};
private:
void RunGroup(JsonReader& r) {
std::vector<RsaOaepTestVector> tests;
ScopedSECKEYPrivateKey private_key;
CK_MECHANISM_TYPE mgf_hash = CKM_INVALID_MECHANISM;
SECOidTag hash_oid = SEC_OID_UNKNOWN;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "d" || n == "e" || n == "keysize" || n == "n" ||
n == "privateKeyJwk" || n == "privateKeyPem") {
r.SkipValue();
} else if (n == "privateKeyPkcs8") {
std::vector<uint8_t> priv_key = r.ReadHex();
private_key = LoadPrivateKey(priv_key);
} else if (n == "mgf") {
ASSERT_EQ("MGF1", r.ReadString());
} else if (n == "mgfSha") {
mgf_hash = HashOidToHashMech(r.ReadHash());
} else if (n == "sha") {
hash_oid = r.ReadHash();
} else if (n == "type") {
ASSERT_EQ("RsaesOaepDecrypt", r.ReadString());
} else if (n == "tests") {
WycheproofReadTests(r, &tests, ReadTestAttr);
} else {
FAIL() << "unknown label in group: " << n;
}
}
for (auto& t : tests) {
TestDecrypt(private_key, hash_oid, mgf_hash, t);
}
}
ScopedSECKEYPrivateKey LoadPrivateKey(const std::vector<uint8_t>& priv_key) {
SECItem pkcs8_item = {siBuffer, toUcharPtr(priv_key.data()),
static_cast<unsigned int>(priv_key.size())};
ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
EXPECT_NE(nullptr, slot);
SECKEYPrivateKey* key = nullptr;
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot.get(), &pkcs8_item, nullptr, nullptr, false, false, KU_ALL, &key,
nullptr);
EXPECT_EQ(SECSuccess, rv);
EXPECT_NE(nullptr, key);
return ScopedSECKEYPrivateKey(key);
}
static void ReadTestAttr(RsaOaepTestVector& t, const std::string& n,
JsonReader& r) {
if (n == "msg") {
t.msg = r.ReadHex();
} else if (n == "ct") {
t.ct = r.ReadHex();
} else if (n == "label") {
t.label = r.ReadHex();
} else {
FAIL() << "unsupported test case field: " << n;
}
}
inline CK_MECHANISM_TYPE HashOidToHashMech(SECOidTag hash_oid) {
switch (hash_oid) {
case SEC_OID_SHA1:
return CKM_SHA_1;
case SEC_OID_SHA224:
return CKM_SHA224;
case SEC_OID_SHA256:
return CKM_SHA256;
case SEC_OID_SHA384:
return CKM_SHA384;
case SEC_OID_SHA512:
return CKM_SHA512;
default:
ADD_FAILURE();
}
return CKM_INVALID_MECHANISM;
}
};
TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha1) {
Run("rsa_oaep_2048_sha1_mgf1sha1");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha256MgfSha1) {
Run("rsa_oaep_2048_sha256_mgf1sha1");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha256) {
Run("rsa_oaep_2048_sha256_mgf1sha256");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha384MgfSha1) {
Run("rsa_oaep_2048_sha384_mgf1sha1");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha384) {
Run("rsa_oaep_2048_sha384_mgf1sha384");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha512MgfSha1) {
Run("rsa_oaep_2048_sha512_mgf1sha1");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha512) {
Run("rsa_oaep_2048_sha512_mgf1sha512");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep3072Sha256MgfSha1) {
Run("rsa_oaep_3072_sha256_mgf1sha1");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep3072Sha256) {
Run("rsa_oaep_3072_sha256_mgf1sha256");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep3072Sha512MgfSha1) {
Run("rsa_oaep_3072_sha512_mgf1sha1");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep3072Sha512) {
Run("rsa_oaep_3072_sha512_mgf1sha512");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep4096Sha256MgfSha1) {
Run("rsa_oaep_4096_sha256_mgf1sha1");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep4096Sha256) {
Run("rsa_oaep_4096_sha256_mgf1sha256");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep4096Sha512MgfSha1) {
Run("rsa_oaep_4096_sha512_mgf1sha1");
}
TEST_F(RsaOaepWycheproofTest, RsaOaep4096Sha512) {
Run("rsa_oaep_4096_sha512_mgf1sha512");
}
TEST_F(RsaOaepWycheproofTest, RsaOaepMisc) { Run("rsa_oaep_misc"); }
TEST(Pkcs11RsaOaepTest, TestOaepWrapUnwrap) {
const size_t kRsaKeyBits = 2048;
const size_t kwrappedBufLen = 4096;
SECStatus rv = SECFailure;
ScopedSECKEYPrivateKey priv;
ScopedSECKEYPublicKey pub;
PK11RSAGenParams rsa_params;
rsa_params.keySizeInBits = kRsaKeyBits;
rsa_params.pe = 65537;
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_NE(slot, nullptr);
SECKEYPublicKey* p_pub_tmp = nullptr;
priv.reset(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
&rsa_params, &p_pub_tmp, false, false,
nullptr));
pub.reset(p_pub_tmp);
ASSERT_NE(priv.get(), nullptr);
ASSERT_NE(pub.get(), nullptr);
ScopedPK11SymKey to_wrap(
PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
CK_RSA_PKCS_OAEP_PARAMS oaep_params = {CKM_SHA256, CKG_MGF1_SHA256,
CKZ_DATA_SPECIFIED, NULL, 0};
SECItem param = {siBuffer, (unsigned char*)&oaep_params, sizeof(oaep_params)};
ScopedSECItem wrapped(SECITEM_AllocItem(nullptr, nullptr, kwrappedBufLen));
rv = PK11_PubWrapSymKeyWithMechanism(pub.get(), CKM_RSA_PKCS_OAEP, &param,
to_wrap.get(), wrapped.get());
ASSERT_EQ(rv, SECSuccess);
PK11SymKey* p_unwrapped_tmp = nullptr;
// Extract key's value in order to validate decryption worked.
rv = PK11_ExtractKeyValue(to_wrap.get());
ASSERT_EQ(rv, SECSuccess);
// References owned by PKCS#11 layer; no need to scope and free.
SECItem* expectedItem = PK11_GetKeyData(to_wrap.get());
// This assumes CKM_RSA_PKCS and doesn't understand OAEP.
// CKM_RSA_PKCS cannot safely return errors, however, as it can lead
// to Bleichenbacher-like attacks. To solve this there's a new definition
// that generates fake key material based on the message and private key.
// This returned key material will not be the key we were expecting, so
// make sure that's the case:
p_unwrapped_tmp = PK11_PubUnwrapSymKey(priv.get(), wrapped.get(), CKM_AES_CBC,
CKA_DECRYPT, 16);
// As long as the wrapped data is the same length as the key
// (which it should be), then CKM_RSA_PKCS should not fail.
ASSERT_NE(p_unwrapped_tmp, nullptr);
ScopedPK11SymKey fakeUnwrapped;
fakeUnwrapped.reset(p_unwrapped_tmp);
rv = PK11_ExtractKeyValue(fakeUnwrapped.get());
ASSERT_EQ(rv, SECSuccess);
// References owned by PKCS#11 layer; no need to scope and free.
SECItem* fakeItem = PK11_GetKeyData(fakeUnwrapped.get());
ASSERT_NE(SECITEM_CompareItem(fakeItem, expectedItem), 0);
ScopedPK11SymKey unwrapped;
p_unwrapped_tmp = PK11_PubUnwrapSymKeyWithMechanism(
priv.get(), CKM_RSA_PKCS_OAEP, &param, wrapped.get(), CKM_AES_CBC,
CKA_DECRYPT, 16);
ASSERT_NE(p_unwrapped_tmp, nullptr);
unwrapped.reset(p_unwrapped_tmp);
rv = PK11_ExtractKeyValue(unwrapped.get());
ASSERT_EQ(rv, SECSuccess);
// References owned by PKCS#11 layer; no need to scope and free.
SECItem* actualItem = PK11_GetKeyData(unwrapped.get());
ASSERT_EQ(SECITEM_CompareItem(actualItem, expectedItem), 0);
}
} // namespace nss_test

View file

@ -1,101 +1,310 @@
/* -*- 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 <stdint.h>
#include <algorithm>
#include <cstdint>
#include <memory>
#include "cryptohi.h"
#include "nss.h"
#include "pk11pub.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "cpputil.h"
#include "databuffer.h"
#include "json_reader.h"
#include "gtest/gtest.h"
#include "nss.h"
#include "nss_scoped_ptrs.h"
#include "pk11pub.h"
#include "secerr.h"
#include "sechash.h"
#include "pk11_signature_test.h"
#include "testvectors/rsa_signature-vectors.h"
namespace nss_test {
// Test that the RSASSA-PKCS1-v1_5 implementation enforces the missing NULL
// parameter.
TEST(RsaPkcs1Test, RequireNullParameter) {
// kSpki is an RSA public key in an X.509 SubjectPublicKeyInfo.
const uint8_t kSpki[] = {
0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81,
0x89, 0x02, 0x81, 0x81, 0x00, 0xf8, 0xb8, 0x6c, 0x83, 0xb4, 0xbc, 0xd9,
0xa8, 0x57, 0xc0, 0xa5, 0xb4, 0x59, 0x76, 0x8c, 0x54, 0x1d, 0x79, 0xeb,
0x22, 0x52, 0x04, 0x7e, 0xd3, 0x37, 0xeb, 0x41, 0xfd, 0x83, 0xf9, 0xf0,
0xa6, 0x85, 0x15, 0x34, 0x75, 0x71, 0x5a, 0x84, 0xa8, 0x3c, 0xd2, 0xef,
0x5a, 0x4e, 0xd3, 0xde, 0x97, 0x8a, 0xdd, 0xff, 0xbb, 0xcf, 0x0a, 0xaa,
0x86, 0x92, 0xbe, 0xb8, 0x50, 0xe4, 0xcd, 0x6f, 0x80, 0x33, 0x30, 0x76,
0x13, 0x8f, 0xca, 0x7b, 0xdc, 0xec, 0x5a, 0xca, 0x63, 0xc7, 0x03, 0x25,
0xef, 0xa8, 0x8a, 0x83, 0x58, 0x76, 0x20, 0xfa, 0x16, 0x77, 0xd7, 0x79,
0x92, 0x63, 0x01, 0x48, 0x1a, 0xd8, 0x7b, 0x67, 0xf1, 0x52, 0x55, 0x49,
0x4e, 0xd6, 0x6e, 0x4a, 0x5c, 0xd7, 0x7a, 0x37, 0x36, 0x0c, 0xde, 0xdd,
0x8f, 0x44, 0xe8, 0xc2, 0xa7, 0x2c, 0x2b, 0xb5, 0xaf, 0x64, 0x4b, 0x61,
0x07, 0x02, 0x03, 0x01, 0x00, 0x01,
};
// kHash is the SHA-256 hash of {1,2,3,4}.
const uint8_t kHash[] = {
0x9f, 0x64, 0xa7, 0x47, 0xe1, 0xb9, 0x7f, 0x13, 0x1f, 0xab, 0xb6,
0xb4, 0x47, 0x29, 0x6c, 0x9b, 0x6f, 0x02, 0x01, 0xe7, 0x9f, 0xb3,
0xc5, 0x35, 0x6e, 0x6c, 0x77, 0xe8, 0x9b, 0x6a, 0x80, 0x6a,
};
// kSignature is the signature of kHash with RSASSA-PKCS1-v1_5.
const uint8_t kSignature[] = {
0xa5, 0xf0, 0x8a, 0x47, 0x5d, 0x3c, 0xb3, 0xcc, 0xa9, 0x79, 0xaf, 0x4d,
0x8c, 0xae, 0x4c, 0x14, 0xef, 0xc2, 0x0b, 0x34, 0x36, 0xde, 0xf4, 0x3e,
0x3d, 0xbb, 0x4a, 0x60, 0x5c, 0xc8, 0x91, 0x28, 0xda, 0xfb, 0x7e, 0x04,
0x96, 0x7e, 0x63, 0x13, 0x90, 0xce, 0xb9, 0xb4, 0x62, 0x7a, 0xfd, 0x09,
0x3d, 0xc7, 0x67, 0x78, 0x54, 0x04, 0xeb, 0x52, 0x62, 0x6e, 0x24, 0x67,
0xb4, 0x40, 0xfc, 0x57, 0x62, 0xc6, 0xf1, 0x67, 0xc1, 0x97, 0x8f, 0x6a,
0xa8, 0xae, 0x44, 0x46, 0x5e, 0xab, 0x67, 0x17, 0x53, 0x19, 0x3a, 0xda,
0x5a, 0xc8, 0x16, 0x3e, 0x86, 0xd5, 0xc5, 0x71, 0x2f, 0xfc, 0x23, 0x48,
0xd9, 0x0b, 0x13, 0xdd, 0x7b, 0x5a, 0x25, 0x79, 0xef, 0xa5, 0x7b, 0x04,
0xed, 0x44, 0xf6, 0x18, 0x55, 0xe4, 0x0a, 0xe9, 0x57, 0x79, 0x5d, 0xd7,
0x55, 0xa7, 0xab, 0x45, 0x02, 0x97, 0x60, 0x42,
};
// kSignature is an invalid signature of kHash with RSASSA-PKCS1-v1_5 with the
// NULL parameter omitted.
const uint8_t kSignatureInvalid[] = {
0x71, 0x6c, 0x24, 0x4e, 0xc9, 0x9b, 0x19, 0xc7, 0x49, 0x29, 0xb8, 0xd4,
0xfb, 0x26, 0x23, 0xc0, 0x96, 0x18, 0xcd, 0x1e, 0x60, 0xe8, 0x88, 0x94,
0x8c, 0x59, 0xfb, 0x58, 0x5c, 0x61, 0x58, 0x7a, 0xae, 0xcc, 0xeb, 0xee,
0x1e, 0x85, 0x7d, 0x83, 0xa9, 0xdc, 0x6f, 0x4c, 0x34, 0x5c, 0xcb, 0xd9,
0xde, 0x58, 0x76, 0xdf, 0x1f, 0x5e, 0xd4, 0x57, 0x5b, 0xeb, 0xaf, 0x4f,
0x7a, 0xa7, 0x6b, 0x21, 0xf1, 0x0a, 0x96, 0x78, 0xc7, 0xa8, 0x02, 0x7a,
0xc2, 0x06, 0xd3, 0x18, 0x79, 0x72, 0x6b, 0xfe, 0x2d, 0xec, 0xd8, 0x8e,
0x98, 0x86, 0x89, 0xf4, 0x67, 0x14, 0x2b, 0xac, 0x6d, 0xd7, 0x04, 0xd8,
0xab, 0x05, 0xe6, 0x51, 0xf6, 0xee, 0x58, 0x63, 0xef, 0x6a, 0x3e, 0x89,
0x99, 0x2a, 0x1c, 0x10, 0xc2, 0xd0, 0x41, 0x9e, 0x1e, 0x9a, 0x9a, 0x57,
0x32, 0x0f, 0x49, 0xb4, 0x57, 0x37, 0xa4, 0x26,
};
CK_MECHANISM_TYPE RsaHashToComboMech(SECOidTag hash) {
switch (hash) {
case SEC_OID_SHA1:
return CKM_SHA1_RSA_PKCS;
case SEC_OID_SHA224:
return CKM_SHA224_RSA_PKCS;
case SEC_OID_SHA256:
return CKM_SHA256_RSA_PKCS;
case SEC_OID_SHA384:
return CKM_SHA384_RSA_PKCS;
case SEC_OID_SHA512:
return CKM_SHA512_RSA_PKCS;
default:
break;
}
return CKM_INVALID_MECHANISM;
}
class Pkcs11RsaBaseTest : public Pk11SignatureTest {
protected:
Pkcs11RsaBaseTest(SECOidTag hashOid)
: Pk11SignatureTest(CKM_RSA_PKCS, hashOid, RsaHashToComboMech(hashOid)) {}
void Verify(const RsaSignatureTestVector& vec) {
Pkcs11SignatureTestParams params = {
DataBuffer(), DataBuffer(vec.public_key.data(), vec.public_key.size()),
DataBuffer(vec.msg.data(), vec.msg.size()),
DataBuffer(vec.sig.data(), vec.sig.size())};
Pk11SignatureTest::Verify(params, (bool)vec.valid);
}
};
class Pkcs11RsaPkcs1WycheproofTest : public ::testing::Test {
protected:
static void ReadTestAttr(RsaSignatureTestVector& t, const std::string& n,
JsonReader& r) {
if (n == "msg") {
t.msg = r.ReadHex();
} else if (n == "sig") {
t.sig = r.ReadHex();
} else {
FAIL() << "unknown test key: " << n;
}
}
void RunGroup(JsonReader& r) {
std::vector<RsaSignatureTestVector> tests;
std::vector<uint8_t> public_key;
SECOidTag hash_oid = SEC_OID_UNKNOWN;
uint64_t keysize = 0;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "e" || n == "keyAsn" || n == "keyJwk" || n == "keyPem" ||
n == "n") {
r.SkipValue();
} else if (n == "keyDer") {
public_key = r.ReadHex();
} else if (n == "keysize") {
keysize = r.ReadInt();
} else if (n == "type") {
ASSERT_EQ("RsassaPkcs1Verify", r.ReadString());
} else if (n == "sha") {
hash_oid = r.ReadHash();
} else if (n == "tests") {
WycheproofReadTests(
r, &tests, ReadTestAttr, false,
[keysize](RsaSignatureTestVector& t, const std::string& result,
const std::vector<std::string>& flags) {
if (result == "acceptable" && keysize >= 1024 &&
std::find_if(flags.begin(), flags.end(), [](std::string v) {
return v == "SmallModulus" || v == "SmallPublicKey";
}) != flags.end()) {
t.valid = true;
};
});
} else {
FAIL() << "unknown group label: " << n;
}
}
for (auto& t : tests) {
Pkcs11RsaBaseTestWrap test(hash_oid);
t.hash_oid = hash_oid;
t.public_key = public_key;
test.Run(t);
}
}
private:
class Pkcs11RsaBaseTestWrap : public Pkcs11RsaBaseTest {
public:
Pkcs11RsaBaseTestWrap(SECOidTag hash) : Pkcs11RsaBaseTest(hash) {}
void TestBody() {}
void Verify1(const RsaSignatureTestVector& vec) {
SECItem spki_item = {siBuffer, toUcharPtr(vec.public_key.data()),
static_cast<unsigned int>(vec.public_key.size())};
ScopedCERTSubjectPublicKeyInfo cert_spki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
ASSERT_TRUE(cert_spki);
ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get()));
ASSERT_TRUE(pub_key);
DataBuffer hash;
hash.Allocate(static_cast<size_t>(HASH_ResultLenByOidTag(vec.hash_oid)));
SECStatus rv = PK11_HashBuf(vec.hash_oid, toUcharPtr(hash.data()),
toUcharPtr(vec.msg.data()), vec.msg.size());
ASSERT_EQ(rv, SECSuccess);
// Verify.
SECItem hash_item = {siBuffer, toUcharPtr(hash.data()),
static_cast<unsigned int>(hash.len())};
SECItem sig_item = {siBuffer, toUcharPtr(vec.sig.data()),
static_cast<unsigned int>(vec.sig.size())};
rv = VFY_VerifyDigestDirect(&hash_item, pub_key.get(), &sig_item,
SEC_OID_PKCS1_RSA_ENCRYPTION, vec.hash_oid,
nullptr);
EXPECT_EQ(rv, vec.valid ? SECSuccess : SECFailure);
};
void Run(const RsaSignatureTestVector& vec) {
/* Using VFY_ interface */
Verify1(vec);
/* Using PKCS #11 interface */
setSkipRaw(true);
Verify(vec);
}
};
};
/* Test that PKCS #1 v1.5 verification requires a minimum of 8B
* of padding, per-RFC3447. The padding formula is
* `pad_len = em_len - t_len - 3`, where em_len is the octet length
* of the RSA modulus and t_len is the length of the `DigestInfo ||
* Hash(message)` sequence. For SHA512, t_len is 83. We'll tweak the
* modulus size to test with a pad_len of 8 (valid) and 6 (invalid):
* em_len = `8 + 83 + 3` = `94*8` = 752b
* em_len = `6 + 83 + 3` = `92*8` = 736b
* Use 6 as the invalid value since modLen % 16 must be zero.
*/
TEST(RsaPkcs1Test, Pkcs1MinimumPadding) {
#define RSA_SHORT_KEY_LENGTH 736
/* if our minimum supported key length is big enough to handle
* our largest Hash function, we can't test a short length */
#if RSA_MIN_MODULUS_BITS < RSA_SHORT_KEY_LENGTH
const size_t kRsaShortKeyBits = RSA_SHORT_KEY_LENGTH;
const size_t kRsaKeyBits = 752;
static const std::vector<uint8_t> kMsg{'T', 'E', 'S', 'T'};
static const std::vector<uint8_t> kSha512DigestInfo{
0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40};
static const std::vector<uint8_t> kMsgSha512{
0x7B, 0xFA, 0x95, 0xA6, 0x88, 0x92, 0x4C, 0x47, 0xC7, 0xD2, 0x23,
0x81, 0xF2, 0x0C, 0xC9, 0x26, 0xF5, 0x24, 0xBE, 0xAC, 0xB1, 0x3F,
0x84, 0xE2, 0x03, 0xD4, 0xBD, 0x8C, 0xB6, 0xBA, 0x2F, 0xCE, 0x81,
0xC5, 0x7A, 0x5F, 0x05, 0x9B, 0xF3, 0xD5, 0x09, 0x92, 0x64, 0x87,
0xBD, 0xE9, 0x25, 0xB3, 0xBC, 0xEE, 0x06, 0x35, 0xE4, 0xF7, 0xBA,
0xEB, 0xA0, 0x54, 0xE5, 0xDB, 0xA6, 0x96, 0xB2, 0xBF};
ScopedSECKEYPrivateKey short_priv, good_priv;
ScopedSECKEYPublicKey short_pub, good_pub;
PK11RSAGenParams rsa_params;
rsa_params.keySizeInBits = kRsaShortKeyBits;
rsa_params.pe = 65537;
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ASSERT_TRUE(slot);
SECKEYPublicKey* p_pub_tmp = nullptr;
short_priv.reset(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
&rsa_params, &p_pub_tmp, false, false,
nullptr));
short_pub.reset(p_pub_tmp);
rsa_params.keySizeInBits = kRsaKeyBits;
good_priv.reset(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
&rsa_params, &p_pub_tmp, false, false,
nullptr));
good_pub.reset(p_pub_tmp);
size_t em_len = kRsaShortKeyBits / 8;
size_t t_len = kSha512DigestInfo.size() + kMsgSha512.size();
size_t pad_len = em_len - t_len - 3;
ASSERT_EQ(6U, pad_len);
std::vector<uint8_t> invalid_pkcs;
invalid_pkcs.push_back(0x00);
invalid_pkcs.push_back(0x01);
invalid_pkcs.insert(invalid_pkcs.end(), pad_len, 0xff);
invalid_pkcs.insert(invalid_pkcs.end(), 1, 0x00);
invalid_pkcs.insert(invalid_pkcs.end(), kSha512DigestInfo.begin(),
kSha512DigestInfo.end());
invalid_pkcs.insert(invalid_pkcs.end(), kMsgSha512.begin(), kMsgSha512.end());
ASSERT_EQ(em_len, invalid_pkcs.size());
// Sign it indirectly. Signing functions check for a proper pad_len.
std::vector<uint8_t> sig(em_len);
uint32_t sig_len;
SECStatus rv =
PK11_PubDecryptRaw(short_priv.get(), sig.data(), &sig_len, sig.size(),
invalid_pkcs.data(), invalid_pkcs.size());
EXPECT_EQ(SECSuccess, rv);
// Verify it.
DataBuffer hash;
hash.Allocate(static_cast<size_t>(HASH_ResultLenByOidTag(SEC_OID_SHA512)));
rv = PK11_HashBuf(SEC_OID_SHA512, toUcharPtr(hash.data()),
toUcharPtr(kMsg.data()), kMsg.size());
ASSERT_EQ(rv, SECSuccess);
SECItem hash_item = {siBuffer, toUcharPtr(hash.data()),
static_cast<unsigned int>(hash.len())};
SECItem sig_item = {siBuffer, toUcharPtr(sig.data()), sig_len};
rv = VFY_VerifyDigestDirect(&hash_item, short_pub.get(), &sig_item,
SEC_OID_PKCS1_RSA_ENCRYPTION, SEC_OID_SHA512,
nullptr);
EXPECT_EQ(SECFailure, rv);
EXPECT_EQ(SEC_ERROR_BAD_SIGNATURE, PORT_GetError());
// Repeat the test with the sufficiently-long key.
em_len = kRsaKeyBits / 8;
t_len = kSha512DigestInfo.size() + kMsgSha512.size();
pad_len = em_len - t_len - 3;
ASSERT_EQ(8U, pad_len);
std::vector<uint8_t> valid_pkcs;
valid_pkcs.push_back(0x00);
valid_pkcs.push_back(0x01);
valid_pkcs.insert(valid_pkcs.end(), pad_len, 0xff);
valid_pkcs.insert(valid_pkcs.end(), 1, 0x00);
valid_pkcs.insert(valid_pkcs.end(), kSha512DigestInfo.begin(),
kSha512DigestInfo.end());
valid_pkcs.insert(valid_pkcs.end(), kMsgSha512.begin(), kMsgSha512.end());
ASSERT_EQ(em_len, valid_pkcs.size());
// Sign it the same way as above (even though we could use sign APIs now).
sig.resize(em_len);
rv = PK11_PubDecryptRaw(good_priv.get(), sig.data(), &sig_len, sig.size(),
valid_pkcs.data(), valid_pkcs.size());
EXPECT_EQ(SECSuccess, rv);
// Verify it.
sig_item = {siBuffer, toUcharPtr(sig.data()), sig_len};
rv = VFY_VerifyDigestDirect(&hash_item, good_pub.get(), &sig_item,
SEC_OID_PKCS1_RSA_ENCRYPTION, SEC_OID_SHA512,
nullptr);
EXPECT_EQ(SECSuccess, rv);
#else
GTEST_SKIP();
#endif
}
TEST(RsaPkcs1Test, RequireNullParameter) {
// The test vectors may be verified with:
//
// openssl rsautl -keyform der -pubin -inkey spki.bin -in sig.bin | der2ascii
// openssl rsautl -keyform der -pubin -inkey spki.bin -in sig2.bin | der2ascii
// Import public key.
SECItem spkiItem = {siBuffer, toUcharPtr(kSpki), sizeof(kSpki)};
ScopedCERTSubjectPublicKeyInfo certSpki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
ASSERT_TRUE(certSpki);
ScopedSECKEYPublicKey pubKey(SECKEY_ExtractPublicKey(certSpki.get()));
ASSERT_TRUE(pubKey);
SECItem spki_item = {siBuffer, toUcharPtr(kSpki), sizeof(kSpki)};
ScopedCERTSubjectPublicKeyInfo cert_spki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
ASSERT_TRUE(cert_spki);
ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get()));
ASSERT_TRUE(pub_key);
SECItem hash = {siBuffer, toUcharPtr(kHash), sizeof(kHash)};
// kSignature is a valid signature.
SECItem sigItem = {siBuffer, toUcharPtr(kSignature), sizeof(kSignature)};
SECStatus rv = VFY_VerifyDigestDirect(&hash, pubKey.get(), &sigItem,
SECItem sig_item = {siBuffer, toUcharPtr(kSignature), sizeof(kSignature)};
SECStatus rv = VFY_VerifyDigestDirect(&hash, pub_key.get(), &sig_item,
SEC_OID_PKCS1_RSA_ENCRYPTION,
SEC_OID_SHA256, nullptr);
EXPECT_EQ(SECSuccess, rv);
// kSignatureInvalid is not.
sigItem = {siBuffer, toUcharPtr(kSignatureInvalid),
sizeof(kSignatureInvalid)};
rv = VFY_VerifyDigestDirect(&hash, pubKey.get(), &sigItem,
sig_item = {siBuffer, toUcharPtr(kSignatureInvalid),
sizeof(kSignatureInvalid)};
rv = VFY_VerifyDigestDirect(&hash, pub_key.get(), &sig_item,
SEC_OID_PKCS1_RSA_ENCRYPTION, SEC_OID_SHA256,
nullptr);
#ifdef NSS_PKCS1_AllowMissingParameters
@ -105,4 +314,10 @@ TEST(RsaPkcs1Test, RequireNullParameter) {
#endif
}
TEST_F(Pkcs11RsaPkcs1WycheproofTest, Pkcs11RsaPkcs1WycheproofTest) {
WycheproofHeader("rsa_signature", "RSASSA-PKCS1-v1_5",
"rsassa_pkcs1_verify_schema.json",
[this](JsonReader& r) { RunGroup(r); });
}
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -7,50 +8,212 @@
#include "nss.h"
#include "pk11pub.h"
#include "sechash.h"
#include "json_reader.h"
#include "databuffer.h"
#include "gtest/gtest.h"
#include "nss_scoped_ptrs.h"
#include "pk11_signature_test.h"
#include "pk11_rsapss_vectors.h"
#include "testvectors_base/test-structs.h"
namespace nss_test {
class Pkcs11RsaPssTest : public Pk11SignatureTest {
CK_MECHANISM_TYPE RsaPssMapCombo(SECOidTag hashOid) {
switch (hashOid) {
case SEC_OID_SHA1:
return CKM_SHA1_RSA_PKCS_PSS;
case SEC_OID_SHA224:
return CKM_SHA224_RSA_PKCS_PSS;
case SEC_OID_SHA256:
return CKM_SHA256_RSA_PKCS_PSS;
case SEC_OID_SHA384:
return CKM_SHA384_RSA_PKCS_PSS;
case SEC_OID_SHA512:
return CKM_SHA512_RSA_PKCS_PSS;
default:
break;
}
return CKM_INVALID_MECHANISM;
}
class Pkcs11RsaPssTestBase : public Pk11SignatureTest {
public:
Pkcs11RsaPssTest() : Pk11SignatureTest(CKM_RSA_PKCS_PSS, SEC_OID_SHA1) {
rsaPssParams_.hashAlg = CKM_SHA_1;
rsaPssParams_.mgf = CKG_MGF1_SHA1;
rsaPssParams_.sLen = HASH_ResultLenByOidTag(SEC_OID_SHA1);
Pkcs11RsaPssTestBase(SECOidTag hashOid, CK_RSA_PKCS_MGF_TYPE mgf, int sLen)
: Pk11SignatureTest(CKM_RSA_PKCS_PSS, hashOid, RsaPssMapCombo(hashOid)) {
pss_params_.hashAlg = PK11_AlgtagToMechanism(hashOid);
pss_params_.mgf = mgf;
pss_params_.sLen = sLen;
params_.type = siBuffer;
params_.data = reinterpret_cast<unsigned char*>(&rsaPssParams_);
params_.len = sizeof(rsaPssParams_);
params_.data = reinterpret_cast<unsigned char*>(&pss_params_);
params_.len = sizeof(pss_params_);
}
protected:
const SECItem* parameters() const { return &params_; }
void Verify(const RsaPssTestVector& vec) {
Pkcs11SignatureTestParams params = {
DataBuffer(), DataBuffer(vec.public_key.data(), vec.public_key.size()),
DataBuffer(vec.msg.data(), vec.msg.size()),
DataBuffer(vec.sig.data(), vec.sig.size())};
Pk11SignatureTest::Verify(params, vec.valid);
}
private:
CK_RSA_PKCS_PSS_PARAMS rsaPssParams_;
CK_RSA_PKCS_PSS_PARAMS pss_params_;
SECItem params_;
};
class Pkcs11RsaPssTest : public Pkcs11RsaPssTestBase {
public:
Pkcs11RsaPssTest()
: Pkcs11RsaPssTestBase(SEC_OID_SHA1, CKG_MGF1_SHA1, SHA1_LENGTH) {}
};
class Pkcs11RsaPssTestWycheproof : public ::testing::Test {
public:
struct TestVector {
uint64_t id;
std::vector<uint8_t> msg;
std::vector<uint8_t> sig;
bool valid;
};
Pkcs11RsaPssTestWycheproof() {}
void Run(const std::string& file) {
WycheproofHeader("rsa_pss_" + file, "RSASSA-PSS",
"rsassa_pss_verify_schema.json",
[this](JsonReader& r) { RunGroup(r); });
}
static void ReadTestAttr(TestVector& t, const std::string& n, JsonReader& r) {
if (n == "msg") {
t.msg = r.ReadHex();
} else if (n == "sig") {
t.sig = r.ReadHex();
} else {
FAIL() << "unknown key in test: " << n;
}
}
private:
class Pkcs11RsaPssTestWrap : public Pkcs11RsaPssTestBase {
public:
Pkcs11RsaPssTestWrap(SECOidTag hash, CK_RSA_PKCS_MGF_TYPE mgf, int s_len)
: Pkcs11RsaPssTestBase(hash, mgf, s_len) {}
void TestBody() {}
void Verify(const Pkcs11SignatureTestParams& params, bool valid) {
Pk11SignatureTest::Verify(params, valid);
}
};
void RunTests(const std::vector<uint8_t>& public_key, SECOidTag hash,
CK_RSA_PKCS_MGF_TYPE mgf, int s_len,
const std::vector<TestVector>& tests) {
ASSERT_NE(0u, public_key.size());
ASSERT_NE(SEC_OID_UNKNOWN, hash);
ASSERT_NE(CKM_INVALID_MECHANISM, mgf);
ASSERT_NE(0u, tests.size());
for (auto& v : tests) {
std::cout << "Running tcid: " << v.id << std::endl;
Pkcs11RsaPssTestWrap test(hash, mgf, s_len);
Pkcs11SignatureTestParams params = {
DataBuffer(), DataBuffer(public_key.data(), public_key.size()),
DataBuffer(v.msg.data(), v.msg.size()),
DataBuffer(v.sig.data(), v.sig.size())};
test.Verify(params, v.valid);
}
}
void RunGroup(JsonReader& r) {
std::vector<uint8_t> public_key;
SECOidTag hash = SEC_OID_UNKNOWN;
CK_RSA_PKCS_MGF_TYPE mgf = CKM_INVALID_MECHANISM;
int s_len = 0;
std::vector<TestVector> tests;
while (r.NextItem()) {
std::string n = r.ReadLabel();
if (n == "") {
break;
}
if (n == "e" || n == "keyAsn" || n == "keyPem" || n == "n") {
(void)r.ReadString();
} else if (n == "keyDer") {
public_key = r.ReadHex();
} else if (n == "keysize") {
(void)r.ReadInt();
} else if (n == "mgf") {
std::string s = r.ReadString();
ASSERT_EQ(s, "MGF1");
} else if (n == "mgfSha") {
std::string s = r.ReadString();
if (s == "SHA-1") {
mgf = CKG_MGF1_SHA1;
} else if (s == "SHA-224") {
mgf = CKG_MGF1_SHA224;
} else if (s == "SHA-256") {
mgf = CKG_MGF1_SHA256;
} else if (s == "SHA-384") {
mgf = CKG_MGF1_SHA384;
} else if (s == "SHA-512") {
mgf = CKG_MGF1_SHA512;
} else {
FAIL() << "unsupported MGF hash";
}
} else if (n == "sLen") {
s_len = static_cast<unsigned int>(r.ReadInt());
} else if (n == "sha") {
std::string s = r.ReadString();
if (s == "SHA-1") {
hash = SEC_OID_SHA1;
} else if (s == "SHA-224") {
hash = SEC_OID_SHA224;
} else if (s == "SHA-256") {
hash = SEC_OID_SHA256;
} else if (s == "SHA-384") {
hash = SEC_OID_SHA384;
} else if (s == "SHA-512") {
hash = SEC_OID_SHA512;
} else {
FAIL() << "unsupported hash";
}
} else if (n == "type") {
ASSERT_EQ("RsassaPssVerify", r.ReadString());
} else if (n == "tests") {
WycheproofReadTests(r, &tests, ReadTestAttr);
} else {
FAIL() << "unknown test group attribute: " << n;
}
}
RunTests(public_key, hash, mgf, s_len, tests);
}
};
TEST_F(Pkcs11RsaPssTest, GenerateAndSignAndVerify) {
// Sign data with a 1024-bit RSA key, using PSS/SHA-256.
SECOidTag hashOid = SEC_OID_SHA256;
CK_MECHANISM_TYPE hashMech = CKM_SHA256;
CK_MECHANISM_TYPE hash_mech = CKM_SHA256;
CK_RSA_PKCS_MGF_TYPE mgf = CKG_MGF1_SHA256;
PK11RSAGenParams rsaGenParams = {1024, 0x10001};
// Generate RSA key pair.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
SECKEYPublicKey* pubKeyRaw = nullptr;
SECKEYPublicKey* pub_keyRaw = nullptr;
ScopedSECKEYPrivateKey privKey(
PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaGenParams,
&pubKeyRaw, false, false, nullptr));
ASSERT_TRUE(!!privKey && pubKeyRaw);
ScopedSECKEYPublicKey pubKey(pubKeyRaw);
&pub_keyRaw, false, false, nullptr));
ASSERT_TRUE(!!privKey && pub_keyRaw);
ScopedSECKEYPublicKey pub_key(pub_keyRaw);
// Generate random data to sign.
uint8_t dataBuf[50];
@ -65,30 +228,30 @@ TEST_F(Pkcs11RsaPssTest, GenerateAndSignAndVerify) {
static_cast<unsigned int>(sigBuf.size())};
// Set up PSS parameters.
CK_RSA_PKCS_PSS_PARAMS rsaPssParams = {hashMech, mgf, hLen};
SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&rsaPssParams),
sizeof(rsaPssParams)};
CK_RSA_PKCS_PSS_PARAMS pss_params = {hash_mech, mgf, hLen};
SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&pss_params),
sizeof(pss_params)};
// Sign.
rv = PK11_SignWithMechanism(privKey.get(), mechanism(), &params, &sig, &data);
EXPECT_EQ(rv, SECSuccess);
// Verify.
rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), &params, &sig, &data,
nullptr);
rv = PK11_VerifyWithMechanism(pub_key.get(), mechanism(), &params, &sig,
&data, nullptr);
EXPECT_EQ(rv, SECSuccess);
// Verification with modified data must fail.
data.data[0] ^= 0xff;
rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), &params, &sig, &data,
nullptr);
rv = PK11_VerifyWithMechanism(pub_key.get(), mechanism(), &params, &sig,
&data, nullptr);
EXPECT_EQ(rv, SECFailure);
// Verification with original data but the wrong signature must fail.
data.data[0] ^= 0xff; // Revert previous changes.
sig.data[0] ^= 0xff;
rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), &params, &sig, &data,
nullptr);
rv = PK11_VerifyWithMechanism(pub_key.get(), mechanism(), &params, &sig,
&data, nullptr);
EXPECT_EQ(rv, SECFailure);
}
@ -99,18 +262,20 @@ TEST_F(Pkcs11RsaPssTest, NoLeakWithInvalidExponent) {
// Generate RSA key pair.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
SECKEYPublicKey* pubKey = nullptr;
SECKEYPublicKey* pub_key = nullptr;
SECKEYPrivateKey* privKey =
PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaGenParams,
&pubKey, false, false, nullptr);
&pub_key, false, false, nullptr);
EXPECT_FALSE(privKey);
EXPECT_FALSE(pubKey);
EXPECT_FALSE(pub_key);
}
class Pkcs11RsaPssVectorTest
: public Pkcs11RsaPssTest,
public ::testing::WithParamInterface<Pkcs11SignatureTestParams> {};
TEST_P(Pkcs11RsaPssVectorTest, Verify) { Verify(GetParam()); }
TEST_P(Pkcs11RsaPssVectorTest, Verify) {
Pk11SignatureTest::Verify(GetParam());
}
TEST_P(Pkcs11RsaPssVectorTest, SignAndVerify) { SignAndVerify(GetParam()); }
@ -155,7 +320,25 @@ static const Pkcs11SignatureTestParams kRsaPssVectors[] = {
// <ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip>
VECTOR_N(10)};
INSTANTIATE_TEST_CASE_P(RsaPssSignVerify, Pkcs11RsaPssVectorTest,
::testing::ValuesIn(kRsaPssVectors));
INSTANTIATE_TEST_SUITE_P(RsaPssSignVerify, Pkcs11RsaPssVectorTest,
::testing::ValuesIn(kRsaPssVectors));
TEST_F(Pkcs11RsaPssTestWycheproof, RsaPss2048Sha1) { Run("2048_sha1_mgf1_20"); }
TEST_F(Pkcs11RsaPssTestWycheproof, RsaPss2048Sha256_0) {
Run("2048_sha256_mgf1_0");
}
TEST_F(Pkcs11RsaPssTestWycheproof, RsaPss2048Sha256_32) {
Run("2048_sha256_mgf1_32");
}
TEST_F(Pkcs11RsaPssTestWycheproof, RsaPss3072Sha256) {
Run("3072_sha256_mgf1_32");
}
TEST_F(Pkcs11RsaPssTestWycheproof, RsaPss4096Sha256) {
Run("4096_sha256_mgf1_32");
}
TEST_F(Pkcs11RsaPssTestWycheproof, RsaPss4096Sha512) {
Run("4096_sha512_mgf1_32");
}
TEST_F(Pkcs11RsaPssTestWycheproof, RsaPssMisc) { Run("misc"); }
} // namespace nss_test

View file

@ -1,4 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
@ -13,58 +14,69 @@
#include "util.h"
namespace nss_test {
class Pkcs11SeedCbcTest : public ::testing::Test {
class Pkcs11SeedTest : public ::testing::Test {
protected:
enum class Action { Encrypt, Decrypt };
SECStatus EncryptDecryptSeed(Action action, unsigned int input_size,
unsigned int output_size) {
void EncryptDecryptSeed(SECStatus expected, unsigned int input_size,
unsigned int output_size,
CK_MECHANISM_TYPE mech = CKM_SEED_CBC) {
// Generate a random key.
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
ScopedPK11SymKey sym_key(
PK11_KeyGen(slot.get(), kMech, nullptr, 16, nullptr));
PK11_KeyGen(slot.get(), mech, nullptr, 16, nullptr));
EXPECT_TRUE(!!sym_key);
std::vector<uint8_t> data(input_size);
std::vector<uint8_t> plaintext(input_size, 0xFF);
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()};
std::vector<uint8_t> ciphertext(output_size, 0);
SECItem iv_param = {siBuffer, init_vector.data(),
(unsigned int)init_vector.size()};
std::vector<uint8_t> decrypted(output_size, 0);
// Try to encrypt/decrypt.
// Try to encrypt, decrypt if positive test.
unsigned int output_len = 0;
if (action == Action::Encrypt) {
return PK11_Encrypt(sym_key.get(), kMech, &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());
EXPECT_EQ(expected,
PK11_Encrypt(sym_key.get(), mech, &iv_param, ciphertext.data(),
&output_len, output_size, plaintext.data(),
plaintext.size()));
if (expected == SECSuccess) {
EXPECT_EQ(expected,
PK11_Decrypt(sym_key.get(), mech, &iv_param, decrypted.data(),
&output_len, output_size, ciphertext.data(),
output_len));
decrypted.resize(output_len);
EXPECT_EQ(plaintext, decrypted);
}
}
const CK_MECHANISM_TYPE kMech = CKM_SEED_CBC;
};
#ifndef NSS_DISABLE_DEPRECATED_SEED
// The intention here is to test the arguments of these functions
// The resulted content is already tested in EncryptDeriveTests.
// SEED_CBC needs an IV of 16 bytes.
// The input data size must be multiple of 16.
// If not, some padding should be added.
// The output size must be at least the size of input data.
TEST_F(Pkcs11SeedCbcTest, SeedCBC_ValidArgs) {
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Encrypt, 16, 16));
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Decrypt, 16, 16));
TEST_F(Pkcs11SeedTest, CBC_ValidArgs) {
EncryptDecryptSeed(SECSuccess, 16, 16);
// No problem if maxLen is bigger than input data.
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Encrypt, 16, 32));
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Decrypt, 16, 32));
EncryptDecryptSeed(SECSuccess, 16, 32);
}
TEST_F(Pkcs11SeedCbcTest, SeedCBC_InvalidArgs) {
TEST_F(Pkcs11SeedTest, CBC_InvalidArgs) {
// maxLen lower than input data.
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Encrypt, 16, 10));
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Decrypt, 16, 10));
EncryptDecryptSeed(SECFailure, 16, 10);
// input data not multiple of SEED_BLOCK_SIZE (16)
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Encrypt, 17, 32));
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Decrypt, 17, 32));
EncryptDecryptSeed(SECFailure, 17, 32);
}
} // namespace nss_test
TEST_F(Pkcs11SeedTest, ECB_Singleblock) {
EncryptDecryptSeed(SECSuccess, 16, 16, CKM_SEED_ECB);
}
TEST_F(Pkcs11SeedTest, ECB_Multiblock) {
EncryptDecryptSeed(SECSuccess, 64, 64, CKM_SEED_ECB);
}
#endif
} // namespace nss_test

View file

@ -0,0 +1,179 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <memory>
#include "nss.h"
#include "pk11pub.h"
#include "sechash.h"
#include "prerror.h"
#include "cpputil.h"
#include "nss_scoped_ptrs.h"
#include "databuffer.h"
#include "gtest/gtest.h"
#include "pk11_signature_test.h"
namespace nss_test {
ScopedSECKEYPrivateKey Pk11SignatureTest::ImportPrivateKey(
const DataBuffer& pkcs8) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
ADD_FAILURE() << "No slot";
return nullptr;
}
SECItem pkcs8Item = {siBuffer, toUcharPtr(pkcs8.data()),
static_cast<unsigned int>(pkcs8.len())};
SECKEYPrivateKey* key = nullptr;
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot.get(), &pkcs8Item, nullptr, nullptr, false, false, KU_ALL, &key,
nullptr);
if (rv != SECSuccess) {
return nullptr;
}
return ScopedSECKEYPrivateKey(key);
}
ScopedSECKEYPublicKey Pk11SignatureTest::ImportPublicKey(
const DataBuffer& spki) {
SECItem spkiItem = {siBuffer, toUcharPtr(spki.data()),
static_cast<unsigned int>(spki.len())};
ScopedCERTSubjectPublicKeyInfo certSpki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
if (!certSpki) {
return nullptr;
}
return ScopedSECKEYPublicKey(SECKEY_ExtractPublicKey(certSpki.get()));
}
bool Pk11SignatureTest::SignHashedData(ScopedSECKEYPrivateKey& privKey,
const DataBuffer& hash,
DataBuffer* sig) {
SECItem hashItem = {siBuffer, toUcharPtr(hash.data()),
static_cast<unsigned int>(hash.len())};
unsigned int sigLen = PK11_SignatureLen(privKey.get());
EXPECT_LT(0, (int)sigLen);
sig->Allocate(static_cast<size_t>(sigLen));
SECItem sigItem = {siBuffer, toUcharPtr(sig->data()),
static_cast<unsigned int>(sig->len())};
SECStatus rv = PK11_SignWithMechanism(privKey.get(), mechanism_, parameters(),
&sigItem, &hashItem);
EXPECT_EQ(sigLen, sigItem.len);
return rv == SECSuccess;
}
bool Pk11SignatureTest::SignData(ScopedSECKEYPrivateKey& privKey,
const DataBuffer& data, DataBuffer* sig) {
unsigned int sigLen = PK11_SignatureLen(privKey.get());
bool result = true;
EXPECT_LT(0, (int)sigLen);
sig->Allocate(static_cast<size_t>(sigLen));
// test the hash and verify interface */
PK11Context* context = PK11_CreateContextByPrivKey(
combo_, CKA_SIGN, privKey.get(), parameters());
if (context == NULL) {
ADD_FAILURE() << "Failed to sign data: couldn't create context"
<< "\n"
<< "mech=0x" << std::hex << combo_ << "\n"
<< "Error: " << PORT_ErrorToString(PORT_GetError());
return false;
}
SECStatus rv = PK11_DigestOp(context, data.data(), data.len());
if (rv != SECSuccess) {
ADD_FAILURE() << "Failed to sign data: Update failed\n"
<< "Error: " << PORT_ErrorToString(PORT_GetError());
PK11_DestroyContext(context, PR_TRUE);
return false;
}
unsigned int len = sigLen;
rv = PK11_DigestFinal(context, sig->data(), &len, sigLen);
if (rv != SECSuccess) {
ADD_FAILURE() << "Failed to sign data: final failed\n"
<< "Error: " << PORT_ErrorToString(PORT_GetError());
result = false;
}
if (len != sigLen) {
ADD_FAILURE() << "sign data: unexpected len " << len << "expected"
<< sigLen;
result = false;
}
PK11_DestroyContext(context, PR_TRUE);
return result;
}
bool Pk11SignatureTest::ImportPrivateKeyAndSignHashedData(
const DataBuffer& pkcs8, const DataBuffer& data, DataBuffer* sig,
DataBuffer* sig2) {
ScopedSECKEYPrivateKey privKey(ImportPrivateKey(pkcs8));
if (!privKey) {
return false;
}
DataBuffer hash;
if (!ComputeHash(data, &hash)) {
ADD_FAILURE() << "Failed to compute hash";
return false;
}
if (!SignHashedData(privKey, hash, sig)) {
ADD_FAILURE() << "Failed to sign hashed data";
return false;
}
if (!SignData(privKey, data, sig2)) {
/* failure was already added by SignData, with an error message */
return false;
}
return true;
}
void Pk11SignatureTest::Verify(ScopedSECKEYPublicKey& pubKey,
const DataBuffer& data, const DataBuffer& sig,
bool valid) {
SECStatus rv;
DataBuffer hash;
SECItem sigItem = {siBuffer, toUcharPtr(sig.data()),
static_cast<unsigned int>(sig.len())};
/* RSA single shot requires encoding the hash before calling
* VerifyWithMechanism. We already check that mechanism
* with the VFY_ interface, so just do the combined hash/Verify
* in that case */
if (!skip_raw_) {
ASSERT_TRUE(ComputeHash(data, &hash));
// Verify.
SECItem hashItem = {siBuffer, toUcharPtr(hash.data()),
static_cast<unsigned int>(hash.len())};
rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism_, parameters(),
&sigItem, &hashItem, nullptr);
EXPECT_EQ(rv, valid ? SECSuccess : SECFailure);
}
// test the hash and verify interface */
PK11Context* context = PK11_CreateContextByPubKey(
combo_, CKA_VERIFY, pubKey.get(), parameters(), NULL);
/* we assert here because we'll crash if we try to continue
* without a context. */
ASSERT_NE((void*)context, (void*)NULL)
<< "CreateContext failed Error:" << PORT_ErrorToString(PORT_GetError())
<< "\n";
rv = PK11_DigestOp(context, data.data(), data.len());
/* expect success unconditionally here */
EXPECT_EQ(rv, SECSuccess);
unsigned int len;
rv = PK11_DigestFinal(context, sigItem.data, &len, sigItem.len);
EXPECT_EQ(rv, valid ? SECSuccess : SECFailure)
<< "verify failed Error:" << PORT_ErrorToString(PORT_GetError()) << "\n";
PK11_DestroyContext(context, PR_TRUE);
}
} // namespace nss_test

View file

@ -7,7 +7,6 @@
#include "pk11pub.h"
#include "sechash.h"
#include "cpputil.h"
#include "nss_scoped_ptrs.h"
#include "databuffer.h"
@ -25,46 +24,28 @@ struct Pkcs11SignatureTestParams {
class Pk11SignatureTest : public ::testing::Test {
protected:
Pk11SignatureTest(CK_MECHANISM_TYPE mech, SECOidTag hash_oid)
: mechanism_(mech), hash_oid_(hash_oid) {}
Pk11SignatureTest(CK_MECHANISM_TYPE mech, SECOidTag hash_oid,
CK_MECHANISM_TYPE combo)
: mechanism_(mech), hash_oid_(hash_oid), combo_(combo) {
skip_raw_ = false;
}
virtual const SECItem* parameters() const { return nullptr; }
CK_MECHANISM_TYPE mechanism() const { return mechanism_; }
void setSkipRaw(bool skip_raw) { skip_raw_ = true; }
ScopedSECKEYPrivateKey ImportPrivateKey(const DataBuffer& pkcs8) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
if (!slot) {
ADD_FAILURE() << "No slot";
return nullptr;
bool ExportPrivateKey(ScopedSECKEYPrivateKey* key, DataBuffer& pkcs8) {
SECItem* pkcs8Item = PK11_ExportDERPrivateKeyInfo(key->get(), nullptr);
if (!pkcs8Item) {
return false;
}
SECItem pkcs8Item = {siBuffer, toUcharPtr(pkcs8.data()),
static_cast<unsigned int>(pkcs8.len())};
SECKEYPrivateKey* key = nullptr;
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
slot.get(), &pkcs8Item, nullptr, nullptr, false, false, KU_ALL, &key,
nullptr);
if (rv != SECSuccess) {
return nullptr;
}
return ScopedSECKEYPrivateKey(key);
pkcs8.Assign(pkcs8Item->data, pkcs8Item->len);
SECITEM_ZfreeItem(pkcs8Item, PR_TRUE);
return true;
}
ScopedSECKEYPublicKey ImportPublicKey(const DataBuffer& spki) {
SECItem spkiItem = {siBuffer, toUcharPtr(spki.data()),
static_cast<unsigned int>(spki.len())};
ScopedCERTSubjectPublicKeyInfo certSpki(
SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
if (!certSpki) {
return nullptr;
}
return ScopedSECKEYPublicKey(SECKEY_ExtractPublicKey(certSpki.get()));
}
ScopedSECKEYPrivateKey ImportPrivateKey(const DataBuffer& pkcs8);
ScopedSECKEYPublicKey ImportPublicKey(const DataBuffer& spki);
bool ComputeHash(const DataBuffer& data, DataBuffer* hash) {
hash->Allocate(static_cast<size_t>(HASH_ResultLenByOidTag(hash_oid_)));
@ -74,66 +55,61 @@ class Pk11SignatureTest : public ::testing::Test {
}
bool SignHashedData(ScopedSECKEYPrivateKey& privKey, const DataBuffer& hash,
DataBuffer* sig) {
SECItem hashItem = {siBuffer, toUcharPtr(hash.data()),
static_cast<unsigned int>(hash.len())};
int sigLen = PK11_SignatureLen(privKey.get());
EXPECT_LT(0, sigLen);
sig->Allocate(static_cast<size_t>(sigLen));
SECItem sigItem = {siBuffer, toUcharPtr(sig->data()),
static_cast<unsigned int>(sig->len())};
SECStatus rv = PK11_SignWithMechanism(privKey.get(), mechanism_,
parameters(), &sigItem, &hashItem);
return rv == SECSuccess;
}
DataBuffer* sig);
bool SignData(ScopedSECKEYPrivateKey& privKey, const DataBuffer& data,
DataBuffer* sig);
bool ImportPrivateKeyAndSignHashedData(const DataBuffer& pkcs8,
const DataBuffer& data,
DataBuffer* sig) {
ScopedSECKEYPrivateKey privKey(ImportPrivateKey(pkcs8));
if (!privKey) {
return false;
}
DataBuffer* sig, DataBuffer* sig2);
DataBuffer hash;
if (!ComputeHash(data, &hash)) {
ADD_FAILURE() << "Failed to compute hash";
return false;
}
return SignHashedData(privKey, hash, sig);
/* most primitive verify implemented in pk11_signature_test.cpp */
void Verify(ScopedSECKEYPublicKey& pubKey, const DataBuffer& data,
const DataBuffer& sig, bool valid);
/* quick helper functions that use the primitive verify */
void Verify(ScopedSECKEYPublicKey& pubKey, const DataBuffer& data,
const DataBuffer& sig) {
Verify(pubKey, data, sig, true);
}
void Verify(const Pkcs11SignatureTestParams& params, const DataBuffer& sig) {
void Verify(const Pkcs11SignatureTestParams& params, const DataBuffer& sig,
bool valid) {
ScopedSECKEYPublicKey pubKey(ImportPublicKey(params.spki_));
ASSERT_TRUE(pubKey);
Verify(pubKey, params.data_, sig, valid);
}
DataBuffer hash;
ASSERT_TRUE(ComputeHash(params.data_, &hash));
// Verify.
SECItem hashItem = {siBuffer, toUcharPtr(hash.data()),
static_cast<unsigned int>(hash.len())};
SECItem sigItem = {siBuffer, toUcharPtr(sig.data()),
static_cast<unsigned int>(sig.len())};
SECStatus rv = PK11_VerifyWithMechanism(
pubKey.get(), mechanism_, parameters(), &sigItem, &hashItem, nullptr);
EXPECT_EQ(rv, SECSuccess);
void Verify(const Pkcs11SignatureTestParams& params, bool valid) {
Verify(params, params.signature_, valid);
}
void Verify(const Pkcs11SignatureTestParams& params) {
Verify(params, params.signature_);
Verify(params, params.signature_, true);
}
void SignAndVerify(const Pkcs11SignatureTestParams& params) {
DataBuffer sig;
ASSERT_TRUE(
ImportPrivateKeyAndSignHashedData(params.pkcs8_, params.data_, &sig));
Verify(params, sig);
DataBuffer sig2;
ASSERT_TRUE(ImportPrivateKeyAndSignHashedData(params.pkcs8_, params.data_,
&sig, &sig2));
Verify(params, sig, true);
Verify(params, sig2, true);
}
// Importing a private key in PKCS#8 format and reexporting it should
// result in the same binary representation.
void ImportExport(const DataBuffer& k) {
DataBuffer exported;
ScopedSECKEYPrivateKey key = ImportPrivateKey(k);
ExportPrivateKey(&key, exported);
EXPECT_EQ(k, exported);
}
private:
CK_MECHANISM_TYPE mechanism_;
SECOidTag hash_oid_;
CK_MECHANISM_TYPE combo_;
bool skip_raw_;
};
} // namespace nss_test