Skip to content

Commit a9cf1ee

Browse files
Merge pull request #10 from danieldia-dev/main
Expand standalone bench suite to mirror comparative.rs and add rust-hpke memory footprint to README
2 parents 2a03e7f + a19c4a6 commit a9cf1ee

3 files changed

Lines changed: 625 additions & 140 deletions

File tree

README.md

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,29 +56,31 @@ The design takes one position on each: **no provider abstraction, no owned RNG,
5656
## Supported ciphersuites
5757

5858
| Component | Variants |
59-
|---|---|
60-
| KEMs | `DhKemX25519HkdfSha256`, `DhKemX448HkdfSha512`, `DhKemP256HkdfSha256`, `DhKemP384HkdfSha384`, `DhKemP521HkdfSha512`, `DhKemK256HkdfSha256` |
59+
|-----------|----------|
60+
| KEMs | `DhKemX25519HkdfSha256`, `DhKemX448HkdfSha512`, `DhKemP256HkdfSha256`, `DhKemP384HkdfSha384`, `DhKemP521HkdfSha512`, `DhKemK256HkdfSha256` |
6161
| KEMs (post-quantum, `pq` feature) | `XWingDraft06`, `MlKem768`, `MlKem1024` |
62-
| KDFs | `HkdfSha256`, `HkdfSha384`, `HkdfSha512` |
63-
| AEADs | `Aes128Gcm`, `Aes256Gcm`, `ChaCha20Poly1305`, `ExportOnly` |
64-
| Modes | Base, Psk, Auth, AuthPsk |
62+
| KDFs | `HkdfSha256`, `HkdfSha384`, `HkdfSha512` |
63+
| AEADs | `Aes128Gcm`, `Aes256Gcm`, `ChaCha20Poly1305`, `ExportOnly` |
64+
| Modes | Base, Psk, Auth, AuthPsk |
6565

6666
## Performance
6767

68-
Across **132 head-to-head benchmarks** against the two major Rust HPKE libraries — `hpke-rs` and `rust-hpke` — `hpke-ng` lands **100 wins, 12 ties, 20 losses**. Broken out: vs `hpke-rs` (76 cells) it's **63W / 8T / 5L**; vs `rust-hpke` (56 cells; no standalone ML-KEM-768/ML-KEM-1024, no secp256k1 support) it's **37W / 4T / 15L**. The largest deltas are on the post-quantum decap path — ML-KEM-768 and ML-KEM-1024 land 53–55% faster, X-Wing decap 37% faster — because `hpke-ng` caches the expanded FIPS 203 decapsulation key in the `PrivateKey` while `hpke-rs` rebuilds it from the seed on every `setup_receiver`. The same idea applied to classical KEMs — caching the recipient's serialized public key alongside the secret — eliminates a redundant base-point scalar multiplication on every decap, lifting **X25519 decap to −41% vs `hpke-rs` / −31% vs `rust-hpke`**, ML-KEM encap to 38–40%, X-Wing encap to 16% vs `hpke-rs` and to roughly parity vs `rust-hpke`. X-Wing `setup_receiver` is also roughly equivalent to `rust-hpke`'s (which wraps raw decap in full HPKE setup). Single-shot open is **26–42% faster vs `hpke-rs`** and **22–35% faster vs `rust-hpke`** across payload sizes; AES-128-GCM single-shot seal 6–22% faster against both across payloads ≤ 16 KiB. **Export is 72–76% faster vs `hpke-rs`** across all five output lengths — the largest sustained delta in the suite (`rust-hpke` excluded: its `export()` API allocates in a way that would bias the comparison). Post-setup `Context::seal` at 64 B is 14% faster vs `hpke-rs` and tied with `rust-hpke`. **End-to-end roundtrip on a 1 KiB message is 30% faster vs both libraries.**
68+
Across **132 head-to-head benchmarks** against the two major Rust HPKE libraries — `hpke-rs` and `rust-hpke` — `hpke-ng` lands **100 wins, 12 ties, 20 losses**. Broken out: vs `hpke-rs` (76 cells) it's **63W / 8T / 5L**; vs `rust-hpke` (61 cells; no standalone ML-KEM-768/ML-KEM-1024, no secp256k1 support) it's **37W / 4T / 15L**. The largest deltas are on the post-quantum decap path — ML-KEM-768 and ML-KEM-1024 land 53–55% faster, X-Wing decap 37% faster — because `hpke-ng` caches the expanded FIPS 203 decapsulation key in the `PrivateKey` while `hpke-rs` rebuilds it from the seed on every `setup_receiver`. The same idea applied to classical KEMs — caching the recipient's serialized public key alongside the secret — eliminates a redundant base-point scalar multiplication on every decap, lifting **X25519 decap to −41% vs `hpke-rs` / −31% vs `rust-hpke`**, ML-KEM encap to 38–40%, X-Wing encap to 16% vs `hpke-rs` and to roughly parity vs `rust-hpke`. X-Wing `setup_receiver` is also roughly equivalent to `rust-hpke`'s (which wraps raw decap in full HPKE setup). Single-shot open is **26–42% faster vs `hpke-rs`** and **22–35% faster vs `rust-hpke`** across payload sizes; AES-128-GCM single-shot seal 6–22% faster against both across payloads ≤ 16 KiB. **Export is 72–76% faster vs `hpke-rs`** across all five output lengths — the largest sustained delta in the suite. Post-setup `Context::seal` at 64 B is 14% faster vs `hpke-rs` and tied with `rust-hpke`. **End-to-end roundtrip on a 1 KiB message is 30% faster vs both libraries.**
6969

