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.
As a remedy, the library already provides a configuration option
MLD_CONFIG_MAX_SIGNING_ATTEMPTS to limit the number of signing attempts.
However, FIPS 204 explicitly forbids lowering the number of signing attempts
below 814, rendering the use of this option in constrained environments
non-compliant.
As a remedy, this commit introduces a facility for _restartable signing_:
In addition to using MLD_CONFIG_MAX_SIGNING_ATTEMPTS, the user can provide
a custom hook for remembering the last signing attempt, and continuing the
next signing call at that point.
More specifically, the idea is to introduce three 'signing hooks'
into the core signature routine:
- `nonce = sign_resume()`: Return the nonce corresponding to the next
signing attempt. `0` for an initial / non-restartable signing
operation.
- `sign_attempt(nonce)`: Note the nonce for the next signing attempt.
- `sign_finish(nonce)`: Note the nonce for which signing succeeded.
Here, only `sign_resume()` affects the signing process functionally,
while `sign_attempt()` and `sign_finish()` are passive logging operations.
This approach to restartable signing only works if the consumer works
directly with the internal signing API. If they use the randomized API,
a fresh seed would be chosen even on supposedly restarted signing operations,
leading to non-compliant signatures. The use of a `sign_resume()` hook
is therefore only safe with MLD_CONFIG_NO_RANDOMIZED_API.
In contrast, `sign_attempt` and `sign_finish` are passive, and can safely
be used on their own with any configuration. Indeed, these hooks serve
other uses outside of restartable signing:
- sign_attempt can be used to get finer-grained benchmarks for individual
signing attempts
- sign_finish can be used to log the number of signing attempts, which
can be useful again for benchmarking, or for checking the expected
distribution of signing attempts.
Finally, sign_{resume|attempt|finish} can all be implemented based on
mutable globals, or based on a context. It therefore makes sense to
introduce them under the existing MLD_CONFIG_CONTEXT_PARAMETER
wrapper, which allows functions to be called with or without an
application-specific context pointer.
Taking all these considerations together, here's what the commit changes:
- Add independent MLD_CONFIG_SIGN_HOOK_{RESUME|FINISH|ATTEMPT} configuration
options for registering signing hooks as above. When set, the user provides
the following functions, respectively:
- `nonce = mld_sign_resume([ctx])`: Provide initial or resuming nonce
- `mld_sign_attempt(nonce [, ctx])`: Log/remember nonce for next attempt
- `mld_sign_finish(nonce [, ctx])`: Log/remember nonce for successful attempt
- The option can be used with or without MLD_CONFIG_CONTEXT_PARAMETER. If the
latter is set, the user-provided context will be passed to `mld_sign_xxx`
as the last argument.
- An example is added illustrating the use of the hooks to implement restartable
signing.
Note: A consumer might want to use restartable signing with randomized APIs.
This is not natively supported. However, it is expected to be easily implementable
since the components required to build randomized signing from deterministic
signing are all public. Note that resuming additionally requires the consumer
to hold the signing randomness `rnd` fixed across the resumed calls.
Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
Uh oh!
There was an error while loading. Please reload this page.