Add signing hooks for observing signature attempts and implementing restartable signing#1237
Open
hanno-becker wants to merge 9 commits into
Open
Add signing hooks for observing signature attempts and implementing restartable signing#1237hanno-becker wants to merge 9 commits into
hanno-becker wants to merge 9 commits into
Conversation
Contributor
CBMC Results (ML-DSA-44, REDUCE-RAM)Full Results (209 proofs)
|
Contributor
CBMC Results (ML-DSA-65)Full Results (209 proofs)
|
Contributor
CBMC Results (ML-DSA-44)Full Results (209 proofs)
|
Contributor
CBMC Results (ML-DSA-87)
Full Results (209 proofs)
|
ba5df63 to
004fcc0
Compare
Contributor
CBMC Results (ML-DSA-65, REDUCE-RAM)Full Results (209 proofs)
|
Contributor
CBMC Results (ML-DSA-87, REDUCE-RAM)Full Results (209 proofs)
|
b2ef39d to
4acce0a
Compare
ec79a2c to
9686b66
Compare
ebcd246 to
815f90d
Compare
8772ce2 to
915038e
Compare
Contributor
Author
|
@mkannwischer Thank you for the review! I changed the distribution test so that a) by default, 2000 iterations are run and a wide error margin allowed, b) a dedicated CI job exercises a larger number of iterations and accordingly checks for a narrower error. |
cc67609 to
a8420d3
Compare
Move the kappa upper bound MLD_MAX_KAPPA -- the largest counter kappa keeping the y-sampling nonces within uint16_t -- from sign.c to params.h, beside MLDSA_L which it derives from. Replace the open-coded `UINT16_MAX - MLDSA_L` in the polyvec contracts and comments with it. No functional change. Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
Functions carrying the optional MLD_CONFIG_CONTEXT_PARAMETER that do not otherwise use it must consume it to avoid -Wunused-parameter, but only when a context is actually configured. This was previously done via an #if-guarded ((void)context). Introduce MLD_CONTEXT_UNUSED(context) in context.h, expanding to ((void)(context)) when a context parameter is configured and to an empty statement otherwise, and use it in the default (stack) MLD_FREE and in the skip-PCT stub. No functional change. Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
Redefine MLD_MAX_SIGNING_ATTEMPTS as the effective bound on signing attempts: the configured MLD_CONFIG_MAX_SIGNING_ATTEMPTS if set, otherwise the hard type-safety bound MLD_MAX_KAPPA / MLDSA_L. mld_get_max_signing_attempts() returns it directly, and its contract bounds the result by MLD_MAX_KAPPA / MLDSA_L (the hard bound), independent of the configured value. This keeps the type-safety bound (used by the loop and casts) distinct from the configured attempt limit, and prepares for the signing hooks, which clamp the resume point against the effective bound. No functional change. Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
The runtime of ML-DSA signatures is theoretically unbounded. Even the
bound of max 814 signature attempts -- approved by NIST as lowering the
chance of signing failure below 2^{-256} -- leaves a performance
distribution whose peak percentiles can be unacceptable for constrained
systems with strict timing requirements.
The library already provides MLD_CONFIG_MAX_SIGNING_ATTEMPTS to cap the
number of signing attempts. However, FIPS 204 forbids lowering that bound
below 814, rendering the option non-compliant in constrained environments.
As a remedy, this commit introduces three optional 'signing hooks' around
the core rejection-sampling loop, keyed by the attempt counter (the loop
iteration; the spec counter is kappa = attempt*MLDSA_L):
- `attempt = sign_resume()`: Return the attempt to resume the operation
from. `0` for an initial / non-restartable signing operation.
- `sign_attempt(attempt)`: Called before each attempt. Returns 0 to
proceed, or non-zero to pause: signing then returns MLD_ERR_SIGNING_PAUSED
with `attempt` as the resume point.
- `sign_finish(attempt)`: Called on success with the succeeding attempt.
Together, resume + attempt make signing _restartable_: a caller can pause
after a bounded number of attempts and later resume from the recorded
attempt, bounding per-call runtime while keeping a FIPS-compliant overall
bound and reproducing the same signature as an uninterrupted run.
This only works if the consumer drives the deterministic internal API: the
randomized API would choose a fresh seed on each (re-)start, so restarting
could not reproduce the uninterrupted signature. Enabling any of the three
hooks therefore requires MLD_CONFIG_NO_RANDOMIZED_API, which in turn is
incompatible with MLD_CONFIG_KEYGEN_PCT (whose one-shot internal signature
cannot be resumed). A logging-only use -- the attempt hook always returns 0,
with resume/finish merely observing, e.g. to benchmark the per-signature
attempt distribution -- would be safe with the randomized API too, but the
restriction is imposed uniformly on all three hooks for now.
Pausing is independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, the upper bound on
the total number of attempts (>= 814 for FIPS 204 compliance) after which
signing gives up with the distinct error MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED; it
must not be lowered as a restart mechanism.
sign_{resume|attempt|finish} can be implemented on mutable globals or on a
context, so they are introduced under the existing MLD_CONFIG_CONTEXT_PARAMETER
wrapper, which lets functions be called with or without an
application-specific context pointer.
This commit adds the core machinery in mldsa/:
- Independent MLD_CONFIG_SIGN_HOOK_{RESUME,ATTEMPT,FINISH} options. When set,
the integration provides, respectively:
- `attempt = mld_sign_hook_resume([ctx])`: provide initial/resume attempt
- `mld_sign_hook_attempt(attempt [, ctx])`: per-attempt hook; pause via
non-zero return
- `mld_sign_hook_finish(attempt [, ctx])`: note the succeeding attempt
The unset dummies leave the default build unchanged.
- The MLD_ERR_SIGNING_PAUSED error code, returned when the attempt hook pauses.
CBMC proofs, a test, and the regenerated configs follow in separate commits.
Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
Add a FAQ entry to README.md pointing users with real-time / bounded-runtime requirements at the signing hooks and the examples/restartable_sign example. Documentation only. Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
Verify the three signing-hook call sites added in the previous commit: mld_sign_resume, mld_sign_attempt and mld_sign_finish. In the CBMC configuration the hooks reduce to their contract dummies, so each harness just checks the call-by-contract stub against its (empty) contract, mirroring how sign.c invokes them (the CBMC config carries no context parameter, so the `context` argument is consumed by the call macro exactly as at the call sites). Also refresh the sign_signature_internal harness Makefile for the reworked signing loop. Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
Add a test analogous to test_alloc, driven by a custom config (test_sign_hook_config.h) that enables the three signing-hook options and forward-declares the hook context; the context type and the hook implementations live in the test. The hooks pause signing after a configurable number of attempts and resume from the recorded attempt, letting one context drive: - a known-answer smoke test, reproducing the test-vector signature both one-shot and single-step; - a restartable-signing equivalence test, checking that pausing after a random number of attempts and resuming yields the same signature as an uninterrupted run, over many random signing-randomness draws; - a distribution test, gathering the attempts-per-signature histogram over many one-shot signatures and checking the measured attempts/signature ratio against the expected geometric mean for the parameter set. The test requires the internal signing API, so it compiles to a no-op main under MLD_CONFIG_NO_SIGN_API. The distribution test additionally generates a fresh key per iteration (the other two use the fixed test-vector key), so it alone is compiled out under MLD_CONFIG_NO_KEYPAIR_API. Statistics are printed via printf only (no putchar), so freestanding platforms with a minimal stdio shim can run it. Wire it up in the Makefile, the test make fragments and scripts/tests (new --sign-hook subcommand). Mirror alloc's CI plumbing: add a sign_hook input to the functest / multi-functest actions and disable it for the config variations it is incompatible with -- those passing their own MLD_CONFIG_FILE (would clash with the test's fixed config) and those enabling MLD_CONFIG_KEYGEN_PCT (the signing hooks require MLD_CONFIG_NO_RANDOMIZED_API, which is incompatible with the PCT). The generated configs follow in a separate commit. Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
Rerun scripts/autogen after the preceding commits, updating various files. Done separately to ease reviewing of the core of the changes. Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
Add examples/restartable_sign, demonstrating the MLD_CONFIG_SIGN_HOOK_{RESUME,
ATTEMPT,FINISH} hooks. The example provides the three hooks over a single
global, drives the deterministic signing API in a loop that pauses (via
MLD_ERR_SIGNING_PAUSED) after every attempt and resumes from the recorded
point, and checks that the split, single-step run reproduces both the one-shot
signature and the reference test vector.
Wire it into the examples README, the examples CI job, and scripts/tests.
Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
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.
Uh oh!
There was an error while loading. Please reload this page.