mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1724869 - land NSS 56238350052a UPGRADE_NSS_RELEASE, r=djackson
Differential Revision: https://phabricator.services.mozilla.com/D122202
This commit is contained in:
parent
3095d71dc7
commit
46e2563077
@ -1 +1 @@
|
||||
NSS_3_69_RTM
|
||||
56238350052a
|
@ -35,6 +35,7 @@ RUN apt-get update \
|
||||
valgrind \
|
||||
zlib1g-dev \
|
||||
clang-format-3.9 \
|
||||
sqlite3 \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& apt-get autoremove -y && apt-get clean -y
|
||||
|
||||
|
@ -11,6 +11,7 @@ RUN apt-get update \
|
||||
make \
|
||||
patch \
|
||||
mercurial \
|
||||
sqlite3 \
|
||||
zlib1g-dev \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& apt-get autoremove -y && apt-get clean -y
|
||||
|
@ -20,6 +20,7 @@ RUN apt-get update \
|
||||
mercurial \
|
||||
ninja-build \
|
||||
pkg-config \
|
||||
sqlite3 \
|
||||
zlib1g-dev \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& apt-get autoremove -y && apt-get clean -y
|
||||
|
@ -10,3 +10,4 @@
|
||||
*/
|
||||
|
||||
#error "Do not include this header file."
|
||||
|
||||
|
@ -8,6 +8,7 @@ Releases
|
||||
:glob:
|
||||
:hidden:
|
||||
|
||||
nss_3_69.rst
|
||||
nss_3_68.rst
|
||||
nss_3_67.rst
|
||||
nss_3_66.rst
|
||||
@ -16,22 +17,21 @@ Releases
|
||||
|
||||
.. note::
|
||||
|
||||
**NSS 3.68** is the latest version of NSS.
|
||||
**NSS 3.69** is the latest version of NSS.
|
||||
|
||||
Complete release notes are available here: :ref:`mozilla_projects_nss_nss_3_68_release_notes`
|
||||
Complete release notes are available here: :ref:`mozilla_projects_nss_nss_3_69_release_notes`
|
||||
|
||||
.. container::
|
||||
|
||||
Changes included in this release:
|
||||
|
||||
- Bug 1709654 - Update for NetBSD configuration.
|
||||
- Bug 1709750 - Disable HPKE test when fuzzing.
|
||||
- Bug 1566124 - Optimize AES-GCM for ppc64le.
|
||||
- Bug 1699021 - Add AES-256-GCM to HPKE.
|
||||
- Bug 1698419 - ECH -10 updates.
|
||||
- Bug 1692930 - Update HPKE to final version.
|
||||
- Bug 1707130 - NSS should use modern algorithms in PKCS#12 files by default.
|
||||
- Bug 1703936 - New coverity/cpp scanner errors.
|
||||
- Bug 1697303 - NSS needs to update it's csp clearing to FIPS 180-3 standards.
|
||||
- Bug 1702663 - Need to support RSA PSS with Hashing PKCS #11 Mechanisms.
|
||||
- Bug 1705119 - Deadlock when using GCM and non-thread safe tokens.
|
||||
- Bug 1722613 - Disable DTLS 1.0 and 1.1 by default
|
||||
- Bug 1720226 - integrity checks in key4.db not happening on private components with AES_CBC
|
||||
- Bug 1720235 - SSL handling of signature algorithms ignores environmental invalid algorithms.
|
||||
- Bug 1721476 - sqlite 3.34 changed it's open semantics, causing nss failures.
|
||||
- Bug 1720230 - Gtest update changed the gtest reports, losing gtest details in all.sh reports.
|
||||
- Bug 1720228 - NSS incorrectly accepting 1536 bit DH primes in FIPS mode
|
||||
- Bug 1720232 - SQLite calls could timeout in starvation situations.
|
||||
- Bug 1720225 - Coverity/cpp scanner errors found in nss 3.67
|
||||
- Bug 1709817 - Import the NSS documentation from MDN in nss/doc.
|
||||
- Bug 1720227 - NSS using a tempdir to measure sql performance not active
|
||||
|
64
security/nss/doc/rst/releases/nss_3_69.rst
Normal file
64
security/nss/doc/rst/releases/nss_3_69.rst
Normal file
@ -0,0 +1,64 @@
|
||||
.. _mozilla_projects_nss_nss_3_69_release_notes:
|
||||
|
||||
NSS 3.69 release notes
|
||||
======================
|
||||
|
||||
`Introduction <#introduction>`__
|
||||
--------------------------------
|
||||
|
||||
.. container::
|
||||
|
||||
Network Security Services (NSS) 3.69 was released on **5 August 2021**.
|
||||
|
||||
.. _distribution_information:
|
||||
|
||||
`Distribution Information <#distribution_information>`__
|
||||
--------------------------------------------------------
|
||||
|
||||
.. container::
|
||||
|
||||
The HG tag is NSS_3_69_RTM. NSS 3.69 requires NSPR 4.32 or newer.
|
||||
|
||||
NSS 3.69 source distributions are available on ftp.mozilla.org for secure HTTPS download:
|
||||
|
||||
- Source tarballs:
|
||||
https://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_69_RTM/src/
|
||||
|
||||
Other releases are available :ref:`mozilla_projects_nss_releases`.
|
||||
|
||||
.. _bugs_fixed_in_nss_3.69:
|
||||
|
||||
`Bugs fixed in NSS 3.69 <#bugs_fixed_in_nss_3.69>`__
|
||||
----------------------------------------------------
|
||||
|
||||
.. container::
|
||||
|
||||
- Bug 1722613 - Disable DTLS 1.0 and 1.1 by default
|
||||
- Bug 1720226 - integrity checks in key4.db not happening on private components with AES_CBC
|
||||
- Bug 1720235 - SSL handling of signature algorithms ignores environmental invalid algorithms.
|
||||
- Bug 1721476 - sqlite 3.34 changed it's open semantics, causing nss failures.
|
||||
- Bug 1720230 - Gtest update changed the gtest reports, losing gtest details in all.sh reports.
|
||||
- Bug 1720228 - NSS incorrectly accepting 1536 bit DH primes in FIPS mode
|
||||
- Bug 1720232 - SQLite calls could timeout in starvation situations.
|
||||
- Bug 1720225 - Coverity/cpp scanner errors found in nss 3.67
|
||||
- Bug 1709817 - Import the NSS documentation from MDN in nss/doc.
|
||||
- Bug 1720227 - NSS using a tempdir to measure sql performance not active
|
||||
|
||||
`Compatibility <#compatibility>`__
|
||||
----------------------------------
|
||||
|
||||
.. container::
|
||||
|
||||
NSS 3.69 shared libraries are backwards-compatible with all older NSS 3.x shared libraries. A
|
||||
program linked with older NSS 3.x shared libraries will work with NSS 3.69 shared libraries
|
||||
without recompiling or relinking. Furthermore, applications that restrict their use of NSS APIs
|
||||
to the functions listed in NSS Public Functions will remain compatible with future versions of
|
||||
the NSS shared libraries.
|
||||
|
||||
`Feedback <#feedback>`__
|
||||
------------------------
|
||||
|
||||
.. container::
|
||||
|
||||
Bugs discovered should be reported by filing a bug report on
|
||||
`bugzilla.mozilla.org <https://bugzilla.mozilla.org/enter_bug.cgi?product=NSS>`__ (product NSS).
|
@ -6,6 +6,15 @@
|
||||
#define GTEST_HAS_RTTI 0
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// Tests are passed the location of their source directory
|
||||
// so that they can load extra resources from there.
|
||||
std::string g_source_dir;
|
||||
|
||||
void usage(const char *progname) {
|
||||
PR_fprintf(PR_STDERR, "Usage: %s [-s <dir>] [-d <dir> [-w]]\n", progname);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
@ -13,13 +22,18 @@ int main(int argc, char **argv) {
|
||||
uint32_t flags = NSS_INIT_READONLY;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "-d")) {
|
||||
if (!strcmp(argv[i], "-s")) {
|
||||
if (i + 1 >= argc) {
|
||||
PR_fprintf(PR_STDERR, "Usage: %s [-d <dir> [-w]]\n", argv[0]);
|
||||
exit(2);
|
||||
usage(argv[0]);
|
||||
}
|
||||
workdir = argv[i + 1];
|
||||
i++;
|
||||
g_source_dir = argv[i];
|
||||
} else if (!strcmp(argv[i], "-d")) {
|
||||
if (i + 1 >= argc) {
|
||||
usage(argv[0]);
|
||||
}
|
||||
i++;
|
||||
workdir = argv[i];
|
||||
} else if (!strcmp(argv[i], "-w")) {
|
||||
flags &= ~NSS_INIT_READONLY;
|
||||
}
|
||||
|
@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# This script converts the test vectors referenced by the specification into
|
||||
# a form that matches our implementation.
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
def pkcs8(sk, pk):
|
||||
print(
|
||||
f'"3067020100301406072a8648ce3d020106092b06010401da470f01044c304a0201010420{sk}a123032100{pk}",'
|
||||
)
|
||||
|
||||
|
||||
i = 0
|
||||
for tc in json.load(sys.stdin):
|
||||
# Only mode_base and mode_psk
|
||||
if tc["mode"] != 0 and tc["mode"] != 1:
|
||||
continue
|
||||
# X25519
|
||||
if tc["kem_id"] != 32:
|
||||
continue
|
||||
# SHA-2 256, 384, and 512 (1..3)
|
||||
if not tc["kdf_id"] in [1, 2, 3]:
|
||||
continue
|
||||
# AES-128-GCM, AES-256-GCM, and ChaCha20Poly1305 (1..3 also)
|
||||
if not tc["aead_id"] in [1, 2, 3]:
|
||||
continue
|
||||
|
||||
print(f"{{{i},")
|
||||
print(f"static_cast<HpkeModeId>({tc['mode']}),")
|
||||
print(f"static_cast<HpkeKemId>({tc['kem_id']}),")
|
||||
print(f"static_cast<HpkeKdfId>({tc['kdf_id']}),")
|
||||
print(f"static_cast<HpkeAeadId>({tc['aead_id']}),")
|
||||
print(f'"{tc["info"]}", // info')
|
||||
pkcs8(tc["skEm"], tc["pkEm"])
|
||||
pkcs8(tc["skRm"], tc["pkRm"])
|
||||
print(f'"{tc.get("psk", "")}", // psk')
|
||||
print(f'"{tc.get("psk_id", "")}", // psk_id')
|
||||
print(f'"{tc["enc"]}", // enc')
|
||||
print(f'"{tc["key"]}", // key')
|
||||
print(f'"{tc["base_nonce"]}", // nonce')
|
||||
|
||||
print("{ // Encryptions")
|
||||
for e in tc["encryptions"]:
|
||||
print("{")
|
||||
print(f'"{e["plaintext"]}", // pt')
|
||||
print(f'"{e["aad"]}", // aad')
|
||||
print(f'"{e["ciphertext"]}", // ct')
|
||||
print("},")
|
||||
print("},")
|
||||
|
||||
print("{ // Exports")
|
||||
for e in tc["exports"]:
|
||||
print("{")
|
||||
print(f'"{e["exporter_context"]}", // context')
|
||||
print(f'{e["L"]}, // len')
|
||||
print(f'"{e["exported_value"]}", // exported')
|
||||
print("},")
|
||||
print("},")
|
||||
print("},")
|
||||
i = i + 1
|
File diff suppressed because it is too large
Load Diff
@ -271,19 +271,3 @@ TEST_F(Blake2BTests, EmptyKeyTest) {
|
||||
EXPECT_EQ(SECFailure, rv);
|
||||
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError());
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
if (NSS_NoDB_Init(nullptr) != SECSuccess) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rv = RUN_ALL_TESTS();
|
||||
|
||||
if (NSS_Shutdown() != SECSuccess) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -30,12 +30,14 @@
|
||||
'target_name': 'freebl_gtest',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'mpi_unittest.cc',
|
||||
'blake2b_unittest.cc',
|
||||
'cmac_unittests.cc',
|
||||
'dh_unittest.cc',
|
||||
'ecl_unittest.cc',
|
||||
'ghash_unittest.cc',
|
||||
'mpi_unittest.cc',
|
||||
'prng_kat_unittest.cc',
|
||||
'rsa_unittest.cc',
|
||||
'cmac_unittests.cc',
|
||||
'<(DEPTH)/gtests/common/gtests.cc'
|
||||
],
|
||||
'dependencies': [
|
||||
@ -50,28 +52,6 @@
|
||||
}],
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'prng_gtest',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'prng_kat_unittest.cc',
|
||||
],
|
||||
'dependencies': [
|
||||
'freebl_gtest_deps',
|
||||
'<(DEPTH)/exports.gyp:nss_exports',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'blake2b_gtest',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'blake2b_unittest.cc',
|
||||
],
|
||||
'dependencies': [
|
||||
'freebl_gtest_deps',
|
||||
'<(DEPTH)/exports.gyp:nss_exports',
|
||||
],
|
||||
},
|
||||
],
|
||||
'target_defaults': {
|
||||
'include_dirs': [
|
||||
|
@ -15,9 +15,11 @@
|
||||
|
||||
#include "blapi.h"
|
||||
|
||||
extern std::string g_source_dir;
|
||||
|
||||
namespace nss_test {
|
||||
|
||||
typedef struct PRNGTestValuesStr {
|
||||
struct PRNGTestValues {
|
||||
std::vector<uint8_t> entropy;
|
||||
std::vector<uint8_t> nonce;
|
||||
std::vector<uint8_t> personal;
|
||||
@ -25,9 +27,7 @@ typedef struct PRNGTestValuesStr {
|
||||
std::vector<uint8_t> additional_entropy;
|
||||
std::vector<uint8_t> additional_input_reseed;
|
||||
std::vector<std::vector<uint8_t>> additional_input;
|
||||
} PRNGTestValues;
|
||||
|
||||
std::vector<PRNGTestValues> test_vector;
|
||||
};
|
||||
|
||||
bool contains(std::string& s, const char* to_find) {
|
||||
return s.find(to_find) != std::string::npos;
|
||||
@ -59,8 +59,10 @@ void print_bytes(std::vector<uint8_t> bytes, std::string name) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
static void ReadFile(const std::string file_name) {
|
||||
static std::vector<PRNGTestValues> ReadFile(const std::string file_name) {
|
||||
std::vector<PRNGTestValues> test_vector;
|
||||
std::ifstream infile(file_name);
|
||||
EXPECT_FALSE(infile.fail()) << "kat file: " << file_name;
|
||||
std::string line;
|
||||
|
||||
// Variables holding the input for each test.
|
||||
@ -123,11 +125,17 @@ static void ReadFile(const std::string file_name) {
|
||||
test = {};
|
||||
infile.seekg(pos);
|
||||
}
|
||||
return test_vector;
|
||||
}
|
||||
|
||||
class PRNGTest : public ::testing::TestWithParam<PRNGTestValues> {
|
||||
class PRNGTest : public ::testing::Test {
|
||||
protected:
|
||||
void RunTest(PRNGTestValues test) {
|
||||
void SetUp() override {
|
||||
test_vector_ = ReadFile(::g_source_dir + "/kat/Hash_DRBG.rsp");
|
||||
ASSERT_FALSE(test_vector_.empty());
|
||||
}
|
||||
|
||||
void RunTest(PRNGTestValues& test) {
|
||||
ASSERT_EQ(2U, test.additional_input.size());
|
||||
SECStatus rv = PRNGTEST_Instantiate_Kat(
|
||||
test.entropy.data(), test.entropy.size(), test.nonce.data(),
|
||||
@ -154,34 +162,15 @@ class PRNGTest : public ::testing::TestWithParam<PRNGTestValues> {
|
||||
rv = PRNGTEST_Uninstantiate();
|
||||
ASSERT_EQ(SECSuccess, rv);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<PRNGTestValues> test_vector_;
|
||||
};
|
||||
|
||||
TEST_P(PRNGTest, HashDRBG) { RunTest(GetParam()); }
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(NISTTestVector, PRNGTest,
|
||||
::testing::ValuesIn(test_vector));
|
||||
|
||||
} // nss_test
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
std::cout << "usage: prng_gtest <.rsp file>" << std::endl;
|
||||
return 1;
|
||||
TEST_F(PRNGTest, HashDRBG) {
|
||||
for (auto& v : test_vector_) {
|
||||
RunTest(v);
|
||||
}
|
||||
|
||||
nss_test::ReadFile(argv[1]);
|
||||
assert(!nss_test::test_vector.empty());
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
|
||||
if (NSS_NoDB_Init(nullptr) != SECSuccess) {
|
||||
return 1;
|
||||
}
|
||||
int rv = RUN_ALL_TESTS();
|
||||
|
||||
if (NSS_Shutdown() != SECSuccess) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace nss_test
|
||||
|
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
@ -4,8 +4,6 @@
|
||||
* 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 UNSAFE_FUZZER_MODE // See Bug 1709750
|
||||
|
||||
#include <memory>
|
||||
#include "blapi.h"
|
||||
#include "gtest/gtest.h"
|
||||
@ -15,9 +13,10 @@
|
||||
#include "pk11pub.h"
|
||||
#include "secerr.h"
|
||||
#include "sechash.h"
|
||||
#include "testvectors/hpke-vectors.h"
|
||||
#include "util.h"
|
||||
|
||||
extern std::string g_source_dir;
|
||||
|
||||
namespace nss_test {
|
||||
|
||||
/* See note in pk11pub.h. */
|
||||
@ -75,8 +74,9 @@ class HpkeTest {
|
||||
CheckEquality(expected_vec, actual);
|
||||
}
|
||||
|
||||
void Seal(const ScopedHpkeContext &cx, std::vector<uint8_t> &aad_vec,
|
||||
std::vector<uint8_t> &pt_vec, std::vector<uint8_t> &out_sealed) {
|
||||
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()),
|
||||
@ -87,11 +87,12 @@ class HpkeTest {
|
||||
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);
|
||||
out_sealed->assign(sealed->data, sealed->data + sealed->len);
|
||||
}
|
||||
|
||||
void Open(const ScopedHpkeContext &cx, std::vector<uint8_t> &aad_vec,
|
||||
std::vector<uint8_t> &ct_vec, std::vector<uint8_t> &out_opened) {
|
||||
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()),
|
||||
@ -101,19 +102,21 @@ class HpkeTest {
|
||||
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);
|
||||
out_opened->assign(opened->data, opened->data + opened->len);
|
||||
}
|
||||
|
||||
void SealOpen(const ScopedHpkeContext &sender,
|
||||
const ScopedHpkeContext &receiver, std::vector<uint8_t> &msg,
|
||||
std::vector<uint8_t> &aad, const std::vector<uint8_t> *expect) {
|
||||
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);
|
||||
Seal(sender, aad, msg, &sealed);
|
||||
if (expect) {
|
||||
EXPECT_EQ(*expect, sealed);
|
||||
}
|
||||
Open(receiver, aad, sealed, opened);
|
||||
Open(receiver, aad, sealed, &opened);
|
||||
EXPECT_EQ(msg, opened);
|
||||
}
|
||||
|
||||
@ -221,105 +224,441 @@ class HpkeTest {
|
||||
}
|
||||
};
|
||||
|
||||
class TestVectors : public HpkeTest,
|
||||
public ::testing::TestWithParam<hpke_vector> {
|
||||
protected:
|
||||
void ReadVector(const hpke_vector &vec) {
|
||||
ScopedPK11SymKey vec_psk;
|
||||
if (!vec.psk.empty()) {
|
||||
ASSERT_FALSE(vec.psk_id.empty());
|
||||
vec_psk_id = hex_string_to_bytes(vec.psk_id);
|
||||
|
||||
std::vector<uint8_t> psk_bytes = hex_string_to_bytes(vec.psk);
|
||||
SECItem psk_item = {siBuffer, toUcharPtr(psk_bytes.data()),
|
||||
static_cast<unsigned int>(psk_bytes.size())};
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
|
||||
ASSERT_TRUE(slot);
|
||||
PK11SymKey *psk_key =
|
||||
PK11_ImportSymKey(slot.get(), CKM_HKDF_KEY_GEN, PK11_OriginUnwrap,
|
||||
CKA_WRAP, &psk_item, nullptr);
|
||||
ASSERT_NE(nullptr, psk_key);
|
||||
vec_psk_key.reset(psk_key);
|
||||
}
|
||||
|
||||
vec_pkcs8_r = hex_string_to_bytes(vec.pkcs8_r);
|
||||
vec_pkcs8_e = hex_string_to_bytes(vec.pkcs8_e);
|
||||
vec_key = hex_string_to_bytes(vec.key);
|
||||
vec_nonce = hex_string_to_bytes(vec.nonce);
|
||||
vec_enc = hex_string_to_bytes(vec.enc);
|
||||
vec_info = hex_string_to_bytes(vec.info);
|
||||
vec_encryptions = vec.encrypt_vecs;
|
||||
vec_exports = vec.export_vecs;
|
||||
// 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) : 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;
|
||||
}
|
||||
|
||||
void TestExports(const ScopedHpkeContext &sender,
|
||||
const ScopedHpkeContext &receiver) {
|
||||
for (auto &vec : vec_exports) {
|
||||
std::vector<uint8_t> context = hex_string_to_bytes(vec.ctxt);
|
||||
std::vector<uint8_t> expected = hex_string_to_bytes(vec.exported);
|
||||
SECItem context_item = {siBuffer, toUcharPtr(context.data()),
|
||||
static_cast<unsigned int>(context.size())};
|
||||
PK11SymKey *actual_r = nullptr;
|
||||
PK11SymKey *actual_s = nullptr;
|
||||
ASSERT_EQ(SECSuccess, PK11_HPKE_ExportSecret(sender.get(), &context_item,
|
||||
vec.len, &actual_s));
|
||||
ASSERT_EQ(SECSuccess,
|
||||
PK11_HPKE_ExportSecret(receiver.get(), &context_item, vec.len,
|
||||
&actual_r));
|
||||
ScopedPK11SymKey scoped_act_s(actual_s);
|
||||
ScopedPK11SymKey scoped_act_r(actual_r);
|
||||
CheckEquality(expected, scoped_act_s.get());
|
||||
CheckEquality(expected, scoped_act_r.get());
|
||||
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() {
|
||||
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 ReadLabel() {
|
||||
SkipWhitespace();
|
||||
if (peek() != '"') {
|
||||
return "";
|
||||
}
|
||||
next();
|
||||
|
||||
std::string s;
|
||||
uint8_t c = take();
|
||||
while (c != '"') {
|
||||
s.push_back(c);
|
||||
c = take();
|
||||
}
|
||||
SkipWhitespace();
|
||||
EXPECT_EQ(take(), ':');
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> 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;
|
||||
}
|
||||
|
||||
bool 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 SkipValue() {
|
||||
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 {
|
||||
ADD_FAILURE() << "No idea how to skip'" << c << "'";
|
||||
}
|
||||
}
|
||||
|
||||
void TestEncryptions(const ScopedHpkeContext &sender,
|
||||
const ScopedHpkeContext &receiver) {
|
||||
for (auto &enc_vec : vec_encryptions) {
|
||||
std::vector<uint8_t> msg = hex_string_to_bytes(enc_vec.pt);
|
||||
std::vector<uint8_t> aad = hex_string_to_bytes(enc_vec.aad);
|
||||
std::vector<uint8_t> expect_ct = hex_string_to_bytes(enc_vec.ct);
|
||||
SealOpen(sender, receiver, msg, aad, &expect_ct);
|
||||
}
|
||||
}
|
||||
|
||||
void ImportKeyPairs(const ScopedHpkeContext &sender,
|
||||
const ScopedHpkeContext &receiver) {
|
||||
ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
|
||||
if (!slot) {
|
||||
ADD_FAILURE() << "No slot";
|
||||
private:
|
||||
void 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;
|
||||
}
|
||||
}
|
||||
|
||||
SECItem pkcs8_e_item = {siBuffer, toUcharPtr(vec_pkcs8_e.data()),
|
||||
static_cast<unsigned int>(vec_pkcs8_e.size())};
|
||||
SECKEYPrivateKey *sk_e = nullptr;
|
||||
EXPECT_EQ(SECSuccess, PK11_ImportDERPrivateKeyInfoAndReturnKey(
|
||||
slot.get(), &pkcs8_e_item, nullptr, nullptr,
|
||||
false, false, KU_ALL, &sk_e, nullptr));
|
||||
skE_derived.reset(sk_e);
|
||||
SECKEYPublicKey *pk_e = SECKEY_ConvertToPublicKey(skE_derived.get());
|
||||
ASSERT_NE(nullptr, pk_e);
|
||||
pkE_derived.reset(pk_e);
|
||||
void SkipWhitespace() {
|
||||
uint8_t c = peek();
|
||||
while (c && (c == ' ' || c == '\t' || c == '\r' || c == '\n')) {
|
||||
next();
|
||||
c = peek();
|
||||
}
|
||||
}
|
||||
|
||||
SECItem pkcs8_r_item = {siBuffer, toUcharPtr(vec_pkcs8_r.data()),
|
||||
static_cast<unsigned int>(vec_pkcs8_r.size())};
|
||||
SECKEYPrivateKey *sk_r = nullptr;
|
||||
EXPECT_EQ(SECSuccess, PK11_ImportDERPrivateKeyInfoAndReturnKey(
|
||||
slot.get(), &pkcs8_r_item, nullptr, nullptr,
|
||||
false, false, KU_ALL, &sk_r, nullptr));
|
||||
skR_derived.reset(sk_r);
|
||||
SECKEYPublicKey *pk_r = SECKEY_ConvertToPublicKey(skR_derived.get());
|
||||
ASSERT_NE(nullptr, pk_r);
|
||||
pkR_derived.reset(pk_r);
|
||||
// This only handles lowercase.
|
||||
uint8_t Hex(uint8_t c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
EXPECT_TRUE(c >= 'a' && c <= 'f');
|
||||
return c - 'a' + 10;
|
||||
}
|
||||
|
||||
ScopedPRFileDesc f_;
|
||||
uint8_t buf_[4096];
|
||||
size_t available_;
|
||||
size_t i_;
|
||||
};
|
||||
|
||||
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.NextItem('[', ']')) {
|
||||
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.NextItem('[', ']')) {
|
||||
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.NextItem('[', ']')) {
|
||||
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(vec_info.data()),
|
||||
static_cast<unsigned int>(vec_info.size())};
|
||||
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));
|
||||
}
|
||||
@ -330,65 +669,43 @@ class TestVectors : public HpkeTest,
|
||||
const std::vector<uint8_t> &info) {
|
||||
SECItem enc_item = {siBuffer, toUcharPtr(enc.data()),
|
||||
static_cast<unsigned int>(enc.size())};
|
||||
SECItem info_item = {siBuffer, toUcharPtr(vec_info.data()),
|
||||
static_cast<unsigned int>(vec_info.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 ScopedHpkeContext &sender,
|
||||
const ScopedHpkeContext &receiver) {
|
||||
SetupS(sender, pkE_derived, skE_derived, pkR_derived, vec_info);
|
||||
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(pkE_derived.get(), encap_item.data,
|
||||
&encap_item.len, encap_item.len));
|
||||
CheckEquality(vec_enc, &encap_item);
|
||||
SetupR(receiver, pkR_derived, skR_derived, vec_enc, vec_info);
|
||||
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 hpke_vector &vec) {
|
||||
ReadVector(vec);
|
||||
SECItem psk_id_item = {siBuffer, toUcharPtr(vec_psk_id.data()),
|
||||
static_cast<unsigned int>(vec_psk_id.size())};
|
||||
PK11SymKey *psk = vec_psk_key ? vec_psk_key.get() : nullptr;
|
||||
SECItem *psk_id = psk ? &psk_id_item : nullptr;
|
||||
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));
|
||||
|
||||
ScopedHpkeContext sender(
|
||||
PK11_HPKE_NewContext(vec.kem_id, vec.kdf_id, vec.aead_id, psk, psk_id));
|
||||
ScopedHpkeContext receiver(
|
||||
PK11_HPKE_NewContext(vec.kem_id, vec.kdf_id, vec.aead_id, psk, psk_id));
|
||||
ASSERT_TRUE(sender);
|
||||
ASSERT_TRUE(receiver);
|
||||
|
||||
ImportKeyPairs(sender, receiver);
|
||||
SetupSenderReceiver(sender, receiver);
|
||||
TestEncryptions(sender, receiver);
|
||||
TestExports(sender, receiver);
|
||||
SetupSenderReceiver(vec, sender, receiver);
|
||||
TestEncryptions(vec, sender, receiver);
|
||||
TestExports(vec, sender, receiver);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPK11SymKey vec_psk_key;
|
||||
std::vector<uint8_t> vec_psk_id;
|
||||
std::vector<uint8_t> vec_pkcs8_e;
|
||||
std::vector<uint8_t> vec_pkcs8_r;
|
||||
std::vector<uint8_t> vec_enc;
|
||||
std::vector<uint8_t> vec_info;
|
||||
std::vector<uint8_t> vec_key;
|
||||
std::vector<uint8_t> vec_nonce;
|
||||
std::vector<hpke_encrypt_vector> vec_encryptions;
|
||||
std::vector<hpke_export_vector> vec_exports;
|
||||
ScopedSECKEYPublicKey pkE_derived;
|
||||
ScopedSECKEYPublicKey pkR_derived;
|
||||
ScopedSECKEYPrivateKey skE_derived;
|
||||
ScopedSECKEYPrivateKey skR_derived;
|
||||
};
|
||||
|
||||
TEST_P(TestVectors, TestVectors) { RunTestVector(GetParam()); }
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(Pk11Hpke, TestVectors,
|
||||
::testing::ValuesIn(kHpkeTestVectors));
|
||||
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,
|
||||
@ -685,5 +1002,3 @@ TEST_F(ModeParameterizedTest, InvalidReceiverKeyType) {
|
||||
}
|
||||
|
||||
} // namespace nss_test
|
||||
|
||||
#endif
|
||||
|
@ -287,9 +287,12 @@ sftkdb_DecryptAttribute(SFTKDBHandle *handle, SECItem *passKey,
|
||||
}
|
||||
|
||||
/* If we are using aes 256, we need to check authentication as well.*/
|
||||
if ((type != CKT_INVALID_TYPE) && (cipherValue.alg == SEC_OID_AES_256_CBC)) {
|
||||
if ((type != CKT_INVALID_TYPE) &&
|
||||
(cipherValue.alg == SEC_OID_PKCS5_PBES2) &&
|
||||
(cipherValue.param->encAlg == SEC_OID_AES_256_CBC)) {
|
||||
SECItem signature;
|
||||
unsigned char signData[SDB_MAX_META_DATA_LEN];
|
||||
CK_RV crv;
|
||||
|
||||
/* if we get here from the old legacy db, there is clearly an
|
||||
* error, don't return the plaintext */
|
||||
@ -301,15 +304,28 @@ sftkdb_DecryptAttribute(SFTKDBHandle *handle, SECItem *passKey,
|
||||
|
||||
signature.data = signData;
|
||||
signature.len = sizeof(signData);
|
||||
rv = sftkdb_GetAttributeSignature(handle, handle, id, type,
|
||||
&signature);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser;
|
||||
rv = SECFailure;
|
||||
/* sign sftkdb_GetAttriibuteSignature returns a crv, not an rv */
|
||||
crv = sftkdb_GetAttributeSignature(handle, handle, id, type,
|
||||
&signature);
|
||||
if (crv == CKR_OK) {
|
||||
rv = sftkdb_VerifyAttribute(handle, passKey, CK_INVALID_HANDLE,
|
||||
type, *plain, &signature);
|
||||
}
|
||||
rv = sftkdb_VerifyAttribute(handle, passKey, CK_INVALID_HANDLE, type,
|
||||
*plain, &signature);
|
||||
if (rv != SECSuccess) {
|
||||
goto loser;
|
||||
/* handle bug 1720226 where old versions of NSS misfiled the signature
|
||||
* attribute on password update */
|
||||
id |= SFTK_KEYDB_TYPE | SFTK_TOKEN_TYPE;
|
||||
signature.len = sizeof(signData);
|
||||
crv = sftkdb_GetAttributeSignature(handle, handle, id, type,
|
||||
&signature);
|
||||
if (crv != CKR_OK) {
|
||||
rv = SECFailure;
|
||||
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
|
||||
goto loser;
|
||||
}
|
||||
rv = sftkdb_VerifyAttribute(handle, passKey, CK_INVALID_HANDLE,
|
||||
type, *plain, &signature);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1198,6 +1214,7 @@ sftk_updateEncrypted(PLArenaPool *arena, SFTKDBHandle *keydb,
|
||||
unsigned int i;
|
||||
for (i = 0; i < privAttrCount; i++) {
|
||||
// Read the old attribute in the clear.
|
||||
CK_OBJECT_HANDLE sdbId = id & SFTK_OBJ_ID_MASK;
|
||||
CK_ATTRIBUTE privAttr = { privAttrTypes[i], NULL, 0 };
|
||||
CK_RV crv = sftkdb_GetAttributeValue(keydb, id, &privAttr, 1);
|
||||
if (crv != CKR_OK) {
|
||||
@ -1222,7 +1239,7 @@ sftk_updateEncrypted(PLArenaPool *arena, SFTKDBHandle *keydb,
|
||||
plainText.data = privAttr.pValue;
|
||||
plainText.len = privAttr.ulValueLen;
|
||||
if (sftkdb_EncryptAttribute(arena, keydb, keydb->db, newKey,
|
||||
iterationCount, id, privAttr.type,
|
||||
iterationCount, sdbId, privAttr.type,
|
||||
&plainText, &result) != SECSuccess) {
|
||||
return CKR_GENERAL_ERROR;
|
||||
}
|
||||
@ -1232,10 +1249,9 @@ sftk_updateEncrypted(PLArenaPool *arena, SFTKDBHandle *keydb,
|
||||
PORT_Memset(plainText.data, 0, plainText.len);
|
||||
|
||||
// Write the newly encrypted attributes out directly.
|
||||
CK_OBJECT_HANDLE newId = id & SFTK_OBJ_ID_MASK;
|
||||
keydb->newKey = newKey;
|
||||
keydb->newDefaultIterationCount = iterationCount;
|
||||
crv = (*keydb->db->sdb_SetAttributeValue)(keydb->db, newId, &privAttr, 1);
|
||||
crv = (*keydb->db->sdb_SetAttributeValue)(keydb->db, sdbId, &privAttr, 1);
|
||||
keydb->newKey = NULL;
|
||||
if (crv != CKR_OK) {
|
||||
return crv;
|
||||
|
@ -101,12 +101,12 @@ static sslOptions ssl_defaults = {
|
||||
* default range of enabled SSL/TLS protocols
|
||||
*/
|
||||
static SSLVersionRange versions_defaults_stream = {
|
||||
SSL_LIBRARY_VERSION_TLS_1_0,
|
||||
SSL_LIBRARY_VERSION_TLS_1_2,
|
||||
SSL_LIBRARY_VERSION_TLS_1_3
|
||||
};
|
||||
|
||||
static SSLVersionRange versions_defaults_datagram = {
|
||||
SSL_LIBRARY_VERSION_TLS_1_1,
|
||||
SSL_LIBRARY_VERSION_TLS_1_2,
|
||||
SSL_LIBRARY_VERSION_TLS_1_2
|
||||
};
|
||||
|
||||
|
@ -209,8 +209,6 @@
|
||||
'gtests/cryptohi_gtest/cryptohi_gtest.gyp:cryptohi_gtest',
|
||||
'gtests/der_gtest/der_gtest.gyp:der_gtest',
|
||||
'gtests/certdb_gtest/certdb_gtest.gyp:certdb_gtest',
|
||||
'gtests/freebl_gtest/freebl_gtest.gyp:prng_gtest',
|
||||
'gtests/freebl_gtest/freebl_gtest.gyp:blake2b_gtest',
|
||||
'gtests/freebl_gtest/freebl_gtest.gyp:freebl_gtest',
|
||||
'gtests/mozpkix_gtest/mozpkix_gtest.gyp:mozpkix_gtest',
|
||||
'gtests/nss_bogo_shim/nss_bogo_shim.gyp:nss_bogo_shim',
|
||||
|
@ -75,6 +75,7 @@ Echo()
|
||||
echo "| $*"
|
||||
echo "---------------------------------------------------------------"
|
||||
}
|
||||
|
||||
dbtest_main()
|
||||
{
|
||||
cd ${HOSTDIR}
|
||||
@ -249,9 +250,9 @@ dbtest_main()
|
||||
${BINDIR}/certutil -L -n bob -d ${CONFLICT_DIR}
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ]; then
|
||||
html_failed "Nicknane conflict test-setting nickname conflict incorrectly worked"
|
||||
html_failed "Nickname conflict test-setting nickname conflict incorrectly worked"
|
||||
else
|
||||
html_passed "Nicknane conflict test-setting nickname conflict was correctly rejected"
|
||||
html_passed "Nickname conflict test-setting nickname conflict was correctly rejected"
|
||||
fi
|
||||
# import a token private key and make sure the corresponding public key is
|
||||
# created
|
||||
@ -262,6 +263,71 @@ dbtest_main()
|
||||
else
|
||||
html_passed "Importing Token Private Key correctly creates the corrresponding Public Key"
|
||||
fi
|
||||
#
|
||||
# If we are testing an sqlite db, make sure we detect corrupted attributes.
|
||||
# This test only runs if 1) we have sqlite3 (the command line sqlite diagnostic
|
||||
# tool) and 2) we are using the sql database (rather than the dbm).
|
||||
#
|
||||
which sqlite3
|
||||
ret=$?
|
||||
KEYDB=${CONFLICT_DIR}/key4.db
|
||||
# make sure sql database is bing used.
|
||||
if [ ! -f ${KEYDB} ]; then
|
||||
Echo "skipping key corruption test: requires sql database"
|
||||
# make sure sqlite3 is installed.
|
||||
elif [ $ret -ne 0 ]; then
|
||||
Echo "skipping key corruption test: sqlite3 command not installed"
|
||||
else
|
||||
# we are going to mangle this key database in multiple tests, save a copy
|
||||
# so that we can restore it later.
|
||||
cp ${KEYDB} ${KEYDB}.save
|
||||
# dump the keys in the log for diagnostic purposes
|
||||
${BINDIR}/certutil -K -d ${CONFLICT_DIR} -f ${R_PWFILE}
|
||||
# fetch the rsa and ec key ids
|
||||
rsa_id=$(${BINDIR}/certutil -K -d ${CONFLICT_DIR} -f ${R_PWFILE} | grep rsa | awk '{ print $4}')
|
||||
ec_id=$(${BINDIR}/certutil -K -d ${CONFLICT_DIR} -f ${R_PWFILE} | grep ' ec ' | awk '{ print $4}')
|
||||
# now loop through all the private attributes and mangle them one at a time
|
||||
for i in 120 122 123 124 125 126 127 128 011
|
||||
do
|
||||
Echo "testing if key corruption is detected in attribute $i"
|
||||
cp ${KEYDB}.save ${KEYDB} # restore the saved keydb
|
||||
# find all the hmacs for this key attribute and mangle each entry
|
||||
sqlite3 ${KEYDB} ".dump metadata" | sed -e "/sig_key_.*_00000$i/{s/.*VALUES('\\(.*\\)',X'\\(.*\\)',NULL.*/\\1 \\2/;p;};d" | while read sig data
|
||||
do
|
||||
# mangle the last byte of the hmac
|
||||
# The following increments the last nibble by 1 with both F and f
|
||||
# mapping to 0. This mangles both upper and lower case results, so
|
||||
# it will work on the mac.
|
||||
last=$((${#data}-1))
|
||||
newbyte=$(echo "${data:${last}}" | tr A-Fa-f0-9 B-F0b-f0-9a)
|
||||
mangle=${data::${last}}${newbyte}
|
||||
echo " amending ${sig} from ${data} to ${mangle}"
|
||||
# write the mangled entry, inserting with a key matching an existing
|
||||
# entry will overwrite the existing entry with the same key (${sig})
|
||||
sqlite3 ${KEYDB} "BEGIN TRANSACTION; INSERT INTO metaData VALUES('${sig}',X'${mangle}',NULL); COMMIT"
|
||||
done
|
||||
# pick the key based on the attribute we are mangling,
|
||||
# only CKA_VALUE (0x011) is not an RSA attribute, so we choose
|
||||
# ec for 0x011 and rsa for all the rest. We could use the dsa
|
||||
# key here, both CKA_VALUE attributes will be modifed in the loop above, but
|
||||
# ec is more common than dsa these days.
|
||||
if [ "$i" = "011" ]; then
|
||||
key_id=$ec_id
|
||||
else
|
||||
key_id=$rsa_id
|
||||
fi
|
||||
# now try to use the mangled key (try to create a cert request with the key).
|
||||
echo "${BINDIR}/certutil -R -k ${key_id} -s 'CN=BadTest, E=bad@mozilla.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US' -d ${CONFLICT_DIR} -f ${R_PWFILE} -a"
|
||||
${BINDIR}/certutil -R -k ${key_id} -s 'CN=BadTest, E=bad@mozilla.com, O=BOGUS NSS, L=Mountain View, ST=California, C=US' -d ${CONFLICT_DIR} -f ${R_PWFILE} -a
|
||||
ret=$?
|
||||
if [ ${ret} -eq 0 ]; then
|
||||
html_failed "Key attribute $i corruption not detected"
|
||||
else
|
||||
html_passed "Corrupted key attribute $i correctly disabled key"
|
||||
fi
|
||||
done
|
||||
cp ${KEYDB}.save ${KEYDB} # restore the saved keydb
|
||||
fi
|
||||
|
||||
|
||||
if [ "${NSS_DEFAULT_DB_TYPE}" = "sql" ] ; then
|
||||
|
@ -24,6 +24,8 @@ gtest_init()
|
||||
{
|
||||
cd "$(dirname "$1")"
|
||||
pwd
|
||||
SOURCE_DIR="$PWD"/../..
|
||||
|
||||
if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then
|
||||
cd ../common
|
||||
. ./init.sh
|
||||
@ -77,7 +79,7 @@ gtest_start()
|
||||
fi
|
||||
echo "executing $i"
|
||||
ASAN_OPTIONS="$ASAN_OPTIONS:$EXTRA_ASAN_OPTIONS" "${BINDIR}/$i" \
|
||||
"${SOURCE_DIR}/gtests/freebl_gtest/kat/Hash_DRBG.rsp" \
|
||||
-s "${SOURCE_DIR}/gtests/$i" \
|
||||
-d "$DIR" -w --gtest_output=xml:"${GTESTREPORT}" \
|
||||
--gtest_filter="${GTESTFILTER:-*}"
|
||||
html_msg $? 0 "$i run successfully"
|
||||
@ -99,8 +101,7 @@ gtest_cleanup()
|
||||
}
|
||||
|
||||
################## main #################################################
|
||||
GTESTS="${GTESTS:-prng_gtest certhigh_gtest certdb_gtest der_gtest pk11_gtest util_gtest freebl_gtest softoken_gtest sysinit_gtest blake2b_gtest smime_gtest mozpkix_gtest}"
|
||||
SOURCE_DIR="$PWD"/../..
|
||||
GTESTS="${GTESTS:-certhigh_gtest certdb_gtest der_gtest pk11_gtest util_gtest freebl_gtest softoken_gtest sysinit_gtest smime_gtest mozpkix_gtest}"
|
||||
gtest_init "$0"
|
||||
gtest_start
|
||||
gtest_cleanup
|
||||
|
Loading…
Reference in New Issue
Block a user