Skip to content

[cryptotest] Add ML-KEM tests using Wycheproof and ACVP test vectors #219

Merged
mkannwischer merged 2 commits intomasterfrom
mjk/mlkem-testvectors
Apr 8, 2026
Merged

[cryptotest] Add ML-KEM tests using Wycheproof and ACVP test vectors #219
mkannwischer merged 2 commits intomasterfrom
mjk/mlkem-testvectors

Conversation

@mkannwischer
Copy link
Copy Markdown
Contributor

Adds ML-KEM to the cryptotest framework with support for all three
parameter sets (ML-KEM-512, ML-KEM-768, ML-KEM-1024) and four
test operations:

  • keygen: deterministic key generation from seed (d||z)
  • encaps: deterministic encapsulation with provided ek and
    randomness m
  • decaps: decapsulation with provided dk and ciphertext
  • keygen_decaps: keygen followed by decapsulation (Wycheproof
    mlkem_*_test.json provides seed + ciphertext, firmware generates
    keys then decapsulates)

Test vector sources:

  • Wycheproof (C2SP): keygen_seed_test, encaps_test,
    semi_expanded_decaps_test, and the combined test.json
    for all three parameter sets (1695 vectors)
  • ACVP (NIST ACVP-Server v1.1.0.41): keyGen and encapDecap
    internalProjection files (240 vectors)

All 1935 test vectors from both sources are covered.

ACVP encapsulationKeyCheck and decapsulationKeyCheck tests are
mapped to encaps and decaps operations respectively: the cryptolib
does not expose dedicated key validation functions, but
encapsulate_derand and decapsulate perform internal checks (FIPS 203
modulus check, hash consistency) that reject invalid keys. We test
this by calling the operations with the provided (possibly invalid)
key and dummy randomness/ciphertext, and verifying success/failure.

The biggest challenge here is that ML-KEM data structures are
large (ek up to 1568B, dk up to 3168B).
Naively serializing these as ujson output arrays results in a test
runtime of >2.5 hours - the vast majority of which appears to be
spent on JSON encoding in the firmware.
To address this, we instead compute the SHA3-256 hash of the
outputs in the firmware and the parses.
This brings down test time for everything to ~265s.

@mkannwischer
Copy link
Copy Markdown
Contributor Author

I have reworked this PR a bit making it easier to also accommodate ML-DSA in #223.
What I wasn't aware of before is that if I statically allocate a buffer in mlkem.c this will be allocated in all cryptotests. This PR is now changed so this actually gets allocated in firmware.c and passed to the ML-KEM handlers as an argument. This way, if multiple tests need large buffers, we can use a union (as I have done in the ML-DSA test).

@mkannwischer mkannwischer force-pushed the mjk/mlkem-testvectors branch 3 times, most recently from 7f6f49d to 7019f31 Compare April 2, 2026 01:35
@mkannwischer mkannwischer marked this pull request as ready for review April 2, 2026 01:35
Copy link
Copy Markdown
Contributor

@jadephilipoom jadephilipoom left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks Matthias!

@jadephilipoom
Copy link
Copy Markdown
Contributor

CI failure looks unrelated and perhaps like the FPGA may have gotten disconnected, I'd suggest just rerunning it to see if it works.

Adds the NIST ACVP-Server v1.1.0.41 as an external dependency
for ML-KEM (and future algorithm) test vectors.

Signed-off-by: Matthias J. Kannwischer <matthias@zerorisc.com>
Adds ML-KEM to the cryptotest framework with support for all three
parameter sets (ML-KEM-512, ML-KEM-768, ML-KEM-1024) and four
test operations:

  - keygen: deterministic key generation from seed (d||z)
  - encaps: deterministic encapsulation with provided ek and
    randomness m
  - decaps: decapsulation with provided dk and ciphertext
  - keygen_decaps: keygen followed by decapsulation (Wycheproof
    mlkem_*_test.json provides seed + ciphertext, firmware generates
    keys then decapsulates)

Test vector sources:
  - Wycheproof (C2SP): keygen_seed_test, encaps_test,
    semi_expanded_decaps_test, and the combined test.json
    for all three parameter sets (1695 vectors)
  - ACVP (NIST ACVP-Server v1.1.0.41): keyGen and encapDecap
    internalProjection files (240 vectors)

All 1935 test vectors from both sources are covered.

ACVP encapsulationKeyCheck and decapsulationKeyCheck tests are
mapped to encaps and decaps operations respectively: the cryptolib
does not expose dedicated key validation functions, but
encapsulate_derand and decapsulate perform internal checks (FIPS 203
modulus check, hash consistency) that reject invalid keys. We test
this by calling the operations with the provided (possibly invalid)
key and dummy randomness/ciphertext, and verifying success/failure.

The biggest challenge here is that ML-KEM data structures are
large (ek up to 1568B, dk up to 3168B).
Naively serializing these as ujson output arrays results in a test
runtime of >2.5 hours - the vast majority of which appears to be
spent on JSON encoding in the firmware.
To address this, we instead compute the SHA3-256 hash of the
outputs in the firmware and the parses.
This brings down test time for everything to ~265s.

Note that the ML-KEM tests need work buffers that are too large
for the stack. This commit allocates them in firmware.c
already in preparation for also adding ML-DSA tests, so that
these buffers can be placed in a union.

Signed-off-by: Matthias J. Kannwischer <matthias@zerorisc.com>
@mkannwischer mkannwischer force-pushed the mjk/mlkem-testvectors branch from 7019f31 to 70d21d5 Compare April 6, 2026 00:50
@mkannwischer
Copy link
Copy Markdown
Contributor Author

CI failure looks unrelated and perhaps like the FPGA may have gotten disconnected, I'd suggest just rerunning it to see if it works.

I have done that twice now, but it does not seem to help.
Do you know what I am doing wrong?

@jadephilipoom
Copy link
Copy Markdown
Contributor

Looks like a broader CI issue unfortunately :/

@mkannwischer mkannwischer merged commit 0114e48 into master Apr 8, 2026
44 of 55 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants