Table of Contents

ML-KEM Test Vectors

Source

NIST ACVP (Automated Cryptographic Validation Protocol) validation vector sets for FIPS 203:

The vectors are stored in NIST's own ACVP JSON schema in tests/Security/Cryptography/TestData/mlkem-acvp-fips203.json.gz, and read at run time by MLKemAcvpVectors with System.Text.Json. Nothing is filtered — every ML-KEM group has an implementation to test — so the file holds the upstream keyGen and encapDecap documents verbatim, behind a small envelope recording provenance. Every NUnit case is named after its parameter set and the tcId of the original ACVP vector file, so a failure traces straight back to the NIST source.

It is a data file rather than C# literals because ML-KEM-1024 alone carries ~9.6 KB of hex per key generation case, and gzipped because the JSON is ~2 MB, compressing to ~815 KB. Regenerate it with scripts/fetch-mlkem-acvp-vectors.py, which zeroes the gzip mtime so regenerating unchanged vectors produces a byte-identical file rather than a spurious diff.


Coverage

For each of ML-KEM-512, ML-KEM-768, and ML-KEM-1024:

Operation ACVP group What is verified
Key generation (AFT) keyGen Seed (d, z) → byte-exact ek and dk
Encapsulation (AFT) encapsulation (ek, m) → byte-exact ciphertext c and shared secret k, plus decapsulation of the same vector
Decapsulation (VAL) decapsulation (dk, c) → byte-exact k for valid decapsulation and modified ciphertext cases — the latter validate the implicit-rejection output K̄ = J(z ‖ c) exactly
Encapsulation key check (VAL) encapsulationKeyCheck FIPS 203 §7.2 modulus check accepts valid keys and rejects "noisy linear system values too large" keys
Decapsulation key check (VAL) decapsulationKeyCheck FIPS 203 §7.3 hash check accepts valid keys and rejects "modified H" keys

Cross-Validation (Interop)

In addition to the ACVP known-answer tests, MLKemTests/MLKemInteropTests cross-validate against independent implementations on every target framework:

Peer Tests
BouncyCastle 2.6+ Same-seed key generation produces byte-identical ek and expanded dk (MLKemPrivateKeyParameters.FromSeed); encapsulate ↔ decapsulate round-trips in both directions; implicit-rejection outputs for tampered ciphertexts match byte-for-byte; randomized multi-trial round-trips
.NET 10 System.Security.Cryptography.MLKem (where OS-supported) Same-seed keys match the CNG/OpenSSL implementation byte-for-byte (ImportPrivateSeed); cross-encapsulation in both directions; implicit-rejection outputs match

Sample Vectors

Complete vectors are thousands of hex characters; the samples below show the short values in full and truncate keys/ciphertexts (lengths noted). Full data: tests/Security/Cryptography/TestData/mlkem-acvp-fips203.json.gz or the ACVP repository.

Key Generation (ML-KEM-512, ACVP keyGen tcId 1)

d  = 47B893474672BA92E4B12EE44FB32953AF8E8503B5FB471D1614FB8A021A660A
z  = 1F8CB39E9E30BC458A0DC5408884B1187FB217018DF760FA57317703B844A0A9
ek = 28266A088B3482439BCA01AFB7CA5C6136A979B5159985A9484B36B679A5F7B9... (800 bytes)
dk = 89C31D05611AAAB258F78BC2DE0A80D5914BF80C376A990D33CB97F4F2077CE1... (1632 bytes)

The implementation consumes the seed as the 64-byte concatenation d ‖ z.

Encapsulation (ML-KEM-512, ACVP encapsulation tcId 1)

m = 19C44D35AB9EF31B1360F0BF33CF63D80E405962D698415C5888F0AF385DCFF4
c = A87953F9DC2996A8DD40BE55901417A933C3D36EC09ED8A6B81C684947086C23... (768 bytes)
k = 4B7B1514D1BC9808F80E3BEE7B528E13B753C99D153F7EA116A5887063BFCACF

Implicit Rejection (ML-KEM-512, ACVP decapsulation tcId 77, "modified ciphertext")

k = 3DE98CA3A5795225FBB69C5C80277F9B5AC7D370A567A1FFD130D5C1FED0F588

The expected k for a modified ciphertext is the implicit-rejection output K̄ = J(z ‖ c); matching it byte-exactly proves the rejection path (not just "some different secret").


Regenerating / Extending the Vectors

python scripts/fetch-mlkem-acvp-vectors.py

The script downloads internalProjection.json from the ACVP folders listed above (it contains both prompts and expected results) and writes the gzipped result. Running it against unchanged upstream vectors produces a byte-identical file, so a real diff means NIST published new vectors.

Pass --limit N to cap the cases per group — useful when iterating locally, but the committed file is the complete set. Because the stored schema is NIST's own, MLKemAcvpVectors reads any field the ACVP files carry; surfacing a new one is a change to the loader alone.

Usage

These test vectors are used by the unit tests in tests/Security/Cryptography/Kem/ to verify FIPS 203 conformance of the ML-KEM implementation against known good values from official sources.

License

The test vectors are derived from the public NIST ACVP-Server repository. See the ACVP-Server license (NIST software is public domain) for terms.