Skip to content

deps(rust): bump ml-kem 0.2 → 0.3.0 (with reader.rs API migration)#28

Merged
systemslibrarian merged 5 commits into
mainfrom
chore/ml-kem-0.3-upgrade
Jun 4, 2026
Merged

deps(rust): bump ml-kem 0.2 → 0.3.0 (with reader.rs API migration)#28
systemslibrarian merged 5 commits into
mainfrom
chore/ml-kem-0.3-upgrade

Conversation

@systemslibrarian
Copy link
Copy Markdown
Owner

Summary

  • Bumps `ml-kem` 0.2 → 0.3.0 in `pqf-reader` (closes build(deps): update ml-kem requirement from 0.2 to 0.3 in /impl/rust/pqf-reader #8).
  • Migrates reader.rs to the new 0.3 API: `KemCore` removed, `EncodedSizeUser::from_bytes` → `ExpandedKeyEncoding::from_expanded_bytes`, `Encoded` → `Array<u8, N>`, per-parameter aliases moved into `ml_kem::ml_kem_768`.
  • Updates the Cargo.toml comment to reflect the migration rationale (BouncyCastle interop is preserved bit-for-bit; the deprecated `ExpandedKeyEncoding` trait is still required because BC doesn't expose the seed).

`pqf-writer` stays on ml-kem 0.2 — the two versions coexist in the workspace because the writer/reader exchange wire bytes, not ml-kem types. A future PR can bump the writer.

Interop safety

  • 2400-byte FIPS 203 expanded decapsulation key layout: unchanged
  • 1088-byte ciphertext wire format: unchanged
  • 32-byte shared secret: unchanged

What CI must confirm (in order of importance)

  • Cross-impl conformance (Rust reader vs .NET vectors) — proves the migrated decap path still produces the same shared secret as BouncyCastle's encap
  • 8 Rust-encoded files — .NET reader must accept — orthogonal but should still pass
  • 50 random containers — both readers must agree — proves writer (still 0.2) + reader (now 0.3) interop
  • Reproducible test vectors — proves no drift in deterministic flows
  • WASM binding — ml-kem 0.3 likely pulls a newer getrandom; the existing `getrandom_v04` rename in bindings/wasm/Cargo.toml (added in deps(rust): bump ml-dsa 0.0.4 → 0.1.0 (with verify_with_context for FIPS 204 Algorithm 3) #27) should cover it. If it fails, we'll need to confirm the version range.

API migration to ml-kem 0.3:
  - `KemCore` removed → use `kem::Kem` (we no longer reference it
    directly; the per-parameter type aliases below capture it).
  - `EncodedSizeUser::from_bytes` removed → `ExpandedKeyEncoding::
    from_expanded_bytes` is the replacement, now fallible
    (returns Result<Self, InvalidKey> with FIPS 203 hash validation
    on the expanded key, per RustCrypto/KEMs #207).
  - `Encoded<T>` replaced by `Array<u8, T::EncodedSize>` from the
    re-exported `hybrid_array` (`ml_kem::array`).
  - Per-parameter aliases moved into `ml_kem::ml_kem_768` — use
    `MlKem768Ct` / `MlKem768Dk` (renamed locally for brevity since
    the param is fixed at the import site).

Interop note: the 2400-byte FIPS 203 expanded decapsulation key
layout produced by BouncyCastle's `MLKemPrivateKeyParameters.
GetEncoded()` is preserved bit-for-bit in 0.3, so all existing
test vectors continue to decrypt. The 1088-byte ciphertext wire
format is unchanged.

Notable: `ExpandedKeyEncoding` is marked deprecated in 0.3.0; the
crate authors recommend `DecapsulationKey::from_seed` (64-byte
d||z) instead. We can't switch because BC does not expose the
seed via `GetEncoded()`, so we accept the deprecation warning.
The pqf-reader bump in this branch made cargo unify the transitive
`kem` crate across both ml-kem versions, which broke ml-kem 0.2's
compile (its source was written against the older `kem` trait shape).
Both crates must move together.

Writer-side changes:
  - `Encapsulate` trait moved to crate root and renamed its required
    method to `encapsulate_with_rng(rng)` (the old `encapsulate(rng)`
    was a single method; now the trait splits into a required RNG
    method and a no-arg convenience). Return is now an infallible
    tuple instead of Result.
  - `EncodedSizeUser::from_bytes` is gone; construct `EncapsulationKey`
    via `TryKeyInit::new_from_slice(bytes) -> Result<_, InvalidKey>`.
  - Per-parameter aliases live under `ml_kem::ml_kem_768`.
  - The "deterministic" feature flag was renamed to "hazmat" and the
    `EncapsulateDeterministic` trait promoted to an inherent method
    on `EncapsulationKey` (same name, same shape).
  - `MlKem768Params` is gone; `EncapsulationKey<MlKem768>` (the crate
    root struct parameterized by Kem) is the new form.

The X-Wing draft KAT in tests/xwing_draft_kat.rs replays the published
IETF vectors via deterministic encap; that's the load-bearing test
for FIPS 203 bit-compatibility across this version bump.
In ml-kem 0.2 `DecapsulationKey::decapsulate` returned
`Result<SharedSecret, ()>` even though the `()` error never fired
in practice — FIPS 203 implicit rejection always returns a
pseudorandom secret, not an error. 0.3 made the inherent method
honest and returns `Array<u8, U32>` directly.

Drop the Result match. The constant-time recipient-trial loop
still iterates every recipient regardless of match, and the AEAD
tag below remains the sole signal of a real match.
ml-kem 0.3's `encapsulate_with_rng` requires a `CryptoRng` from the
newer rand_core it re-exports; the writer uses rand 0.8 everywhere
else (x25519-dalek's `random_from_rng`, AEAD nonce filling). Mixing
versions inside one fn would force a deep import chain.

Switch the encap call to `encapsulate_deterministic` (gated by the
"hazmat" feature, already enabled): generate 32 CSPRNG bytes via
rand 0.8 OsRng and pass them in. Cryptographically equivalent to
the RNG-based variant since the bytes come from the same OS CSPRNG.
Same shape as decapsulate — the 0.2 Result return was a vestigial
() error type that never fired. 0.3 honestly returns the
`(Ciphertext, SharedKey)` tuple directly.
@systemslibrarian systemslibrarian merged commit 320bb86 into main Jun 4, 2026
40 checks passed
@systemslibrarian systemslibrarian deleted the chore/ml-kem-0.3-upgrade branch June 4, 2026 09:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

implementation/rust PR touches the Rust impl.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant