Skip to content

Add ML-KEM and ML-DSA support#399

Open
aidangarske wants to merge 55 commits into
wolfSSL:masterfrom
aidangarske:pqc-support
Open

Add ML-KEM and ML-DSA support#399
aidangarske wants to merge 55 commits into
wolfSSL:masterfrom
aidangarske:pqc-support

Conversation

@aidangarske

@aidangarske aidangarske commented May 23, 2026

Copy link
Copy Markdown
Member

ML-KEM (FIPS 203) and ML-DSA (FIPS 204) via wolfSSL backend.

Algorithms: ML-KEM-512/768/1024, ML-DSA-44/65/87 hybrid schemes supported now as well

Opt-in: ./scripts/build-wolfprovider.sh --enable-pqc (adds --enable-mlkem --enable-mldsa to wolfSSL).

  • PQC is not auto detected its only enabled when specifically built.
  • Ability to enable either algo only or both
  • Reject pqc with debian bookworm build could test with trixie eventually
  • ML-DSA CertificateVerify signing and verification both work in TLS
  • wolfProvider now generates ML-DSA certs
  • wolfProvider X509 ML-DSA sigs are valid
  • osp integration with https://github.com/open-quantum-safe/oqs-demos/tree/main/nginx using ML-DSA and hybrid schemes and tested in CI

Validation: three independent paths cross-checked, all pass.

  • Internal unit tests (11 functions x 3 levels = 33 assertions) in make test
  • wolfProvider <-> OpenSSL 3.6+ default provider (12 cross-pairs)
  • wolfProvider <-> wolfSSL direct wc_* API (12 cross-pairs)
  • entire openssl mldsa mlkem test suite exercised in CI for full compatibility
  • Interop tests with hybrid in place as well

CI: new wolfssl-versions-pqc.yml runs three matrix rows - pre-PQC wolfSSL, latest stable, master -- and the three-way interop validator on the PQC-enabled rows.

  • Put floor to 3.6 where mldsa apis solid and 5.9.2 for pqc in general; added note that we could do lower than 3.6 if reuqested but offically support and test those higher versions

supplemental PR for interop test in wolfCrypt: wolfSSL/wolfssl#10603

Test plan

  • make test passes (all 11 PQC tests + existing suite)
  • ./test/pqc_interop.test -- ALL PASS (24 cross-pairs)
  • Build against pre-PQC wolfSSL: PQC code paths skip, make test clean
  • CI green on all three matrix rows

Copilot AI review requested due to automatic review settings May 23, 2026 05:56

This comment was marked as resolved.

@aidangarske aidangarske self-assigned this May 23, 2026
@aidangarske aidangarske marked this pull request as ready for review May 26, 2026 17:13

@Frauschi Frauschi left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Some smaller findings. The biggest "issue" imo is the usage of the now old ML-DSA API instead of the new one. But moving this to the new one should be easy.

Comment thread docs/INTEGRATION_GUIDE.md Outdated
Comment thread docs/INTEGRATION_GUIDE.md Outdated
Comment thread docs/INTEGRATION_GUIDE.md Outdated
Comment thread docs/INTEGRATION_GUIDE.md Outdated
Comment thread docs/INTEGRATION_GUIDE.md Outdated
Comment thread src/wp_mldsa_kmgmt.c Outdated
Comment thread src/wp_mldsa_kmgmt.c Outdated
Comment thread src/wp_mldsa_kmgmt.c Outdated
Comment thread src/wp_mldsa_kmgmt.c
Comment thread src/wp_mlkem_kmgmt.c
@Frauschi

Copy link
Copy Markdown

Jenkins retest this please

@aidangarske aidangarske requested a review from Frauschi May 29, 2026 23:43
@aidangarske

Copy link
Copy Markdown
Member Author

Jenkins retest this please

Frauschi
Frauschi previously approved these changes Jun 1, 2026

@Frauschi Frauschi left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM

