HPKE Library Survey for HERALD Reference Implementations
Task: R-202 — Survey HPKE library availability across TS/Python/Go/Rust
Worker: worker-opus-2026-05-05-R202-par
Date prepared: 2026-05-05
Last verified: 2026-05-05 (web sources are point-in-time; re-check before commit)
1. HERALD context — what we need from an HPKE library
HERALD's whisper attestation [EXT §7.3] is the only HPKE-binding primitive in the protocol substrate. The relevant normative requirements:
"Encryption MUST use HPKE per RFC 9180 with a registered KEM/KDF/AEAD suite. The default suite ishpke-x25519-hkdf-sha256-aes128gcm. The HPKE Additional Authenticated Data (AAD) MUST equal the envelopeidof the envelope that carries thewhisper. The AAD binding is what prevents cut-and-paste of an encrypted blob into a different envelope." — EXT.md line 532
"Recipients MUST verify the AAD on decrypt and reject ciphertexts whose AAD does not equal the carrying envelope's id." — EXT.md line 534The threat model is explicit about why this matters:
"An encrypted blob with no authenticated binding to the envelope it sits inside can be cut-and-pasted by a malicious conduit into a different envelope. Defence. Specify HPKE (RFC 9180) with envelope id (or canonical body hash) as AAD." — THREATS.md §4.12 (lines 270–274)From these, the minimum capability bar for any candidate library is:
| Capability | Required? | Why |
|---|---|---|
| RFC 9180 conformance, base mode | MUST | EXT §7.3 default suite is RFC-9180-registered |
DHKEM(X25519, HKDF-SHA256) KEM | MUST | Default suite KEM |
HKDF-SHA256 KDF | MUST | Default suite KDF |
AES-128-GCM AEAD | MUST | Default suite AEAD |
ChaCha20Poly1305 AEAD | SHOULD | Common alternative; fallback for hw without AES-NI |
AAD passed per-Seal/Open call | MUST | EXT §7.3 binds AAD = envelope id. Libraries that fix AAD = "" or only expose info are insufficient |
| Sender/Recipient context API | SHOULD | Allows future extension to multi-message whispers; default whisper is single-shot |
| Stable release ≥ 1.0 or ≥ 0.x with adoption | SHOULD | HERALD reference impl should not bind itself to alpha-quality crypto |
| Maintained (commit < 12 months old) | SHOULD | Crypto rot is real |
A library that exposes only the RFC-9180 single-shot Seal(pkR, info, "", pt) (i.e. AAD hard-coded to empty) fails the EXT §7.3 requirement and is unsuitable. The info parameter is not AAD — it is mixed into the HPKE key schedule (RFC 9180 §5.1) and does not protect each ciphertext under a unique tag the way AAD does.
2. TypeScript / JavaScript
2.1 @hpke/core (dajiaji/hpke-js) — primary candidate
- Repository: https://github.com/dajiaji/hpke-js
- Package:
@hpke/coreon npm + JSR (modular packages:@hpke/dhkem-x25519,@hpke/dhkem-x448,@hpke/chacha20poly1305,@hpke/ml-kem,@hpke/hybridkem-x-wing) - License: MIT
- Latest version:
@hpke/core1.9.0;@hpke/common1.10.1 (March 12, 2026) — last verified 2026-05-05 - GitHub stars: 116
- Last commit: 2026-04-15 (active)
- Runtimes: Node.js 16+, Deno 2.x, Cloudflare Workers, Bun 0.6+, browsers (Web Crypto), Electron
- RFC 9180 compliance: "supporting all of the ciphersuites defined in RFC9180"
- Modes: Base, PSK, Auth, AuthPSK
- Ciphersuites:
- KEMs: DHKEM(P-256/P-384/P-521, X25519/X448), ML-KEM-512/768/1024, X-Wing, secp256k1
- KDFs: HKDF-SHA256/384/512
- AEADs: AES-128-GCM, AES-256-GCM, ChaCha20Poly1305, Export-Only
- AAD exposure: YES — confirmed in source (
packages/core/src/senderContext.ts):
``typescript``
override async seal(
data: ArrayBuffer,
aad: ArrayBuffer = EMPTY.buffer as ArrayBuffer,
): Promise<ArrayBuffer>
- Audit status: "not been formally audited." Passes RFC 9180 test vectors and Project Wycheproof tests.
- Known security history: CVE-2025-64767 / GHSA-73g8-5h73-26h4 (critical, CVSS 9.1, published 2025-11-19). AEAD nonce reuse via race condition in concurrent
Seal()calls. Affected ≤1.7.4, fixed in 1.7.5+. HERALD reference impl MUST pin@hpke/core≥ 1.7.5 and avoid concurrentsealon a single context (or use single-shot pattern, which is what HERALD whisper requires anyway).
2.2 panva/hpke — alternative
- Repository: https://github.com/panva/hpke (Filip Skokan, prolific author of
jose) - License: MIT
- Latest version: 1.1.0 (March 31, 2026)
- GitHub stars: 30
- Last commit: 2026-05-01 (active)
- Runtimes: Node.js, Deno, Bun, Browsers, Cloudflare Workers, Electron — Web Crypto only, zero dependencies
- Ciphersuites: DHKEM(P-256/384/521, X25519/X448), ML-KEM, hybrid; HKDF-SHA256/384/512, SHAKE128/256, TurboSHAKE; AES-128/256-GCM, ChaCha20Poly1305
- AAD exposure: Per single-shot
Seal()/Open()API documented in README; AAD parameter exposure on streaming context not verified from README (would need source-code check). Filip Skokan's other libraries (jose) follow IETF idioms closely so AAD support is highly likely, but unverified at submission time. - Audit status: Not stated.
- Notable: Pure Web Crypto API; smallest dependency surface.
2.3 TypeScript recommendation
For T-114 (HPKE-bound whisper attestation) and the existing T-003 TS reference library:
Use@hpke/core1.7.5 or newer, with explicit subpackage imports (@hpke/dhkem-x25519for the default suite). Do not use the umbrellahpke-jspackage (it pulls all suites; tree-shaking is the point of@hpke/core).
Rationale: maintained, modular, audited-by-test-vectors, AAD on seal/open confirmed, post-CVE patched, runs everywhere HERALD needs to run. Track panva/hpke as a contender if dependency footprint or Web-Crypto-purity becomes load-bearing later.
3. Python
3.1 cryptography (pyca) — gold standard, but insufficient for HERALD whisper
- Repository: https://github.com/pyca/cryptography
- Module:
cryptography.hazmat.primitives.hpke(added in v47.0.0, released 2026-04-24) - License: Apache-2.0 / BSD
- RFC 9180 compliance: Yes
- Ciphersuites: X25519, P-256/384/521, ML-KEM-768/1024, hybrids; HKDF-SHA256/384/512, SHAKE-128/256; AES-128/256-GCM, ChaCha20Poly1305
- API: Single-shot only. Documented surface:
``python``
Suite.encrypt(plaintext, public_key, info=b"")
Suite.decrypt(ciphertext, private_key, info=b"")
- AAD exposure: NO direct AAD parameter. The
infoparameter is the HPKE application context info string (RFC 9180 §5.1), which is mixed into the key schedule — it is not the per-message AEAD AAD that EXT §7.3 demands. RFC 9180 §6.1 single-shotSealBasecalls AEAD withaad=""internally; pyca'sSuite.encryptdoes not surface that argument. - Implication: pyca/cryptography 47.0.0's HPKE module cannot satisfy EXT §7.3 as written. It can satisfy the requirement only if HERALD reinterprets the binding as "envelope id stuffed into
info," which is not equivalent (info binds the key schedule, not the ciphertext under the AEAD tag — different security goal, different invariants). - Status: Watch list — file an upstream issue requesting AAD-bearing single-shot or a stateful context API. For now this library is not the right Python choice for HERALD whisper.
3.2 pyhpke (dajiaji) — primary candidate
- Repository: https://github.com/dajiaji/pyhpke
- PyPI:
pyhpke0.6.4 (December 21, 2025) - License: MIT
- GitHub stars: 13
- Last commit: 2026-05-04 (very active)
- RFC 9180 compliance: "supports all of the HPKE modes and cipher suites defined in RFC9180"
- Ciphersuites: DHKEM(P-256/384/521, X25519/X448); HKDF-SHA256/384/512; AES-128/256-GCM, ChaCha20Poly1305, Export-Only; modes: Base/PSK/Auth/AuthPSK
- AAD exposure: YES — confirmed from API docs:
``python``
seal(pt: bytes, aad: bytes = b'') -> bytes
open(ct: bytes, aad: bytes = b'') -> bytes
(sender/recipient context methods, both stateful and supporting per-message AAD)
- Audit status: "has not been formally audited" — passes all RFC 9180 official test vectors.
- Underlying crypto: built on
cryptography(pyca) for primitives.
3.3 hpke-py (ctz / Joseph Birr-Pixton) — alternative
- Repository: https://github.com/ctz/hpke-py
- PyPI:
hpke0.3.2 (October 26, 2024) - License: Apache-2.0
- GitHub stars: 8
- Last commit: 2024-10-26 — stale (>12 months at survey time; flag this)
- RFC 9180 compliance: Full
- Modes: Base/PSK/Auth/AuthPSK
- Ciphersuites: DHKEM(P-256/384/521, X25519/X448); HKDF-SHA256/384/512; AES-128/256-GCM, ChaCha20Poly1305, Export-Only
- Author: Joseph Birr-Pixton — also author of
rustls, high-credibility crypto engineer. - Concern: Maintenance gap. Author quality is high but the package has not seen a release in ~18 months at survey time.
3.4 Python recommendation
For T-004 (Python reference library) and any future Python HERALD whisper code:
Usepyhpke0.6.4+ as primary. It is the only well-maintained Python HPKE library that exposes per-message AAD onseal/openper EXT §7.3.
Secondary: ride alongside cryptography for everything else (Ed25519 sigs, hashing, key serialization), and file an upstream pyca issue requesting AAD-bearing HPKE so we can migrate to the standard library long-term.
hpke-py is a credible fallback if pyhpke becomes unmaintained, but its 18-month release gap is itself a concern.
4. Go
4.1 Go 1.26 standard library: crypto/hpke — strongly preferred
- Source: Go standard library, available since Go 1.26 (released February 10, 2026).
- License: Go BSD-style.
- Proposal: https://github.com/golang/go/issues/75300 (status: Proposal-Accepted, landed 1.26)
- RFC 9180 compliance: Full (graduated from
crypto/internal/hpkewhich had been used bycrypto/tlsEncrypted Client Hello) - Modes: Base mode only at 1.26 (auth and PSK explicitly deferred per the proposal; "the API can theoretically extend to auth modes later").
- Ciphersuites:
- KEMs: DHKEM(P-256/384/521, X25519), ML-KEM-768/1024, MLKEM768-X25519 (X-Wing), MLKEM768-P256, MLKEM1024-P384
- KDFs: HKDF-SHA256/384/512, SHAKE128/256
- AEADs: AES-128-GCM, AES-256-GCM, ChaCha20Poly1305, ExportOnly
- AAD exposure: YES on the streaming API, partial on single-shot:
```go
// Single-shot (no per-message AAD parameter; uses empty AAD internally)
func Seal(pk PublicKey, kdf KDF, aead AEAD, info, plaintext []byte) ([]byte, error)
func Open(k PrivateKey, kdf KDF, aead AEAD, info, ciphertext []byte) ([]byte, error)
// Streaming (AAD on each Seal/Open)
func NewSender(pk PublicKey, kdf KDF, aead AEAD, info []byte) (enc []byte, s Sender, err error)
func (s Sender) Seal(aad, plaintext []byte) ([]byte, error)
func (r *Recipient) Open(aad, ciphertext []byte) ([]byte, error)
```
- HERALD usage pattern: Use the
Sender/RecipientAPI even for single-message whispers — pass envelopeidasaad, send a single ciphertext, discard context. This is the only path that satisfies EXT §7.3 with stdlib. - Audit status: Carries the Go security team's review process and Go 1.26's release-stamp testing; the underlying internal package was already in production for
crypto/tlsECH before being graduated. - Strategic value: Zero third-party dependency; binds HERALD Go reference to the platform's own crypto. This is the right long-term choice.
4.2 cloudflare/circl/hpke — pre-stdlib incumbent
- Repository: https://github.com/cloudflare/circl (sub-package
hpke) - License: BSD-3-Clause
- Latest version: v1.6.3 (January 22, 2026)
- GitHub stars (whole library): ~1,655
- Last commit: 2026-05-04 (very active)
- RFC 9180 compliance: Implements draft-irtf-cfrg-hpke-07 (predecessor of RFC 9180; the package documentation explicitly says "draft-07," not "RFC 9180" — verify equivalence on a per-suite basis before shipping)
- Modes: Base/Auth/PSK/AuthPSK
- Known limitation: Does not implement Export-Only AEAD codepoint 0xFFFF.
- Ciphersuites: P-256/384/521, X25519/X448, X25519-Kyber768-draft00, X-Wing; HKDF-SHA256/384/512; AES-128-GCM, AES-256-GCM, ChaCha20Poly1305
- AAD exposure: YES — both
Sealer.Seal(pt, aad)andOpener.Open(ct, aad)takeaad []byte. - API: Streaming with auto-incrementing nonce; supports
MarshalBinary/UnmarshalBinaryfor context persistence. - Audit status: Cloudflare runs CIRCL in production at scale; not formally third-party audited but widely-deployed crypto code from a trusted maintainer.
- Trade-off vs. stdlib: Strictly more features (auth modes, CIRCL ecosystem), but adds a heavy third-party dep and predates RFC 9180.
4.3 Other Go HPKE libraries (briefly)
FiloSottile/hpke— Filippo Valsorda's experimental package; appears to be the precursor / tracker forcrypto/hpkestdlib work. License BSD-3-Clause, 19 stars, active 2025-12. Use stdlib instead.jedisct1/go-hpke-compact— single-suite (X25519/HKDF-SHA256/{AES-GCM,ChaCha20Poly1305}), 20 stars, last commit 2024-12. Useful pedagogical reference; not a production choice.cisco/go-hpke— last commit 2023-05-11, predates RFC 9180 finalization. Stale, do not use.danielhavir/go-hpke— last commit 2019-11. Abandoned.
4.4 Go recommendation
For future T-103 (Go reference library, projected) and any conduit/Herald written in Go:
Usecrypto/hpkefrom Go 1.26+ standard library. Pin Go MSRV to 1.26.2 or later. Use theSender/Recipientstreaming API (not the single-shotSeal/Open) so envelopeidcan be passed as AAD per EXT §7.3.
cloudflare/circl/hpke is acceptable for code that needs to support Go versions older than 1.26 (e.g. interop testing or transient backports), but new code should not introduce CIRCL purely for HPKE.
5. Rust
5.1 hpke (rozbb/rust-hpke) — primary candidate
- Repository: https://github.com/rozbb/rust-hpke
- Crate:
hpkeon crates.io - License: Apache-2.0 / MIT (dual-licensed)
- Latest stable: 0.13.0 (February 19, 2025); pre-release 0.14.0-pre.2 (February 25, 2026)
- Total downloads: ~5.4M (significant adoption signal)
- GitHub stars: 86
- Last commit: 2026-04-24 (active)
- MSRV: 1.85.0 on the 0.14.0 line
- RFC 9180 compliance: "complies with the HPKE standard (RFC 9180)"
- Modes: Base/PSK/Auth/AuthPSK
- Ciphersuites:
- KEMs: DHKEM(Curve25519, Curve448, P-256/384/521); X-Wing (MLKEM768-X25519)
- KDFs: HKDF-SHA256/384/512, SHAKE256
- AEADs: AES-GCM-128/256, ChaCha20Poly1305
- AAD exposure: YES:
``rust``
AeadCtxS::seal(msg, aad)
AeadCtxR::open(&ciphertext, aad)
// also non-allocating in-place variants:
AeadCtxS::seal_in_place_detached(&mut msg, aad)
AeadCtxR::open_in_place_detached(&mut ciphertext, aad, &auth_tag)
- Audit status: Not formally audited. Cloudflare did an internal review of v0.8 ("we found no security issues" re: composition, memory zeroing, RNG). Useful prior, not equivalent to a public audit.
- Notable:
no_stdsupport, feature-flagged primitives, mature 5-year history.
5.2 hpke-rs (cryspen, formerly franziskuskiefer) — alternative
- Repository: https://github.com/cryspen/hpke-rs
- Crate:
hpke-rson crates.io - License: MPL-2.0 (note: file-level copyleft; check compatibility before vendoring into HERALD code that might be Apache-2.0 / MIT)
- Latest version: 0.6.1 (March 20, 2026)
- Total downloads: ~957K
- GitHub stars: 49
- Last commit: 2026-05-04 (very active)
- RFC 9180 compliance: Yes; Base/PSK/Auth/AuthPSK
- Ciphersuites: DHKEM(X25519, P-256/384/521, K256, X448), X-Wing draft-06; HKDF-SHA256/384/512; AES-GCM-128/256, ChaCha20Poly1305, Export-Only
- AAD exposure: YES — the high-level
sealtakesaad: &[u8]directly:
``rust``
fn seal(&mut self, pk_r: &HpkePublicKey, info: &[u8], aad: &[u8],
plaintext: &[u8], psk: Option<&[u8]>, psk_id: Option<&[u8]>,
sk_s: Option<&HpkePrivateKey>) -> Result<(Vec<u8>, Vec<u8>), HpkeError>
- Pluggable backends:
libcrux(formally verified, narrow suite coverage) orRustCrypto(broad coverage). HERALD would preferlibcruxfor the default X25519/HKDF-SHA256/AES-128-GCM suite if formal verification is achievable. - Maintainer: Franziskus Kiefer (Cryspen) — credible crypto engineer, formal-methods adjacent.
- Audit status: Not stated as formally audited; libcrux backend has formal-verification provenance.
5.3 Other Rust HPKE crates (briefly)
hpkewg/hpke-pq— HPKE WG's post-quantum draft tracker. 11 stars, March 2026. Not for production.capeprivacy/hybrid-pke— wrapper aroundhpke, seemingly abandoned (April 2024 last push).hpke_dispatch— Divvy-Up's runtime-dispatch wrapper aroundhpke; useful for protocol-agility.
5.4 Rust recommendation
For any future Rust HERALD reference impl:
Use hpke (rozbb/rust-hpke) ≥ 0.13 as the primary choice for a HERALD-Rust reference implementation. Re-evaluate at 0.14 stable release.Use hpke-rs (cryspen) if formal verification of the cryptographic core (via the libcrux backend) is a hard requirement — but factor in the MPL-2.0 license and the more verbose API.Both libraries pass our minimum AAD-on-seal/open bar. rozbb/hpke has more downloads and dual MIT/Apache licensing; cryspen/hpke-rs has a more rigorous backend story.
6. Side-by-side comparison
| Library | Lang | License | Latest | Stars | Last activity | RFC 9180 | AAD on seal/open | Default-suite support | Audit | Recommended? |
|---|---|---|---|---|---|---|---|---|---|---|
@hpke/core (dajiaji) | TS | MIT | 1.9.0 (2026-04) | 116 | 2026-04 | Full | Yes | Yes | Test-vectors only; CVE-2025-64767 fixed in 1.7.5 | Yes for T-003/T-114; pin ≥1.7.5 |
panva/hpke | TS | MIT | 1.1.0 (2026-03) | 30 | 2026-05 | Yes | Likely (unverified) | Yes | None stated | Watch list |
pyhpke (dajiaji) | Python | MIT | 0.6.4 (2025-12) | 13 | 2026-05 | Full | Yes | Yes | Test-vectors only | Yes for T-004 |
cryptography (pyca) | Python | Apache-2.0/BSD | 47.0.0 (2026-04) | n/a | 2026-04 | Yes | NO (single-shot, no AAD param) | Yes | Industry-standard | No for whisper; use for Ed25519 etc. |
hpke-py (ctz) | Python | Apache-2.0 | 0.3.2 (2024-10) | 8 | 2024-10 | Full | Likely (unverified) | Yes | None | Fallback only; stale |
crypto/hpke Go stdlib | Go | BSD | Go 1.26 (2026-02) | n/a | n/a | Full base mode | Yes (streaming API) | Yes | Go security process | Yes for future T-103 |
cloudflare/circl/hpke | Go | BSD-3 | v1.6.3 (2026-01) | 1,655 | 2026-05 | draft-07 (≈RFC 9180) | Yes | Yes | Cloudflare-deployed | Acceptable for legacy Go |
hpke (rozbb) | Rust | Apache-2.0/MIT | 0.13.0 (2025-02), 0.14.0-pre.2 | 86 | 2026-04 | Full | Yes | Yes | Cloudflare internal review (v0.8) | Yes for future Rust impl |
hpke-rs (cryspen) | Rust | MPL-2.0 | 0.6.1 (2026-03) | 49 | 2026-05 | Full | Yes | Yes | libcrux backend = formally verified | Yes if formal-verif required |
7. Per-task recommendations
| HERALD task | Language | Library | Pin |
|---|---|---|---|
| T-003 (TS reference lib core) | TS | @hpke/core + @hpke/dhkem-x25519 | ≥ 1.7.5 (post CVE-2025-64767) |
| T-004 (Python reference lib core) | Python | pyhpke | ≥ 0.6.4 |
| T-114 (HPKE-bound whisper, TS impl) | TS | @hpke/core (single-shot pattern with AAD = envelope id) | ≥ 1.7.5 |
| T-103 (future Go reference lib) | Go | crypto/hpke stdlib | Go ≥ 1.26.2 |
| Future Rust reference | Rust | hpke (rozbb) | 0.13.x track now; migrate to 0.14 stable when released |
8. Gotchas and pitfalls (canonical list)
These are footguns every HERALD implementer using HPKE must understand.
8.1 info is not AAD
Most HPKE libraries expose two distinct context-binding strings: the info parameter (mixed into key-schedule per RFC 9180 §5.1) and the aad parameter (passed to AEAD per §5.2). They serve different purposes:
infobinds the key derivation to an application context. Sameinfobetween sender and recipient is required for any decryption to succeed at all.aadis per-AEAD-operation associated data, authenticated under the AEAD tag. Wrong AAD →InvalidTagon decrypt for that specific message.
EXT §7.3 says AAD = envelope id. Putting envelope id in info is not equivalent: it would prevent decryption if info were wrong, but does not provide per-message tagging that distinguishes between "this ciphertext was minted for envelope X" and "this ciphertext was minted for envelope Y." Verifiers would have to derive a separate context per envelope, multiplying setup cost, and the EXT §7.3 requirement is specifically the AAD wire-binding — pyca's info-only API will be flagged non-conformant by any reviewer who reads RFC 9180.
Action: When picking a Python library, refuse anything that doesn't surface AAD per call. Same for any other language.
8.2 Single-shot APIs that bury AAD
Several libraries (pyca/cryptography, Go stdlib's Seal/Open top-level functions) offer a single-shot API that does not expose the AEAD AAD — it's hard-coded to empty. Do not use these for HERALD whisper. Always use the streaming/context API even for single messages, so envelope id can be passed.
8.3 Concurrent seal on a single context
CVE-2025-64767 in @hpke/core ≤1.7.4 was a race: concurrent seal() calls on a single sender context could reuse nonces. Two protections:
1. Pin to fixed version (≥1.7.5).
2. For HERALD whisper, never call seal more than once on a single context — whisper is one-shot per envelope. The default usage pattern is safe.
A library-agnostic rule: do not share an HPKE sender or recipient context across asynchronous tasks unless the library documents internal locking.
8.4 Export interface as a side-channel
HPKE's export interface (Sender.Export/Recipient.Export) lets callers derive arbitrary keys from the same sender/recipient setup. Several libraries surface Export-Only as a "no-AEAD" mode. HERALD whisper does not use export. If a future extension does, it MUST domain-separate via the exporter context string and MUST NOT reuse keys across exports and seals — the library does not enforce this for you.
8.5 Ciphersuite negotiation downgrade
EXT §7.3 lets whisper.kem carry the suite identifier. A naïve verifier that trusts the field could be forced to a weaker suite. Mitigation:
- Verifier MUST validate that the suite is registered (RFC 9180 IANA registry + any HERALD-specific allowlist for default-suite-only deployments).
- Sigil birth attestations SHOULD constrain the suite the recipient is willing to receive, mirroring how TLS reins in cipher choice.
This is a HERALD-protocol-level concern, not a library bug, but the library's API ergonomics affect how easy it is to lock down.
8.6 Recipient public key freshness
EXT §7.3 says the recipient X25519 public key is "typically published in the recipient's birth attestation or in a Herald-issued endpoint cert." The library doesn't validate freshness. HERALD verifiers MUST reject whispers encrypted to revoked or molted keys (a separate concern from HPKE itself but a system-level requirement).
8.7 Pre-RFC drafts vs. RFC 9180
cisco/go-hpke and earlier cloudflare/circl/hpke versions implement drafts of HPKE that predate RFC 9180. The wire formats can differ in subtle ways (KEM/KDF/AEAD codepoint values, label strings). HERALD MUST require RFC 9180 conformance specifically; implementers MUST verify against RFC 9180 §A test vectors.
8.8 ML-KEM and post-quantum suites
All four reference libraries now ship ML-KEM and X-Wing variants. EXT §7.3's default suite is purely classical (X25519/AES-128-GCM). HERALD v0.1 MUST stick to the classical default; PQ migration is a separate task. Library API surfaces that aggressively prefer PQ suites (e.g., the hpke-js umbrella) should be configured explicitly to the X25519 suite.
8.9 License compatibility
Most libraries are MIT or Apache-2.0 / MIT dual-licensed. cryspen/hpke-rs is MPL-2.0, which is file-level copyleft. If HERALD reference impls vendor or statically link hpke-rs, ensure the consuming code's license either matches or declares the MPL files unchanged (MPL §3.3). Most projects can comply trivially; flag for reviewers.
8.10 Audit status across the board
No HPKE library surveyed has a published third-party formal audit at this date (verify on re-check). The Cloudflare review of rust-hpke v0.8 is a credible internal-review prior, not a public audit. RFC 9180's small surface and the libraries' habit of building on already-audited primitives (X25519, HKDF, AES-GCM) is a comfort, but HERALD should be honest in its threat model: the HPKE binding is "best-available unaudited RFC-9180 implementation," not "audited cryptographic core."
9. References
Specs
- RFC 9180 — Hybrid Public Key Encryption: https://www.rfc-editor.org/rfc/rfc9180.html
- HERALD EXT.md §7.3 (whisper) —
EXT.mdlines 516–536 (this repository) - HERALD THREATS.md §4.12 (whisper without AEAD/AAD binding) —
THREATS.mdlines 270–276 (this repository)
TypeScript
@hpke/core: https://github.com/dajiaji/hpke-js (last verified 2026-05-05)- CVE-2025-64767 advisory: https://github.com/dajiaji/hpke-js/security/advisories/GHSA-73g8-5h73-26h4
panva/hpke: https://github.com/panva/hpke
Python
pyhpke: https://github.com/dajiaji/pyhpke (API docs: https://pyhpke.readthedocs.io/en/stable/api.html)- pyca
cryptographyHPKE: https://cryptography.io/en/latest/hazmat/primitives/hpke/ (added v47.0.0) hpke-py: https://github.com/ctz/hpke-py
Go
crypto/hpkestdlib (Go 1.26): https://pkg.go.dev/crypto/hpke- Stdlib proposal: https://github.com/golang/go/issues/75300
cloudflare/circl/hpke: https://pkg.go.dev/github.com/cloudflare/circl/hpke- Go 1.26 release notes: https://go.dev/blog/go1.26
Rust
hpke(rozbb): https://github.com/rozbb/rust-hpke (https://crates.io/crates/hpke)hpke-rs(cryspen): https://github.com/cryspen/hpke-rs (https://crates.io/crates/hpke-rs)
Test vectors
- RFC 9180 Appendix A — official test vectors (used by hpke-js, pyhpke, rust-hpke, hpke-rs)
- Project Wycheproof (additional fault-resistance tests; used by
@hpke/core)
End of survey.