7070
Memory and binary footprint:
7171

72-
| Quantity | hpke-rs | hpke-ng |
73-
|------------------------------------------------|-----------|-----------------------------|
74-
| `Hpke<K, F, A>` struct | 320 bytes | **0 bytes** (`PhantomData`) |
75-
| `Context<_, _, ChaCha20Poly1305>` struct | 400 bytes | **88 bytes** |
76-
| `Context<_, _, ExportOnly>` struct | n/a | **56 bytes** |
77-
| `Context<_, _, Aes128Gcm>` struct | 400 bytes | 792 bytes |
78-
| `Context<_, _, Aes256Gcm>` struct | 400 bytes | 1,048 bytes |
79-
| Minimal release binary | 561 KB | **392 KB** (~30% smaller) |
72+
| Quantity | hpke-rs | hpke-ng | rust-hpke |
73+
|--------------------------------------------|-----------|----------------------------------------|---------------|
74+
| `Hpke<K, F, A>` struct | 320 bytes | **0 bytes** (`PhantomData`) | n/a |
75+
| `Context<_, _, ChaCha20Poly1305>` struct | 400 bytes | **88 bytes** | 96 bytes |
76+
| `Context<_, _, ExportOnly>` struct | n/a | **56 bytes** | 184 bytes |
77+
| `Context<_, _, Aes128Gcm>` struct | 400 bytes | 792 bytes | 912 bytes |
78+
| `Context<_, _, Aes256Gcm>` struct | 400 bytes | 1,048 bytes | 1,168 bytes |
79+
| Minimal release binary | 561 KB | **392 KB** (~30% smaller than hpke-rs) | 430 KB |
8080

81-
The AES-GCM `Context` rows are larger than `hpke-rs` because the cipher's expanded round keys + GHash table are cached inline — that is what eliminates the per-call AES key-schedule cost in `Context::seal`. Streaming applications using AES-GCM trade memory for throughput here; ChaCha20-Poly1305 is unaffected.
81+
The `rust-hpke` equivalents are `AeadCtxS<A, Kdf, Kem>` / `AeadCtxR<A, Kdf, Kem>` (sender and receiver contexts respectively, measured here as `AeadCtxS` with `X25519HkdfSha256` + `HkdfSha256`). The context size grows by `Nh` bytes when switching to a larger KDF (e.g. +32 bytes for `HkdfSha512`). Note that there is no configuration struct equivalent to `Hpke<K, F, A>` in `rust-hpke` — it uses free functions (`setup_sender`, `setup_receiver`) rather than a typed handle, so that row is set to n/a. The `ExportOnly` row maps to `ExportOnlyAead` in `rust-hpke`'s type system; its 184-byte size is larger than `hpke-ng`'s 56 bytes because `rust-hpke`'s `AeadCtx` always reserves space for the full nonce buffer regardless of the AEAD variant.
82+
83+
The AES-GCM `Context` rows are larger for `hpke-ng` than `hpke-rs` because the cipher's expanded round keys + GHash table are cached inline — that is what eliminates the per-call AES key-schedule cost in `Context::seal`. Streaming applications using AES-GCM trade memory for throughput here; `ChaCha20-Poly1305` is unaffected.
8284

8385
Build with `RUSTFLAGS="-C target-cpu=native"` for AES-NI / SHA-NI where available. The `[profile.bench]` in `Cargo.toml` enables `lto = "thin"` and `codegen-units = 1`. For head-to-head numbers, run `cargo bench --features comparative --bench comparative` locally; the comparative bench loads both `hpke-rs` (with its `experimental` feature so the post-quantum KEM stubs are wired up) and `rust-hpke` (pinned to a v0.14 pre-release commit for X-Wing support) as dev-dependencies, and produces side-by-side criterion results for every supported ciphersuite. KEM-op rows for `hpke-rs` and `rust-hpke` carry an `_via_setup_*` suffix because neither library exposes raw `encap` / `decap` separable from setup; the suffix marks rows that are explicitly *not* apples-to-apples with `hpke-ng`'s bare-operation rows.
8486

@@ -96,7 +98,7 @@ The post-DH all-zeros check is constant-time. `Context` cannot be `Clone`d, so t
9698
This crate composes RustCrypto primitives. Constant-time properties are inherited from those crates:
9799

98100
| Primitive | CT property |
99-
|---|---|
101+
|-----------|-------------|
100102
| X25519, X448 | CT by construction. |
101103
| P-256, P-384, P-521, secp256k1 | CT in `arithmetic` mode (pinned). |
102104
| HKDF-SHA-{256,384,512} | CT (deterministic; no secret-dependent branches). |

0 commit comments

Comments
 (0)