Repository navigation
ml-dsa: reduce stack usage and add low-memory signing - #1457
Draft
tob-scott-a wants to merge 1 commit into
Draft
tob-scott-a wants to merge 1 commit into
tob-scott-a wants to merge 1 commit into
Conversation
tob-scott-a
force-pushed
the
feat/ml-dsa-reduce-stack
branch
from
October 8, 2026 21:35
d41a2dd to
d4035e9
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ML-DSA constructs large matrix and secret-vector temporaries during key generation and signing. This reduces construction/cloning stack usage with
allocand adds an opt-in, allocation-free signing path for embedded applications.alloc, matrix rows and cached key components are allocated individually to avoid constructing or cloning a full expanded key on the stack. The default configuration retains its caches.low-memoryfeature retains the public matrix seed and three raw secret vectors, regenerates matrix entries and secret NTTs one polynomial at a time, and uses in-place transforms. Key generation and public-key derivation also process matrix products one row at a time.SigningWorkspace<P>provides reusable inline scratch, with const constructors for all three parameter sets. Deterministic, randomized, and precomputed-mu signing methods accept caller-owned scratch. Existing signing methods construct scratch locally whenlow-memoryis selected. Withdefault-features = false, signing needs no allocator; withallocenabled, the returned signature still allocates its components.zeroize, workspace intermediates are erased after successful signing and on drop. Encodings and signature outputs are unchanged. The library continues to forbid unsafe code.Stack and RAM measurements
Cortex-M33 results below compare upstream
a840817with this branch usinglow-memory, no allocation, andzeroize. Both use the same standalone probe, Rust 1.95.0,thumbv8m.main-none-eabi, optimization level 3, LTO, one codegen unit, and QEMU 11.0.3'smps2-an505model.The workspace resides in static SRAM separately from the call stack. Counting it too, signing scratch uses 30,776 / 38,552 / 49,264 bytes instead of 70,388 / 103,868 / 132,092 bytes. Retained signature sizes are unchanged at 5,152 / 6,704 / 9,280 bytes.
These are written-stack watermarks for a fixed zero seed and message, including startup/caller frames and excluding static objects. They are smoke measurements, not worst-case firmware budgets. Interrupt frames, key generation, output storage, and the rest of the application require separate RAM budgeting. The emulated board uses 2 MiB of SRAM to accommodate the baseline.
A separate reused-key ML-DSA-65 host benchmark measured approximately 518 µs cached versus 877 µs with the workspace (roughly 1.7× slower), using fixed inputs, no allocation,
zeroize, and 40 Criterion samples on ARM64 macOS. Regenerating the matrix and secret NTTs trades CPU time for RAM; host timings do not establish real-device cycle costs.Reproduction instructions are in the Cortex-M33 probe README. The host
stack_usageexample and a 256 KiB allocation-backed lifecycle regression test are also included. CI adds all three Cortex-M33 parameter sets with a 28 KiB signing-stack limit and expands the no_std matrix to Cortex-M33.Timing/cache review and validation
The regenerated matrix depends only on public
rhoand row/column indices. Dense secret products, NTTs, and norm scans use fixed public bounds and addresses. Every norm scan visits all coefficients before the existing signing rejection decisions. No coefficient-dependent early exits, sparse secret products, new division, or secret-indexed lookups were added.Source review and disassembly checks found no new timing/cache hazards. Cortex-M33 code for all parameter sets at optimization level 3, and ML-DSA-65 at level 2, contained no division instructions or division helper calls. ARM64 and x86-64 code was also scanned for division and floating-point square-root instructions. Physical power/EM leakage and fault resistance have not been tested on hardware.
Local validation passed:
low-memory,zeroize, including ACVP, Wycheproof, serialization, and property tests.