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) #

experimental0.10.10
{
  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) #

experimental0.10.10
{
  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) #

experimental0.10.10
{
  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) #

experimental0.10.10
{
  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.