HPKE-PQ (experimental)
Post-quantum HPKE variants under explicit opt-in. The IETF HPKE-WG has two active drafts proposing ML-KEM as a KEM for RFC 9180:
- **b.crypto.hpke.pq.connolly** — draft-connolly-cfrg-hpke-mlkem-04 (individual draft; carries codepoint allocations today). - **b.crypto.hpke.pq.wg** — draft-ietf-hpke-pq-03 (WG-adopted; the more authoritative track but codepoints may still move before IANA registration).
The framework ships BOTH behind opt-in namespaces rather than picking a single draft prematurely. Each wrapper binds a draft- distinguishing label into the RFC 9180 §5.1 info parameter so an envelope sealed under one draft CANNOT be opened by the other — the cross-draft substitution attack the IANA codepoint normally prevents is enforced by the info-label binding.
Both wrappers compose the existing b.crypto.hpke.seal / .open path (ML-KEM-1024 KEM + HKDF-SHA3-512 KDF + ChaCha20-Poly1305 AEAD per framework PQC-first policy). Operators wanting to migrate to the final IANA-registered codepoints (when they appear) call the stable b.crypto.hpke.seal directly — the experimental wrappers exist for operators integrating against systems that speak one of the active drafts today.
b.crypto.hpke.pq.connolly.seal(opts) #
{
recipientPubKey: string, // ML-KEM-1024 PEM
plaintext: Buffer|string,
info: Buffer|string, // application context
aad: Buffer|string, // additional authenticated data
}
Seal a payload under draft-connolly-cfrg-hpke-mlkem-04 codepoints. Returns { enc, ciphertext }; the framework's existing b.crypto.hpke.seal semantics apply (ML-KEM-1024 + HKDF-SHA3-512 + ChaCha20-Poly1305 per project policy). Opens ONLY via b.crypto.hpke.pq.connolly.open — cross-draft substitution into b.crypto.hpke.pq.wg.open refuses by construction.
var pair = b.crypto.hpke.generateKeyPair();
var sealed = b.crypto.hpke.pq.connolly.seal({
recipientPubKey: pair.publicKey,
plaintext: "hello",
info: "app/topic",
});
b.crypto.hpke.pq.connolly.open(opts) #
{
privateKey: string, // ML-KEM-1024 PEM
enc: Buffer,
ciphertext: Buffer,
info: Buffer|string,
aad: Buffer|string,
}
Open a draft-connolly-cfrg-hpke-mlkem-04 envelope produced by connolly.seal. Refuses envelopes sealed under wg.seal (the info-label binding catches cross-draft substitution).
var pt = b.crypto.hpke.pq.connolly.open({
privateKey: pair.privateKey, enc: sealed.enc,
ciphertext: sealed.ciphertext, info: "app/topic",
});
b.crypto.hpke.pq.wg.seal(opts) #
{
recipientPubKey: string, // ML-KEM-1024 PEM
plaintext: Buffer|string,
info: Buffer|string,
aad: Buffer|string,
}
Seal under draft-ietf-hpke-pq-03 codepoints (the WG-adopted PQ-HPKE draft). Otherwise identical contract to b.crypto.hpke.pq.connolly.seal.
var sealed = b.crypto.hpke.pq.wg.seal({
recipientPubKey: pair.publicKey,
plaintext: "hello",
});
b.crypto.hpke.pq.wg.open(opts) #
{
privateKey: string,
enc: Buffer,
ciphertext: Buffer,
info: Buffer|string,
aad: Buffer|string,
}
Open a draft-ietf-hpke-pq-03 envelope produced by wg.seal.
var pt = b.crypto.hpke.pq.wg.open({
privateKey: pair.privateKey, enc: sealed.enc,
ciphertext: sealed.ciphertext,
});
Last updated 2026-08-08T16:39:49.652Z by seeder.