From 10afb4362522536f05b3652cdfa8dfcfc94b6402 Mon Sep 17 00:00:00 2001 From: Kevin Jacobs Date: Mon, 25 Jan 2021 17:39:56 +0000 Subject: [PATCH] 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 --- .../abi-check/expected-report-libnss3.so.txt | 5 + gtests/pk11_gtest/pk11_hpke_unittest.cc | 125 ++++++++ lib/nss/nss.def | 7 + lib/pk11wrap/pk11hpke.c | 277 +++++++++++++++++- lib/pk11wrap/pk11pub.h | 19 ++ 5 files changed, 432 insertions(+), 1 deletion(-) diff --git a/automation/abi-check/expected-report-libnss3.so.txt b/automation/abi-check/expected-report-libnss3.so.txt index e69de29bb2..f758e8bec7 100644 --- a/automation/abi-check/expected-report-libnss3.so.txt +++ b/automation/abi-check/expected-report-libnss3.so.txt @@ -0,0 +1,5 @@ + +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} diff --git a/gtests/pk11_gtest/pk11_hpke_unittest.cc b/gtests/pk11_gtest/pk11_hpke_unittest.cc index 26385f144d..48de0a7af1 100644 --- a/gtests/pk11_gtest/pk11_hpke_unittest.cc +++ b/gtests/pk11_gtest/pk11_hpke_unittest.cc @@ -116,6 +116,32 @@ class HpkeTest { 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()); @@ -437,6 +463,105 @@ TEST_F(ModeParameterizedTest, BadEncapsulatedPubKey) { 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'}; diff --git a/lib/nss/nss.def b/lib/nss/nss.def index 5ad688b4e1..db912e1ec9 100644 --- a/lib/nss/nss.def +++ b/lib/nss/nss.def @@ -1213,3 +1213,10 @@ PK11_PubUnwrapSymKeyWithMechanism; ;+ local: ;+ *; ;+}; +;+NSS_3.62 { # NSS 3.62 release +;+ global: +PK11_HPKE_ExportContext; +PK11_HPKE_ImportContext; +;+ local: +;+ *; +;+}; \ No newline at end of file diff --git a/lib/pk11wrap/pk11hpke.c b/lib/pk11wrap/pk11hpke.c index 02025cfcd0..626989fd32 100644 --- a/lib/pk11wrap/pk11hpke.c +++ b/lib/pk11wrap/pk11hpke.c @@ -53,12 +53,24 @@ PK11_HPKE_GetEncapPubKey(const HpkeContext *cx) return NULL; } SECStatus +PK11_HPKE_ExportContext(const HpkeContext *cx, PK11SymKey *wrapKey, SECItem **serialized) +{ + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; +} +SECStatus PK11_HPKE_ExportSecret(const HpkeContext *cx, const SECItem *info, unsigned int L, PK11SymKey **outKey) { PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); return SECFailure; } +HpkeContext * +PK11_HPKE_ImportContext(const SECItem *serialized, PK11SymKey *wrapKey) +{ + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return NULL; +} SECStatus PK11_HPKE_Open(HpkeContext *cx, const SECItem *aad, const SECItem *ct, SECItem **outPt) @@ -94,6 +106,7 @@ PK11_HPKE_SetupR(HpkeContext *cx, const SECKEYPublicKey *pkR, SECKEYPrivateKey * } #else +#define SERIALIZATION_VERSION 1 static const char *DRAFT_LABEL = "HPKE-07"; static const char *EXP_LABEL = "exp"; @@ -129,12 +142,14 @@ static const hpkeKemParams kemParams[] = { { HpkeDhKemX25519Sha256, 32, 32, 32, SEC_OID_CURVE25519, CKM_SHA256 }, }; +#define MAX_WRAPPED_EXP_LEN 72 // Largest kdfParams->Nh + 8 static const hpkeKdfParams kdfParams[] = { /* KDF, Nh, mechanism */ { HpkeKdfHkdfSha256, SHA256_LENGTH, CKM_SHA256 }, { HpkeKdfHkdfSha384, SHA384_LENGTH, CKM_SHA384 }, { HpkeKdfHkdfSha512, SHA512_LENGTH, CKM_SHA512 }, }; +#define MAX_WRAPPED_KEY_LEN 40 // Largest aeadParams->Nk + 8 static const hpkeAeadParams aeadParams[] = { /* AEAD, Nk, Nn, tagLen, mechanism */ { HpkeAeadAes128Gcm, 16, 12, 16, CKM_AES_GCM }, @@ -192,6 +207,20 @@ encodeNumber(PRUint64 value, PRUint8 *b, size_t count) return b + count; } +static PRUint8 * +decodeNumber(PRUint64 *value, PRUint8 *b, size_t count) +{ + unsigned int i; + PRUint64 number = 0; + PORT_Assert(b && value && count <= sizeof(*value)); + + for (i = 0; i < count; i++) { + number = (number << 8) + b[i]; + } + *value = number; + return b + count; +} + SECStatus PK11_HPKE_ValidateParameters(HpkeKemId kemId, HpkeKdfId kdfId, HpkeAeadId aeadId) { @@ -288,6 +317,246 @@ PK11_HPKE_DestroyContext(HpkeContext *cx, PRBool freeit) } } +/* Export Format: + struct { + uint8 serilizationVersion; + uint8 hpkeVersion; + uint16 kemId; + uint16 kdfId; + uint16 aeadId; + uint16 modeId; + uint64 sequenceNumber; + opaque senderPubKey<1..2^16-1>; + opaque baseNonce<1..2^16-1>; + opaque key<1..2^16-1>; + opaque exporterSecret<1..2^16-1>; + } HpkeSerializedContext +*/ +#define EXPORTED_CTX_BASE_LEN 26 /* Fixed size plus 2B for each variable. */ +#define REMAINING_BYTES(walker, buf) \ + buf->len - (walker - buf->data) +SECStatus +PK11_HPKE_ExportContext(const HpkeContext *cx, PK11SymKey *wrapKey, SECItem **serialized) +{ + SECStatus rv; + size_t allocLen; + PRUint8 *walker; + SECItem *keyBytes = NULL; // Maybe wrapped + SECItem *exporterBytes = NULL; // Maybe wrapped + SECItem *serializedCx = NULL; + PRUint8 wrappedKeyBytes[MAX_WRAPPED_KEY_LEN] = { 0 }; + PRUint8 wrappedExpBytes[MAX_WRAPPED_EXP_LEN] = { 0 }; + SECItem wrappedKey = { siBuffer, wrappedKeyBytes, sizeof(wrappedKeyBytes) }; + SECItem wrappedExp = { siBuffer, wrappedExpBytes, sizeof(wrappedExpBytes) }; + + CHECK_FAIL_ERR((!cx || !cx->aeadContext || !serialized), SEC_ERROR_INVALID_ARGS); + CHECK_FAIL_ERR((cx->aeadContext->operation != (CKA_NSS_MESSAGE | CKA_DECRYPT)), + SEC_ERROR_NOT_A_RECIPIENT); + + /* If a wrapping key was provided, do the wrap first + * so that we know what size to allocate. */ + if (wrapKey) { + rv = PK11_WrapSymKey(CKM_AES_KEY_WRAP_KWP, NULL, wrapKey, + cx->key, &wrappedKey); + CHECK_RV(rv); + rv = PK11_WrapSymKey(CKM_AES_KEY_WRAP_KWP, NULL, wrapKey, + cx->exporterSecret, &wrappedExp); + CHECK_RV(rv); + + keyBytes = &wrappedKey; + exporterBytes = &wrappedExp; + } else { + rv = PK11_ExtractKeyValue(cx->key); + CHECK_RV(rv); + keyBytes = PK11_GetKeyData(cx->key); + CHECK_FAIL(!keyBytes); + PORT_Assert(keyBytes->len == cx->aeadParams->Nk); + + rv = PK11_ExtractKeyValue(cx->exporterSecret); + CHECK_RV(rv); + exporterBytes = PK11_GetKeyData(cx->exporterSecret); + CHECK_FAIL(!exporterBytes); + PORT_Assert(exporterBytes->len == cx->kdfParams->Nh); + } + + allocLen = EXPORTED_CTX_BASE_LEN + cx->baseNonce->len + cx->encapPubKey->len; + allocLen += wrapKey ? wrappedKey.len : cx->aeadParams->Nk; + allocLen += wrapKey ? wrappedExp.len : cx->kdfParams->Nh; + + serializedCx = SECITEM_AllocItem(NULL, NULL, allocLen); + CHECK_FAIL(!serializedCx); + + walker = &serializedCx->data[0]; + *(walker)++ = (PRUint8)SERIALIZATION_VERSION; + *(walker)++ = (PRUint8)HPKE_DRAFT_VERSION; + + walker = encodeNumber(cx->kemParams->id, walker, 2); + walker = encodeNumber(cx->kdfParams->id, walker, 2); + walker = encodeNumber(cx->aeadParams->id, walker, 2); + walker = encodeNumber(cx->mode, walker, 2); + walker = encodeNumber(cx->sequenceNumber, walker, 8); + + /* sender public key, serialized. */ + walker = encodeNumber(cx->encapPubKey->len, walker, 2); + PORT_Memcpy(walker, cx->encapPubKey->data, cx->encapPubKey->len); + walker += cx->encapPubKey->len; + + /* base nonce */ + walker = encodeNumber(cx->baseNonce->len, walker, 2); + PORT_Memcpy(walker, cx->baseNonce->data, cx->baseNonce->len); + walker += cx->baseNonce->len; + + /* key. */ + walker = encodeNumber(keyBytes->len, walker, 2); + PORT_Memcpy(walker, keyBytes->data, keyBytes->len); + walker += keyBytes->len; + + /* exporter_secret. */ + walker = encodeNumber(exporterBytes->len, walker, 2); + PORT_Memcpy(walker, exporterBytes->data, exporterBytes->len); + walker += exporterBytes->len; + + CHECK_FAIL_ERR(REMAINING_BYTES(walker, serializedCx) != 0, + SEC_ERROR_LIBRARY_FAILURE); + *serialized = serializedCx; + +CLEANUP: + if (rv != SECSuccess) { + SECITEM_ZfreeItem(serializedCx, PR_TRUE); + } + return rv; +} + +HpkeContext * +PK11_HPKE_ImportContext(const SECItem *serialized, PK11SymKey *wrapKey) +{ + SECStatus rv = SECSuccess; + HpkeContext *cx = NULL; + PRUint8 *walker; + PRUint64 tmpn; + PRUint8 tmp8; + HpkeKemId kem; + HpkeKdfId kdf; + HpkeAeadId aead; + PK11SlotInfo *slot = NULL; + PK11SymKey *tmpKey = NULL; + SECItem tmpItem = { siBuffer, NULL, 0 }; + SECItem emptyItem = { siBuffer, NULL, 0 }; + + CHECK_FAIL_ERR((!serialized || !serialized->data || serialized->len == 0), + SEC_ERROR_INVALID_ARGS); + CHECK_FAIL_ERR((serialized->len < EXPORTED_CTX_BASE_LEN), SEC_ERROR_BAD_DATA); + + walker = serialized->data; + + tmp8 = *(walker++); + CHECK_FAIL_ERR((tmp8 != SERIALIZATION_VERSION), SEC_ERROR_BAD_DATA); + tmp8 = *(walker++); + CHECK_FAIL_ERR((tmp8 != HPKE_DRAFT_VERSION), SEC_ERROR_INVALID_ALGORITHM); + + walker = decodeNumber(&tmpn, walker, 2); + kem = (HpkeKemId)tmpn; + + walker = decodeNumber(&tmpn, walker, 2); + kdf = (HpkeKdfId)tmpn; + + walker = decodeNumber(&tmpn, walker, 2); + aead = (HpkeAeadId)tmpn; + + /* Create context. We'll manually set the mode, though we + * no longer have the PSK and have no need for it. */ + cx = PK11_HPKE_NewContext(kem, kdf, aead, NULL, NULL); + CHECK_FAIL(!cx); + + walker = decodeNumber(&tmpn, walker, 2); + CHECK_FAIL_ERR((tmpn != HpkeModeBase && tmpn != HpkeModePsk), + SEC_ERROR_BAD_DATA); + cx->mode = (HpkeModeId)tmpn; + + walker = decodeNumber(&cx->sequenceNumber, walker, 8); + slot = PK11_GetBestSlot(CKM_HKDF_DERIVE, NULL); + CHECK_FAIL(!slot); + + /* Import sender public key (serialized). */ + walker = decodeNumber(&tmpn, walker, 2); + CHECK_FAIL_ERR(tmpn >= REMAINING_BYTES(walker, serialized), + SEC_ERROR_BAD_DATA); + tmpItem.data = walker; + tmpItem.len = tmpn; + cx->encapPubKey = SECITEM_DupItem(&tmpItem); + CHECK_FAIL(!cx->encapPubKey); + walker += tmpItem.len; + + /* Import base_nonce. */ + walker = decodeNumber(&tmpn, walker, 2); + CHECK_FAIL_ERR(tmpn != cx->aeadParams->Nn, SEC_ERROR_BAD_DATA); + CHECK_FAIL_ERR(tmpn >= REMAINING_BYTES(walker, serialized), + SEC_ERROR_BAD_DATA); + tmpItem.data = walker; + tmpItem.len = tmpn; + cx->baseNonce = SECITEM_DupItem(&tmpItem); + CHECK_FAIL(!cx->baseNonce); + walker += tmpItem.len; + + /* Import key */ + walker = decodeNumber(&tmpn, walker, 2); + CHECK_FAIL_ERR(tmpn >= REMAINING_BYTES(walker, serialized), + SEC_ERROR_BAD_DATA); + tmpItem.data = walker; + tmpItem.len = tmpn; + walker += tmpItem.len; + if (wrapKey) { + cx->key = PK11_UnwrapSymKey(wrapKey, CKM_AES_KEY_WRAP_KWP, + NULL, &tmpItem, cx->aeadParams->mech, + CKA_NSS_MESSAGE | CKA_DECRYPT, 0); + CHECK_FAIL(!cx->key); + } else { + CHECK_FAIL_ERR(tmpn != cx->aeadParams->Nk, SEC_ERROR_BAD_DATA); + tmpKey = PK11_ImportSymKey(slot, cx->aeadParams->mech, + PK11_OriginUnwrap, CKA_NSS_MESSAGE | CKA_DECRYPT, + &tmpItem, NULL); + CHECK_FAIL(!tmpKey); + cx->key = tmpKey; + } + + /* Import exporter_secret. */ + walker = decodeNumber(&tmpn, walker, 2); + CHECK_FAIL_ERR(tmpn != REMAINING_BYTES(walker, serialized), + SEC_ERROR_BAD_DATA); + tmpItem.data = walker; + tmpItem.len = tmpn; + walker += tmpItem.len; + + if (wrapKey) { + cx->exporterSecret = PK11_UnwrapSymKey(wrapKey, CKM_AES_KEY_WRAP_KWP, + NULL, &tmpItem, cx->kdfParams->mech, + CKM_HKDF_DERIVE, 0); + CHECK_FAIL(!cx->exporterSecret); + } else { + CHECK_FAIL_ERR(tmpn != cx->kdfParams->Nh, SEC_ERROR_BAD_DATA); + tmpKey = PK11_ImportSymKey(slot, CKM_HKDF_DERIVE, PK11_OriginUnwrap, + CKA_DERIVE, &tmpItem, NULL); + CHECK_FAIL(!tmpKey); + cx->exporterSecret = tmpKey; + } + + cx->aeadContext = PK11_CreateContextBySymKey(cx->aeadParams->mech, + CKA_NSS_MESSAGE | CKA_DECRYPT, + cx->key, &emptyItem); + +CLEANUP: + if (rv != SECSuccess) { + PK11_FreeSymKey(tmpKey); + PK11_HPKE_DestroyContext(cx, PR_TRUE); + cx = NULL; + } + if (slot) { + PK11_FreeSlot(slot); + } + + return cx; +} + SECStatus PK11_HPKE_Serialize(const SECKEYPublicKey *pk, PRUint8 *buf, unsigned int *len, unsigned int maxLen) { @@ -799,6 +1068,12 @@ pk11_hpke_Decap(HpkeContext *cx, const SECKEYPublicKey *pkR, SECKEYPrivateKey *s CHECK_RV(rv); rv = pk11_hpke_ExtractAndExpand(cx, dh, kemContext, &cx->sharedSecret); CHECK_RV(rv); + + /* Store the sender serialized public key, which + * may be required by application use cases. */ + cx->encapPubKey = SECITEM_DupItem(encS); + CHECK_FAIL(!cx->encapPubKey); + CLEANUP: if (rv != SECSuccess) { PK11_FreeSymKey(cx->sharedSecret); @@ -817,7 +1092,6 @@ PK11_HPKE_GetEncapPubKey(const HpkeContext *cx) if (!cx) { return NULL; } - /* Will be NULL on receiver. */ return cx->encapPubKey; } @@ -1088,4 +1362,5 @@ PK11_HPKE_Open(HpkeContext *cx, const SECItem *aad, } return rv; } + #endif // NSS_ENABLE_DRAFT_HPKE diff --git a/lib/pk11wrap/pk11pub.h b/lib/pk11wrap/pk11pub.h index 7ec6bb90b7..1a074219b0 100644 --- a/lib/pk11wrap/pk11pub.h +++ b/lib/pk11wrap/pk11pub.h @@ -746,9 +746,28 @@ HpkeContext *PK11_HPKE_NewContext(HpkeKemId kemId, HpkeKdfId kdfId, HpkeAeadId a SECStatus PK11_HPKE_Deserialize(const HpkeContext *cx, const PRUint8 *enc, unsigned int encLen, SECKEYPublicKey **outPubKey); 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, PK11SymKey **outKey); 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_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);