Comment thread docs/INTEGRATION_GUIDE.md
Comment thread docs/INTEGRATION_GUIDE.md Outdated
Comment thread include/wolfprovider/settings.h Outdated
Comment thread src/wp_mlkem_kem.c
Comment thread src/wp_mldsa_kmgmt.c
Comment thread src/wp_mldsa_kmgmt.c
Comment thread src/wp_mlkem_kmgmt.c
Comment thread test/standalone/tests/pqc_interop/test_pqc_interop.c
Comment thread src/wp_mldsa_sig.c
* a hostile caller from driving OOM via unbounded digest_sign_update. */
#define WP_MLDSA_BUF_MAX (64UL * 1024UL * 1024UL)

static int wp_mldsa_buf_append(wp_MlDsaSigCtx* ctx, const unsigned char* data,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Use a digest object that is updated with the data.
Should be using wc_MlDsaKey_SignCtxHash() and wc_MlDsaKey_SignCtxHashWithSeed() and wc_MlDsaKey_VerifyCtxHash().
Leave the random generation to the MlDsa if possible - that is use different API when random is test random.
wc_MlDsaKey_SignMuWithSeed() - Mu contains the hash.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think I got the streaming sorted. The pure path doesn't buffer anymore; it runs the message straight through wc_Shake256_Update into mu and signs with SignMuWithSeed / verifies with VerifyMu (your "Mu contains the hash" route). SignCtxHash is in there too, just on the pre-hash path where it belongs.

I couldn't use SignCtxHash for the pure path though it's HashML-DSA and native OpenSSL rejects those sigs when it verifies pure ML-DSA, which is exactly what TLS and X509 do so it blows up interop (confirmed against 3.6.2).

Only thing I couldn't land is leaving the random to MlDsa on the streaming path. SignMuWithSeed forces a seed and there's no rng variant, so for now I generate it on the wolfProvider side (same DRBG, just one layer out). If you're open to adding a wc_MlDsaKey_SignMu(key, sig, sigLen, mu, muLen, rng) the mu version of SignCtx's rng path I could rip the seed gen out and hand it back to MlDsa.

Comment thread src/wp_mldsa_kmgmt.c Outdated
Comment thread src/wp_mlkem_kmgmt.c Outdated
Comment thread src/wp_mlkem_kmgmt.c Outdated
@SparkiDev SparkiDev removed their assignment Jun 16, 2026
@aidangarske aidangarske added ci:nginx-pqc PR OSP toggle: run nginx-pqc and removed ci:nginx-pqc PR OSP toggle: run nginx-pqc labels Jun 16, 2026
…hybrid-group KEM; run all PQC unit tests in CI
…ify per group), validating the ML-DSA TLS signature algorithm end-to-end
…pply FIPS 204 sig params in all init paths, reject wrong-length keygen seed, close hybrid match fail-open
…t, reject wrong-length IKME/test-entropy, scrub hybrid shared secret on failure, fix fill_rnd log flag
…erive ECC public on hybrid private import, scrub ML-KEM shared secret on failure
…dersized buffer (early-return on size check), reject mismatched public on hybrid keypair import
…A public only when actually decoded, give hybrid variant tables internal linkage
….9.2); fix wget TLS by setting LD_LIBRARY_PATH after nginx download
…rray of GIDs, so the loop ran once with a GID); drop install-layout-dependent mime.types include; add startup debug
…per wolfSSL ref), loading wolfProvider via provider.conf in non-replace builds
…re private first since its encode embeds the public
…eed only for test-entropy/deterministic signing
… wc_MlDsaKey_SignCtxHash/VerifyCtxHash (pure path unchanged for md=NULL)
…eject the external-mu + pre-hash combination both ways
@aidangarske aidangarske added the ci:nginx-pqc PR OSP toggle: run nginx-pqc label Jun 23, 2026
…_Shake256_Update) and SignMuWithSeed/VerifyMu, instead of buffering the whole message
… gate lost with SignMuWithSeed) and reject context/mu/encoding changes after streaming starts
…nal-mu input has been buffered, not just after pure streaming starts
@aidangarske aidangarske added ci:nginx-pqc PR OSP toggle: run nginx-pqc and removed ci:nginx-pqc PR OSP toggle: run nginx-pqc labels Jun 23, 2026
@aidangarske

Copy link
Copy Markdown
Member Author

jenkins retest this please

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

Labels

ci:nginx-pqc PR OSP toggle: run nginx-pqc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants