fix(deps): migrate to ml-kem 0.3 (supersedes #133) #147

Merged
vxfemboy merged 2 commits from fix/deps-ml-kem-0.3 into main 2026-07-29 04:07:33 +00:00
Owner

Migrates the workspace to ml-kem 0.3 (post-quantum KEM in the REALITY hybrid handshake). Supersedes Renovate #133close #133.

ml-kem 0.3 removed KemCore/EncodedSizeUser (now the external kem::Kem trait + Key/KeyExport/TryKeyInit), renamed generategenerate_keypair_from_rng, as_bytesto_bytes, and made encapsulate/decapsulate infallible. Also bumps rand_core to 0.10 in yip-utls's two RNG bridges (ml-kem 0.3's requirement); x25519-dalek keeps its own transitive rand_core 0.6, no split conflict.

Wire-compat: byte-identical (empirically proven)

Rather than trusting the changelog, the migration built two probe binaries pinned to ml-kem =0.2.3 and =0.3.2, drove both through an identical deterministic RNG, and compared:

  • encapsulation key (1184 B), ciphertext (1088 B), shared secret (32 B) — byte-identical;
  • cross-decapsulation: 0.2's ciphertext into a 0.3 key and vice-versa both derive the same shared secret — proves real interop, not just parallel self-consistency.

The hybrid key-share shape tests and the JA4-lock test (crafted_hello_matches_chrome150_ja4) pass, so the ClientHello fingerprint is unaffected.

One behavior change (hardening, not a regression)

ml-kem 0.3 validates the client's encapsulation key on decode; a malformed/non-canonical ek is now rejected (Error::Protocol("client ML-KEM ek failed validation")) instead of silently accepted. This is stricter — matches real ML-KEM-strict servers — and is the only semantic delta. Flagged for your awareness.

cargo test --workspace all green; fmt + clippy clean.

Migrates the workspace to **ml-kem 0.3** (post-quantum KEM in the REALITY hybrid handshake). Supersedes Renovate #133 — **close #133**. ml-kem 0.3 removed `KemCore`/`EncodedSizeUser` (now the external `kem::Kem` trait + `Key`/`KeyExport`/`TryKeyInit`), renamed `generate`→`generate_keypair_from_rng`, `as_bytes`→`to_bytes`, and made encapsulate/decapsulate infallible. Also bumps `rand_core` to 0.10 in yip-utls's two RNG bridges (ml-kem 0.3's requirement); x25519-dalek keeps its own transitive rand_core 0.6, no split conflict. ## Wire-compat: byte-identical (empirically proven) Rather than trusting the changelog, the migration built two probe binaries pinned to ml-kem `=0.2.3` and `=0.3.2`, drove both through an identical deterministic RNG, and compared: - encapsulation key (1184 B), ciphertext (1088 B), shared secret (32 B) — **byte-identical**; - **cross-decapsulation**: 0.2's ciphertext into a 0.3 key and vice-versa both derive the same shared secret — proves real interop, not just parallel self-consistency. The hybrid key-share shape tests and the JA4-lock test (`crafted_hello_matches_chrome150_ja4`) pass, so the ClientHello fingerprint is unaffected. ## One behavior change (hardening, not a regression) ml-kem 0.3 validates the client's encapsulation key on decode; a malformed/non-canonical ek is now rejected (`Error::Protocol("client ML-KEM ek failed validation")`) instead of silently accepted. This is stricter — matches real ML-KEM-strict servers — and is the only semantic delta. Flagged for your awareness. `cargo test --workspace` all green; fmt + clippy clean.
fix(deps): migrate to ml-kem 0.3
Some checks failed
CI / shear (pull_request) Successful in 8m35s
CI / deny (pull_request) Successful in 16m51s
CI / build-test (pull_request) Successful in 28m15s
Integration (privileged) / bench-smoke (pull_request) Successful in 3m38s
Coverage / coverage (pull_request) Failing after 35m15s
Integration (privileged) / device-tests (pull_request) Successful in 51s
Integration (privileged) / netem-comparison (pull_request) Failing after 8m53s
Integration (privileged) / netns-tunnel-test (pull_request) Failing after 10m6s
Integration (privileged) / dpi-undetectability (pull_request) Failing after 12m8s
08b9c71790
Bumps yip-utls's ml-kem 0.2 -> 0.3 (supersedes Renovate PR #133) and the
rand_core bridge it needs (0.6 -> 0.10, ml-kem 0.3's new requirement).

API changes in ml-kem 0.3 (server.rs, stream.rs):
- `KemCore`/`EncodedSizeUser` removed; replaced by `kem::Kem` (re-exported
  as `ml_kem::Kem`) plus `Key<T>`/`KeyExport`/`TryKeyInit`.
- `MlKem768::generate(rng)` -> `MlKem768::generate_keypair_from_rng(rng)`.
- `EncapsulationKey::from_bytes` (infallible) -> `EncapsulationKey::new`
  (now validates the key, returning `Result<_, InvalidKey>` - stricter,
  matches what real ML-KEM-strict servers already do).
- `ek.as_bytes()` -> `ek.to_bytes()` (via `KeyExport`).
- `Encapsulate::encapsulate` (rng, fallible) -> `encapsulate_with_rng`
  (infallible - encapsulation cannot fail).
- `Decapsulate::decapsulate` is now infallible (drops the vestigial
  `Result<_, ()>` that was never actually `Err` in 0.2 either).
- The `RandomSourceRng`/`MlKemRng` bridges now implement rand_core 0.10's
  `TryRng`/`TryCryptoRng` (`Error = Infallible`) instead of 0.6's
  `RngCore`/`CryptoRng`; `Rng`/`CryptoRng` are blanket-derived from that.

Verified semantic equivalence (the crux requirement, since this is a
hybrid X25519+ML-KEM handshake locked to a JA4 fingerprint): built two
standalone probe binaries pinned to ml-kem 0.2.3 and 0.3.2 respectively,
drove both through the identical deterministic counting-byte RNG stream,
and confirmed byte-for-byte identical ek/ciphertext/shared-secret output,
plus successful CROSS-decapsulation (0.2's ciphertext into 0.3's
decapsulation key and vice versa, both landing on the same shared
secret). The on-wire ML-KEM encoding is unchanged.

cargo test --workspace (YIP_SKIP_PRIVILEGED_TESTS=1): all green, including
yip-utls's server_key_share_4588_shapes/emit_server_hello_roundtrips_4588
(hybrid handshake round-trip) and ja_diff's
crafted_hello_matches_chrome150_ja4_and_permutes_ja3 (JA4 lock).
cargo fmt --all --check and cargo clippy --workspace --all-targets -- -D
warnings are both clean.
Merge remote-tracking branch 'origin/main' into fix/deps-ml-kem-0.3
Some checks failed
CI / shear (pull_request) Successful in 9m52s
CI / deny (pull_request) Successful in 18m11s
CI / build-test (pull_request) Successful in 42m23s
Coverage / coverage (pull_request) Failing after 59m56s
Integration (privileged) / bench-smoke (pull_request) Successful in 1m55s
Integration (privileged) / device-tests (pull_request) Successful in 58s
Integration (privileged) / netem-comparison (pull_request) Failing after 7m44s
Integration (privileged) / netns-tunnel-test (pull_request) Failing after 8m47s
Integration (privileged) / dpi-undetectability (pull_request) Failing after 8m37s
8acb5d6caf
# Conflicts:
#	Cargo.lock
Sign in to join this conversation.
No description provided.