mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-27 10:57:34 +09:00
Replace NSS with Pale Moon's
This commit is contained in:
parent
ff1e5e48bf
commit
8c2e376f94
2870 changed files with 1762232 additions and 1374220 deletions
1
security/nss/gtests/pk11_gtest/hpke-vectors.json
Normal file
1
security/nss/gtests/pk11_gtest/hpke-vectors.json
Normal file
File diff suppressed because one or more lines are too long
230
security/nss/gtests/pk11_gtest/json_reader.cc
Normal file
230
security/nss/gtests/pk11_gtest/json_reader.cc
Normal 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
138
security/nss/gtests/pk11_gtest/json_reader.h
Normal file
138
security/nss/gtests/pk11_gtest/json_reader.h
Normal 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_
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, ¶ms, cipher_v24.data(),
|
||||
&output_len_v24, cipher_v24.size(), plainIn.data(),
|
||||
plainIn.size()));
|
||||
EXPECT_EQ(output_len_message, (int)output_len_v24 - kTagSize);
|
||||
EXPECT_EQ(0, memcmp(cipher_message.data(), cipher_v24.data(),
|
||||
output_len_message));
|
||||
EXPECT_EQ(0, memcmp(gcm_message_params.pTag,
|
||||
cipher_v24.data() + output_len_message, kTagSize));
|
||||
// now make sure we can decrypt
|
||||
EXPECT_EQ(SECSuccess,
|
||||
PK11_AEADRawOp(decrypt_message_context.get(),
|
||||
&gcm_message_params, sizeof(gcm_message_params),
|
||||
aad.data(), aad.size(), plainOut_message.data(),
|
||||
&output_len_message, plainOut_message.size(),
|
||||
cipher_message.data(), output_len_message));
|
||||
EXPECT_EQ(output_len_message, (int)plainIn.size());
|
||||
EXPECT_EQ(
|
||||
0, memcmp(plainOut_message.data(), plainIn.data(), plainIn.size()));
|
||||
EXPECT_EQ(
|
||||
SECSuccess,
|
||||
PK11_AEADRawOp(decrypt_simulated_context.get(), &gcm_simulated_params,
|
||||
sizeof(gcm_simulated_params), aad.data(), aad.size(),
|
||||
plainOut_simulated.data(), &output_len_simulated,
|
||||
plainOut_simulated.size(), cipher_message.data(),
|
||||
output_len_simulated));
|
||||
EXPECT_EQ(output_len_simulated, (int)plainIn.size());
|
||||
EXPECT_EQ(
|
||||
0, memcmp(plainOut_simulated.data(), plainIn.data(), plainIn.size()));
|
||||
if (separateTag) {
|
||||
// in the separateTag case, we need to copy the tag back to the
|
||||
// end of the cipher_message.data() before using the v2.4 interface
|
||||
memcpy(cipher_message.data() + output_len_message,
|
||||
gcm_message_params.pTag, kTagSize);
|
||||
}
|
||||
EXPECT_EQ(SECSuccess,
|
||||
PK11_Decrypt(sym_key.get(), mech, ¶ms, plainOut_v24.data(),
|
||||
&output_len_v24, plainOut_v24.size(),
|
||||
cipher_message.data(), output_len_v24));
|
||||
EXPECT_EQ(output_len_v24, plainIn.size());
|
||||
EXPECT_EQ(0, memcmp(plainOut_v24.data(), plainIn.data(), plainIn.size()));
|
||||
}
|
||||
return SECSuccess;
|
||||
}
|
||||
|
||||
const CK_MECHANISM_TYPE mech = CKM_AES_GCM;
|
||||
};
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
123
security/nss/gtests/pk11_gtest/pk11_aeskeywrapkwp_unittest.cc
Normal file
123
security/nss/gtests/pk11_gtest/pk11_aeskeywrapkwp_unittest.cc
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, ¶m, encrypted, &encrypted_len,
|
||||
sizeof(encrypted), kInput, input_len);
|
||||
EXPECT_EQ(SECFailure, rv);
|
||||
|
||||
param.len++;
|
||||
reinterpret_cast<CK_GCM_PARAMS*>(param.data)->pIv = param_buf;
|
||||
reinterpret_cast<CK_GCM_PARAMS*>(param.data)->ulIvLen = 12;
|
||||
reinterpret_cast<CK_GCM_PARAMS*>(param.data)->pAAD = nullptr;
|
||||
reinterpret_cast<CK_GCM_PARAMS*>(param.data)->ulAADLen = 0;
|
||||
reinterpret_cast<CK_GCM_PARAMS*>(param.data)->ulTagBits = 128;
|
||||
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, ¶m, 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
|
||||
|
|
|
|||
|
|
@ -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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms,
|
||||
&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, ¶ms, 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, ¶ms, 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,
|
||||
¶ms, &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, ¶ms, cipher_v24.data(),
|
||||
&output_len_v24, cipher_v24.size(), plainIn.data(),
|
||||
plainIn.size()));
|
||||
EXPECT_EQ(output_len_message, (int)output_len_v24 - tagSize);
|
||||
EXPECT_EQ(0, memcmp(cipher_message.data(), cipher_v24.data(),
|
||||
output_len_message));
|
||||
EXPECT_EQ(0, memcmp(chacha_message_params.pTag,
|
||||
cipher_v24.data() + output_len_message, tagSize));
|
||||
// now make sure we can decrypt
|
||||
EXPECT_EQ(
|
||||
SECSuccess,
|
||||
PK11_AEADRawOp(decrypt_message_context.get(), &chacha_message_params,
|
||||
sizeof(chacha_message_params), aad.data(), aad.size(),
|
||||
plainOut_message.data(), &output_len_message,
|
||||
plainOut_message.size(), cipher_message.data(),
|
||||
output_len_message));
|
||||
EXPECT_EQ(output_len_message, (int)plainIn.size());
|
||||
EXPECT_EQ(
|
||||
0, memcmp(plainOut_message.data(), plainIn.data(), plainIn.size()));
|
||||
EXPECT_EQ(SECSuccess,
|
||||
PK11_AEADRawOp(decrypt_simulated_context.get(),
|
||||
&chacha_simulated_params,
|
||||
sizeof(chacha_simulated_params), aad.data(),
|
||||
aad.size(), plainOut_simulated.data(),
|
||||
&output_len_simulated, plainOut_simulated.size(),
|
||||
cipher_message.data(), output_len_simulated));
|
||||
EXPECT_EQ(output_len_simulated, (int)plainIn.size());
|
||||
EXPECT_EQ(
|
||||
0, memcmp(plainOut_simulated.data(), plainIn.data(), plainIn.size()));
|
||||
if (separateTag) {
|
||||
// in the separateTag case, we need to copy the tag back to the
|
||||
// end of the cipher_message.data() before using the v2.4 interface
|
||||
memcpy(cipher_message.data() + output_len_message,
|
||||
chacha_message_params.pTag, tagSize);
|
||||
}
|
||||
EXPECT_EQ(SECSuccess,
|
||||
PK11_Decrypt(sym_key.get(), mech, ¶ms, plainOut_v24.data(),
|
||||
&output_len_v24, plainOut_v24.size(),
|
||||
cipher_message.data(), output_len_v24));
|
||||
EXPECT_EQ(output_len_v24, plainIn.size());
|
||||
EXPECT_EQ(0, memcmp(plainOut_v24.data(), plainIn.data(), plainIn.size()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
@ -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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, 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, ¶ms, encrypted, &encrypted_len,
|
||||
sizeof(encrypted), kData, sizeof(kData));
|
||||
EXPECT_EQ(SECFailure, rv);
|
||||
|
||||
params.data = nullptr;
|
||||
rv = PK11_Encrypt(key.get(), kMechXor, ¶ms, 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
|
||||
|
|
|
|||
|
|
@ -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*>(¶m),
|
||||
sizeof(CK_AES_CTR_PARAMS)};
|
||||
|
||||
key = PK11_ImportSymKey(slot, cipher, PK11_OriginUnwrap, CKA_ENCRYPT,
|
||||
&keyItem, NULL);
|
||||
ctx = PK11_CreateContextBySymKey(cipher, CKA_ENCRYPT, key, ¶mItem);
|
||||
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(), ¶mItem));
|
||||
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, ¶m_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, ¶m_item);
|
||||
}
|
||||
|
||||
} // namespace nss_test
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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/. */
|
||||
|
|
|
|||
81
security/nss/gtests/pk11_gtest/pk11_dsa_unittest.cc
Normal file
81
security/nss/gtests/pk11_gtest/pk11_dsa_unittest.cc
Normal 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
|
||||
237
security/nss/gtests/pk11_gtest/pk11_ecdh_unittest.cc
Normal file
237
security/nss/gtests/pk11_gtest/pk11_ecdh_unittest.cc
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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': [
|
||||
|
|
|
|||
199
security/nss/gtests/pk11_gtest/pk11_hkdf_unittest.cc
Normal file
199
security/nss/gtests/pk11_gtest/pk11_hkdf_unittest.cc
Normal 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, ¶ms_item,
|
||||
CKM_GENERIC_SECRET_KEY_GEN, CKA_DERIVE, vec.size));
|
||||
if (vec.valid) {
|
||||
ASSERT_NE(nullptr, okm.get()) << msg;
|
||||
ASSERT_EQ(SECSuccess, PK11_ExtractKeyValue(okm.get())) << msg;
|
||||
ASSERT_EQ(0, SECITEM_CompareItem(&okm_item, PK11_GetKeyData(okm.get())))
|
||||
<< msg;
|
||||
} else {
|
||||
ASSERT_EQ(nullptr, okm.get()) << msg;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(Pkcs11HkdfTest, WycheproofVectors) {
|
||||
RunWycheproofTest(std::get<0>(GetParam()), std::get<1>(GetParam()),
|
||||
std::get<2>(GetParam()));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_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
|
||||
74
security/nss/gtests/pk11_gtest/pk11_hmac_unittest.cc
Normal file
74
security/nss/gtests/pk11_gtest/pk11_hmac_unittest.cc
Normal 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
|
||||
891
security/nss/gtests/pk11_gtest/pk11_hpke_unittest.cc
Normal file
891
security/nss/gtests/pk11_gtest/pk11_hpke_unittest.cc
Normal 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
|
||||
197
security/nss/gtests/pk11_gtest/pk11_ike_unittest.cc
Normal file
197
security/nss/gtests/pk11_gtest/pk11_ike_unittest.cc
Normal 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, ¶ms_item,
|
||||
CKM_GENERIC_SECRET_KEY_GEN, CKA_DERIVE, vec.size));
|
||||
if (vec.valid) {
|
||||
ASSERT_NE(nullptr, okm.get()) << msg;
|
||||
ASSERT_EQ(SECSuccess, PK11_ExtractKeyValue(okm.get())) << msg;
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
136
security/nss/gtests/pk11_gtest/pk11_kbkdf.cc
Normal file
136
security/nss/gtests/pk11_gtest/pk11_kbkdf.cc
Normal 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, ¶msItem,
|
||||
CKM_SHA512_HMAC, CKA_SIGN,
|
||||
expectedKeyLen));
|
||||
ASSERT_NE(result, nullptr);
|
||||
|
||||
ASSERT_EQ(PK11_ExtractKeyValue(result.get()), SECSuccess);
|
||||
|
||||
/* We don't need to free this -- it is just a reference... */
|
||||
SECItem *actualItem = PK11_GetKeyData(result.get());
|
||||
ASSERT_NE(actualItem, nullptr);
|
||||
|
||||
SECItem expectedItem = {siBuffer, expectedKey, expectedKeyLen};
|
||||
ASSERT_EQ(SECITEM_CompareItem(actualItem, &expectedItem), 0);
|
||||
|
||||
/* Extract the additional key. */
|
||||
if (expectedAdditional == NULL || kdfParams->ulAdditionalDerivedKeys != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedPK11SlotInfo slot(PK11_GetSlotFromKey(result.get()));
|
||||
|
||||
CK_OBJECT_HANDLE_PTR keyHandle = kdfParams->pAdditionalDerivedKeys[0].phKey;
|
||||
ScopedPK11SymKey additionalKey(
|
||||
PK11_SymKeyFromHandle(slot.get(), result.get(), PK11_OriginDerive,
|
||||
CKM_SHA512_HMAC, *keyHandle, PR_FALSE, NULL));
|
||||
|
||||
ASSERT_EQ(PK11_ExtractKeyValue(additionalKey.get()), SECSuccess);
|
||||
|
||||
/* We don't need to free this -- it is just a reference... */
|
||||
actualItem = PK11_GetKeyData(additionalKey.get());
|
||||
ASSERT_NE(actualItem, nullptr);
|
||||
|
||||
expectedItem = {siBuffer, expectedAdditional, expectedAdditionalLen};
|
||||
ASSERT_EQ(SECITEM_CompareItem(actualItem, &expectedItem), 0);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(Pkcs11KbkdfTest, TestAdditionalKey) {
|
||||
/* Test number 11 of NIST CAVP vectors for Counter mode KDF, with counter
|
||||
* after a fixed input (AES/128 CMAC). Resulting key (of size 256 bits)
|
||||
* split into two 128-bit chunks since that aligns with a PRF invocation
|
||||
* boundary. */
|
||||
CK_BYTE inputKey[] = {0x23, 0xeb, 0x06, 0x5b, 0xe1, 0x27, 0xa8, 0x81,
|
||||
0xe3, 0x5a, 0x65, 0x14, 0xd4, 0x35, 0x67, 0x9f};
|
||||
CK_BYTE expectedKey[] = {0xea, 0x4e, 0xbb, 0xb4, 0xef, 0xff, 0x4b, 0x01,
|
||||
0x68, 0x40, 0x12, 0xed, 0x8f, 0xf9, 0xc6, 0x4e};
|
||||
CK_BYTE expectedAdditional[] = {0x70, 0xae, 0x38, 0x19, 0x7c, 0x36,
|
||||
0x44, 0x5a, 0x6c, 0x80, 0x4a, 0x0e,
|
||||
0x44, 0x81, 0x9a, 0xc3};
|
||||
|
||||
CK_SP800_108_COUNTER_FORMAT iterator = {CK_FALSE, 8};
|
||||
CK_BYTE fixedData[] = {
|
||||
0xe6, 0x79, 0x86, 0x1a, 0x61, 0x34, 0x65, 0xa6, 0x73, 0x85, 0x37, 0x26,
|
||||
0x71, 0xb1, 0x07, 0xe6, 0xb8, 0x95, 0xa2, 0xf6, 0x40, 0x43, 0xc9, 0x34,
|
||||
0xff, 0x42, 0x56, 0xa7, 0xe6, 0x3c, 0xfb, 0x8b, 0xfa, 0xcc, 0x21, 0x24,
|
||||
0x25, 0x1c, 0x90, 0xfa, 0x67, 0x0d, 0x45, 0x74, 0x5c, 0x1c, 0x35, 0xda,
|
||||
0x9b, 0x6e, 0x05, 0xaf, 0x77, 0xea, 0x9c, 0x4a, 0xd4, 0x86, 0xfd, 0x1a};
|
||||
|
||||
CK_PRF_DATA_PARAM dataParams[] = {
|
||||
{CK_SP800_108_BYTE_ARRAY, fixedData,
|
||||
sizeof(fixedData) / sizeof(*fixedData)},
|
||||
{CK_SP800_108_ITERATION_VARIABLE, &iterator, sizeof(iterator)}};
|
||||
|
||||
CK_KEY_TYPE ckGeneric = CKK_GENERIC_SECRET;
|
||||
CK_OBJECT_CLASS ckClass = CKO_SECRET_KEY;
|
||||
CK_ULONG derivedLength = 16;
|
||||
|
||||
CK_ATTRIBUTE derivedTemplate[] = {
|
||||
{CKA_CLASS, &ckClass, sizeof(ckClass)},
|
||||
{CKA_KEY_TYPE, &ckGeneric, sizeof(ckGeneric)},
|
||||
{CKA_VALUE_LEN, &derivedLength, sizeof(derivedLength)}};
|
||||
|
||||
CK_OBJECT_HANDLE keyHandle;
|
||||
CK_DERIVED_KEY derivedKey = {
|
||||
derivedTemplate, sizeof(derivedTemplate) / sizeof(*derivedTemplate),
|
||||
&keyHandle};
|
||||
|
||||
CK_SP800_108_KDF_PARAMS kdfParams = {CKM_AES_CMAC,
|
||||
sizeof(dataParams) / sizeof(*dataParams),
|
||||
dataParams, 1, &derivedKey};
|
||||
|
||||
RunKDF(CKM_SP800_108_COUNTER_KDF, &kdfParams, inputKey,
|
||||
sizeof(inputKey) / sizeof(*inputKey), expectedKey,
|
||||
sizeof(expectedKey) / sizeof(*expectedKey), expectedAdditional,
|
||||
sizeof(expectedAdditional) / sizeof(*expectedAdditional));
|
||||
}
|
||||
|
||||
// Close the namespace
|
||||
} // namespace nss_test
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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/. */
|
||||
|
|
|
|||
|
|
@ -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/. */
|
||||
|
|
|
|||
204
security/nss/gtests/pk11_gtest/pk11_rsaencrypt_unittest.cc
Normal file
204
security/nss/gtests/pk11_gtest/pk11_rsaencrypt_unittest.cc
Normal 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
|
||||
285
security/nss/gtests/pk11_gtest/pk11_rsaoaep_unittest.cc
Normal file
285
security/nss/gtests/pk11_gtest/pk11_rsaoaep_unittest.cc
Normal 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, ¶ms_item, decrypted.data(),
|
||||
&decrypted_len, decrypted.size(), vec.ct.data(), vec.ct.size());
|
||||
|
||||
if (vec.valid) {
|
||||
EXPECT_EQ(SECSuccess, rv);
|
||||
decrypted.resize(decrypted_len);
|
||||
EXPECT_EQ(vec.msg, decrypted);
|
||||
} else {
|
||||
EXPECT_EQ(SECFailure, rv);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
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, ¶m,
|
||||
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, ¶m, 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ¶ms_; }
|
||||
|
||||
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(), ¶ms, &sig, &data);
|
||||
EXPECT_EQ(rv, SECSuccess);
|
||||
|
||||
// Verify.
|
||||
rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), ¶ms, &sig, &data,
|
||||
nullptr);
|
||||
rv = PK11_VerifyWithMechanism(pub_key.get(), mechanism(), ¶ms, &sig,
|
||||
&data, nullptr);
|
||||
EXPECT_EQ(rv, SECSuccess);
|
||||
|
||||
// Verification with modified data must fail.
|
||||
data.data[0] ^= 0xff;
|
||||
rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), ¶ms, &sig, &data,
|
||||
nullptr);
|
||||
rv = PK11_VerifyWithMechanism(pub_key.get(), mechanism(), ¶ms, &sig,
|
||||
&data, nullptr);
|
||||
EXPECT_EQ(rv, SECFailure);
|
||||
|
||||
// Verification with original data but the wrong signature must fail.
|
||||
data.data[0] ^= 0xff; // Revert previous changes.
|
||||
sig.data[0] ^= 0xff;
|
||||
rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism(), ¶ms, &sig, &data,
|
||||
nullptr);
|
||||
rv = PK11_VerifyWithMechanism(pub_key.get(), mechanism(), ¶ms, &sig,
|
||||
&data, nullptr);
|
||||
EXPECT_EQ(rv, SECFailure);
|
||||
}
|
||||
|
||||
|
|
@ -99,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
|
||||
|
|
|
|||
|
|
@ -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, ¶ms, output.data(),
|
||||
&output_len, output_size, data.data(), data.size());
|
||||
} else {
|
||||
return PK11_Decrypt(sym_key.get(), kMech, ¶ms, output.data(),
|
||||
&output_len, output_size, data.data(), data.size());
|
||||
EXPECT_EQ(expected,
|
||||
PK11_Encrypt(sym_key.get(), mech, &iv_param, ciphertext.data(),
|
||||
&output_len, output_size, plaintext.data(),
|
||||
plaintext.size()));
|
||||
|
||||
if (expected == SECSuccess) {
|
||||
EXPECT_EQ(expected,
|
||||
PK11_Decrypt(sym_key.get(), mech, &iv_param, decrypted.data(),
|
||||
&output_len, output_size, ciphertext.data(),
|
||||
output_len));
|
||||
decrypted.resize(output_len);
|
||||
EXPECT_EQ(plaintext, decrypted);
|
||||
}
|
||||
}
|
||||
const CK_MECHANISM_TYPE kMech = CKM_SEED_CBC;
|
||||
};
|
||||
|
||||
#ifndef NSS_DISABLE_DEPRECATED_SEED
|
||||
// The intention here is to test the arguments of these functions
|
||||
// The resulted content is already tested in EncryptDeriveTests.
|
||||
// SEED_CBC needs an IV of 16 bytes.
|
||||
// The input data size must be multiple of 16.
|
||||
// If not, some padding should be added.
|
||||
// The output size must be at least the size of input data.
|
||||
TEST_F(Pkcs11SeedCbcTest, SeedCBC_ValidArgs) {
|
||||
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Encrypt, 16, 16));
|
||||
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Decrypt, 16, 16));
|
||||
TEST_F(Pkcs11SeedTest, CBC_ValidArgs) {
|
||||
EncryptDecryptSeed(SECSuccess, 16, 16);
|
||||
// No problem if maxLen is bigger than input data.
|
||||
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Encrypt, 16, 32));
|
||||
EXPECT_EQ(SECSuccess, EncryptDecryptSeed(Action::Decrypt, 16, 32));
|
||||
EncryptDecryptSeed(SECSuccess, 16, 32);
|
||||
}
|
||||
|
||||
TEST_F(Pkcs11SeedCbcTest, SeedCBC_InvalidArgs) {
|
||||
TEST_F(Pkcs11SeedTest, CBC_InvalidArgs) {
|
||||
// maxLen lower than input data.
|
||||
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Encrypt, 16, 10));
|
||||
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Decrypt, 16, 10));
|
||||
EncryptDecryptSeed(SECFailure, 16, 10);
|
||||
// input data not multiple of SEED_BLOCK_SIZE (16)
|
||||
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Encrypt, 17, 32));
|
||||
EXPECT_EQ(SECFailure, EncryptDecryptSeed(Action::Decrypt, 17, 32));
|
||||
EncryptDecryptSeed(SECFailure, 17, 32);
|
||||
}
|
||||
|
||||
} // 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
|
||||
|
|
|
|||
179
security/nss/gtests/pk11_gtest/pk11_signature_test.cc
Normal file
179
security/nss/gtests/pk11_gtest/pk11_signature_test.cc
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue