/* -*- 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 #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 &expected, SECItem *actual) { if (!actual) { EXPECT_TRUE(expected.empty()); return; } std::vector 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 &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 expected_vec(raw->data, raw->data + raw->len); CheckEquality(expected_vec, actual); } void Seal(const ScopedHpkeContext &cx, const std::vector &aad_vec, const std::vector &pt_vec, std::vector *out_sealed) { SECItem aad_item = {siBuffer, toUcharPtr(aad_vec.data()), static_cast(aad_vec.size())}; SECItem pt_item = {siBuffer, toUcharPtr(pt_vec.data()), static_cast(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 &aad_vec, const std::vector &ct_vec, std::vector *out_opened) { SECItem aad_item = {siBuffer, toUcharPtr(aad_vec.data()), static_cast(aad_vec.size())}; SECItem ct_item = {siBuffer, toUcharPtr(ct_vec.data()), static_cast(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 &msg, const std::vector &aad, const std::vector *expect) { std::vector sealed; std::vector 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 context = {'c', 't', 'x', 't'}; SECItem context_item = {siBuffer, context.data(), static_cast(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 info = {'t', 'e', 's', 't', '-', 'i', 'n', 'f', 'o'}; SECItem info_item = {siBuffer, info.data(), static_cast(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(enc), &info_item)); } }; struct HpkeEncryptVector { std::vector pt; std::vector aad; std::vector ct; static std::vector ReadVec(JsonReader &r) { std::vector 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 ctxt; size_t len; std::vector exported; static std::vector ReadVec(JsonReader &r) { std::vector 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 info; std::vector pkcs8_e; std::vector pkcs8_r; std::vector psk; std::vector psk_id; std::vector enc; std::vector key; std::vector nonce; std::vector encryptions; std::vector exports; static std::vector Pkcs8(const std::vector &sk, const std::vector &pk) { // Only X25519 format. std::vector 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 Read(JsonReader &r) { std::vector 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 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(r.ReadInt()); fields |= 1 << static_cast(RequiredFields::mode); } else if (n == "kem_id") { vec.kem_id = static_cast(r.ReadInt()); fields |= 1 << static_cast(RequiredFields::kem); } else if (n == "kdf_id") { vec.kdf_id = static_cast(r.ReadInt()); fields |= 1 << static_cast(RequiredFields::kdf); } else if (n == "aead_id") { vec.aead_id = static_cast(r.ReadInt()); fields |= 1 << static_cast(RequiredFields::aead); } else if (n == "info") { vec.info = r.ReadHex(); } else if (n == "skEm") { sk_e = r.ReadHex(); fields |= 1 << static_cast(RequiredFields::skEm); } else if (n == "pkEm") { pk_e = r.ReadHex(); fields |= 1 << static_cast(RequiredFields::pkEm); } else if (n == "skRm") { sk_r = r.ReadHex(); fields |= 1 << static_cast(RequiredFields::skRm); } else if (n == "pkRm") { pk_r = r.ReadHex(); fields |= 1 << static_cast(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(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 &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(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(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(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(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 &info) { SECItem info_item = {siBuffer, toUcharPtr(info.data()), static_cast(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 &enc, const std::vector &info) { SECItem enc_item = {siBuffer, toUcharPtr(enc.data()), static_cast(enc.size())}; SECItem info_item = {siBuffer, toUcharPtr(info.data()), static_cast(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(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> {}; 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 msg = {'s', 'e', 'c', 'r', 'e', 't'}; std::vector 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 msg = {'s', 'e', 'c', 'r', 'e', 't'}; std::vector 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 msg = {'s', 'e', 'c', 'r', 'e', 't'}; std::vector 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 msg = {'s', 'e', 'c', 'r', 'e', 't'}; std::vector 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 msg = {'s', 'e', 'c', 'r', 'e', 't'}; std::vector aad = {'a', 'a', 'd'}; SECItem msg_item = {siBuffer, msg.data(), static_cast(msg.size())}; SECItem aad_item = {siBuffer, aad.data(), static_cast(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(0xff), HpkeKdfHkdfSha256, HpkeAeadChaCha20Poly1305, nullptr, nullptr); EXPECT_EQ(nullptr, cx); EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); cx = PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, static_cast(0xff), HpkeAeadChaCha20Poly1305, nullptr, nullptr); EXPECT_EQ(nullptr, cx); EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); cx = PK11_HPKE_NewContext(HpkeDhKemX25519Sha256, HpkeKdfHkdfSha256, static_cast(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()); } } // namespace nss_test