Skip to content

Python classes ML_KEM_512, ML_KEM_768, ML_KEM_1024 #13

Closed

Description

@Zikithezikit

The user-facing part of #3.

  • PyO3 functions for keygen, encaps and decaps per parameter set; Rust errors become KyberError (or a subclass), not a bare Exception
  • Classes that work exactly like the Kyber* ones: an instance is a keypair, static encapsulate/decapsulate/from_keys, hex/b64/dict, tuple unpacking
  • Seed is 64 bytes, d then z (FIPS 203 Algorithm 19 order)
  • Kyber and ML-KEM keys are the same size, so it's easy to mix them up. An ML-KEM method should refuse a Kyber keypair object (we can't tell raw bytes apart, so the docs need to say that)
  • Update __all__, regenerate the .pyi stubs

Done when: Python tests cover all three classes, and the ACVP tests from #11 also run through the Python API for at least one vector per set.

Depends on: #10. Plan: WP-5.

Part of #3.

Activity

  1. added this to the 2.0.0 milestone on Oct 29, 2026
  2. Zikithezikit commented on Oct 30, 2026

    @Zikithezikit
    OwnerAuthor

    Handoff for whoever picks this up (the Rust side is finished in #30; read src/mlkem/kem.rs first).

    Rust surface, all pub(crate) and under the dead_code allow in src/lib.rs, which you should drop once this wires them in:

    • kem::keygen::() -> Result<(Vec, Zeroizing<Vec>), KyberError>, kem::keygen_from_seed::(d, z) with d and z as &[u8; 32] (no error, it cannot fail), kem::encaps::(ek) -> Result<(SharedSecret, Vec)> which returns (K, c) in the order of the standard, and kem::decaps::(dk, c) -> Result. Call them as kem::keygen::<{ MlKem768::K }>().
    • The seed for Python is 64 bytes, d then z. Split it and check the length before calling.
    • decaps never errors on a well-formed but wrong ciphertext; it returns the implicit-rejection secret. It does error on a wrong ciphertext length, a wrong dk length, and a dk whose stored H(ek) does not match.
    • Errors are KyberError: InvalidInput { param, expected, actual }, RandomBytesGeneration, and two new variants, InvalidEncapsulationKey (modulus check) and InvalidDecapsulationKey (hash check). Map each to its own KyberError subclass in Python. The legacy functions in src/lib.rs raise a bare PyException; do not copy that for the new ones and do not change the legacy ones.
    • Do not use the acvp feature or kem::acvp from the bindings. Those exist for vector tests only (FIPS 203 section 3.3).

    Things that will bite:

    • ML-KEM and Kyber keys have identical sizes, so a Kyber keypair object passed to an ML-KEM method must be refused by type, not by length.
    • Secrets that cross into Python become bytes objects that cannot be wiped; say so in the docs. The Rust side zeroizes its own copies, but converting a Zeroizing<Vec> with to_vec() makes an unwiped copy before it is handed over, so build the Python bytes straight from the slice.
    • Panics must not cross the boundary; the new Rust code has none on untrusted input, keep it that way in the glue.
    • Run cargo +stable clippy as well as the default one; CI uses the newest stable.

    For the "ACVP through the Python API" item: scripts/fetch_vectors.py already downloads the pinned files into tests/vectors/external/acvp/. The internalProjection decapsulation cases use keys that pass the hash check, so they work through the public decaps. Encapsulation cases fix m, which the public API does not allow, so for those check only that the Python encaps output decapsulates to the same secret, or use a keyGen case through the seed path (seed = d || z gives the exact ek and dk of the keyGen vectors).

  3. Zikithezikit commented on Oct 30, 2026

    @Zikithezikit
    OwnerAuthor

    Done in #31 (rebase-merged into dev-v2 as 3f3aa86, f495537, c406cad and 6e72926). The ML_KEM_512/768/1024 classes, the nine private Rust functions, the KyberError subclasses, the Kyber/ML-KEM refusal by type and the ACVP vectors through the Python API are in. Left open on purpose: ML-KEM from_keys checks sizes only, like the Kyber one (see the comment on #18).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestml-kemML-KEM (FIPS 203) work

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions