Skip to content

Add non-blocking (chunked) ML-DSA and ML-KEM for bare-metal schedulers - #12052

Open
dgarske wants to merge 1 commit into
wolfSSL:masterfrom
dgarske:pq_nonblock
Open

dgarske wants to merge 1 commit into
wolfSSL:masterfrom
dgarske:pq_nonblock

Conversation

@dgarske

@dgarske dgarske commented Nov 6, 2026

Copy link
Copy Markdown
Member

Adds a resumable, non-blocking mode for ML-DSA (keygen, sign, verify) and ML-KEM (keygen, encapsulate, decapsulate), the post-quantum analogue of WC_ECC_NONBLOCK, for bare-metal targets with a cooperative scheduler that must regain the CPU during a long PQC operation.

What it adds

  • WC_MLDSA_NONBLOCK / WC_MLKEM_NONBLOCK build options, each requiring the matching *_SMALL_MEM paths, which already process one row or polynomial at a time.
  • wc_MlDsaNonBlock / wc_MlKemNonBlock caller-owned contexts and wc_MlDsaKey_set_nonblock() / wc_MlKemKey_set_nonblock(). With a context attached, each public operation runs one slice and returns MP_WOULDBLOCK until it completes; the caller re-invokes it in a loop, as for ECC.
  • wolfcrypt/src/wc_mldsa.c - resumable keygen, sign and verify on the small-memory paths; sign yields per matrix column inside each rejection attempt and again between attempts, so it always hands control back even when the first attempt succeeds.
  • wolfcrypt/src/wc_mlkem.c, wc_mlkem_poly.c - the small-memory keygen and encapsulate cores from Reduce ML-KEM small memory heap use and allow caching matrix A with it #11565 are split into a one-time prelude plus per-row helpers (mlkem_keygen_seeds_row(), mlkem_encapsulate_seeds_row() and a tail for v); the blocking entry points keep calling the unchanged wrappers. Decapsulate drives the re-encryption through the same per-row core, carrying the block-wise ciphertext compare result across yields.
  • wolfcrypt/test/test.c - mldsa_nonblock_test and mlkem_nonblock_test: every operation yields, results match the blocking path, tampered inputs are rejected, an abandoned operation is released and restarted.
  • .github/configs/pq-all.json - pq-nonblock-smallmem and pq-nonblock-smallmem-dynkeys builds so make check exercises both, the second with WOLFSSL_MLKEM_CACHE_A and WOLFSSL_MLKEM_DYNAMIC_KEYS.

Testing

testwolfcrypt green in the combined non-blocking build, with WOLFSSL_MLKEM_CACHE_A, with CACHE_A + DYNAMIC_KEYS, with WC_MLDSA_FAULT_HARDEN + WC_MLKEM_FAULT_HARDEN, under ASan/UBSan, in the small-memory build without the options, and in the default build. Cross-built for Thumb-2 (--enable-armasm, armv7m) and run under qemu-arm with the non-blocking options, with and without CACHE_A. ML-DSA-44 certificate authentication in a TLS 1.3 loopback handshake verified against the non-blocking build (TLS uses the blocking path).

Also fixes #12040: the Doxygen and source comments for wc_MlDsaKey_Size(), wc_MlDsaKey_PrivSize() and wc_MlDsaKey_GetPrivLen() now say what each returns (private key alone versus private key with the public key appended), PrivSize() gets the else its sibling level chains have, and test_mldsa.c asserts PrivSize() == Size() + PubSize().

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Mismatching docs for ML-DSA size functions

1 participant