Commit 10afb436 authored by Kevin Jacobs's avatar Kevin Jacobs

Bug 1678398 - Add Export/Import functions for HPKE context. r=mt

This patch adds and exports two new HPKE functions: `PK11_HPKE_ExportContext` and
`PK11_HPKE_ImportContext`, which are used to export a serialized HPKE context,
then later reimport that context and resume Open and Export operations. Only receiver
contexts are currently supported for export (see the rationale in pk11pub.h).

One other change introduced here is that `PK11_HPKE_GetEncapPubKey` now works as
expected on the receiver side.

If the `wrapKey` argument is provided to the Export/Import functions, then the
symmetric keys are wrapped with AES Key Wrap with Padding (SP800-38F, 6.3)
prior to serialization.

Differential Revision: https://phabricator.services.mozilla.com/D99277

--HG--
extra : moz-landing-system : lando
parent e2528512
2 Added functions:
[A] 'function SECStatus PK11_HPKE_ExportContext(const HpkeContext*, PK11SymKey*, SECItem**)' {PK11_HPKE_ExportContext@@NSS_3.62}
[A] 'function HpkeContext* PK11_HPKE_ImportContext(const SECItem*, PK11SymKey*)' {PK11_HPKE_ImportContext@@NSS_3.62}
...@@ -116,6 +116,32 @@ class HpkeTest { ...@@ -116,6 +116,32 @@ class HpkeTest {
EXPECT_EQ(msg, 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, bool GenerateKeyPair(ScopedSECKEYPublicKey &pub_key,
ScopedSECKEYPrivateKey &priv_key) { ScopedSECKEYPrivateKey &priv_key) {
ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
...@@ -437,6 +463,105 @@ TEST_F(ModeParameterizedTest, BadEncapsulatedPubKey) { ...@@ -437,6 +463,105 @@ TEST_F(ModeParameterizedTest, BadEncapsulatedPubKey) {
EXPECT_EQ(SEC_ERROR_INVALID_ARGS, PORT_GetError()); 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) { TEST_P(ModeParameterizedTest, EphemeralKeys) {
std::vector<uint8_t> msg = {'s', 'e', 'c', 'r', 'e', 't'}; std::vector<uint8_t> msg = {'s', 'e', 'c', 'r', 'e', 't'};
std::vector<uint8_t> aad = {'a', 'a', 'd'}; std::vector<uint8_t> aad = {'a', 'a', 'd'};
......
...@@ -1213,3 +1213,10 @@ PK11_PubUnwrapSymKeyWithMechanism; ...@@ -1213,3 +1213,10 @@ PK11_PubUnwrapSymKeyWithMechanism;
;+ local: ;+ local:
;+ *; ;+ *;
;+}; ;+};
;+NSS_3.62 { # NSS 3.62 release
;+ global:
PK11_HPKE_ExportContext;
PK11_HPKE_ImportContext;
;+ local:
;+ *;
;+};
\ No newline at end of file
This diff is collapsed.
...@@ -746,9 +746,28 @@ HpkeContext *PK11_HPKE_NewContext(HpkeKemId kemId, HpkeKdfId kdfId, HpkeAeadId a ...@@ -746,9 +746,28 @@ HpkeContext *PK11_HPKE_NewContext(HpkeKemId kemId, HpkeKdfId kdfId, HpkeAeadId a
SECStatus PK11_HPKE_Deserialize(const HpkeContext *cx, const PRUint8 *enc, SECStatus PK11_HPKE_Deserialize(const HpkeContext *cx, const PRUint8 *enc,
unsigned int encLen, SECKEYPublicKey **outPubKey); unsigned int encLen, SECKEYPublicKey **outPubKey);
void PK11_HPKE_DestroyContext(HpkeContext *cx, PRBool freeit); void PK11_HPKE_DestroyContext(HpkeContext *cx, PRBool freeit);
/* Serialize an initialized receiver context. This only retains the keys and
* associated information necessary to resume Export and Open operations after
* import. Serialization is currently supported for receiver contexts only.
* This is done for two reasons: 1) it avoids having to move the encryption
* sequence number outside of the token (or adding encryption context
* serialization support to softoken), and 2) we don't have to worry about IV
* reuse due to sequence number cloning.
*
* |wrapKey| is required when exporting in FIPS mode. If exported with a
* wrapping key, that same key must be provided to the import function,
* otherwise behavior is undefined.
*
* Even when exported with key wrap, HPKE expects the nonce to also be kept
* secret and that value is not protected by wrapKey. Applications are
* responsible for maintaining the confidentiality of the exported information.
*/
SECStatus PK11_HPKE_ExportContext(const HpkeContext *cx, PK11SymKey *wrapKey, SECItem **serialized);
SECStatus PK11_HPKE_ExportSecret(const HpkeContext *cx, const SECItem *info, unsigned int L, SECStatus PK11_HPKE_ExportSecret(const HpkeContext *cx, const SECItem *info, unsigned int L,
PK11SymKey **outKey); PK11SymKey **outKey);
const SECItem *PK11_HPKE_GetEncapPubKey(const HpkeContext *cx); const SECItem *PK11_HPKE_GetEncapPubKey(const HpkeContext *cx);
HpkeContext *PK11_HPKE_ImportContext(const SECItem *serialized, PK11SymKey *wrapKey);
SECStatus PK11_HPKE_Open(HpkeContext *cx, const SECItem *aad, const SECItem *ct, SECItem **outPt); SECStatus PK11_HPKE_Open(HpkeContext *cx, const SECItem *aad, const SECItem *ct, SECItem **outPt);
SECStatus PK11_HPKE_Seal(HpkeContext *cx, const SECItem *aad, const SECItem *pt, SECItem **outCt); SECStatus PK11_HPKE_Seal(HpkeContext *cx, const SECItem *aad, const SECItem *pt, SECItem **outCt);
SECStatus PK11_HPKE_Serialize(const SECKEYPublicKey *pk, PRUint8 *buf, unsigned int *len, unsigned int maxLen); SECStatus PK11_HPKE_Serialize(const SECKEYPublicKey *pk, PRUint8 *buf, unsigned int *len, unsigned int maxLen);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment