From 58c81d979d7a82b2e1bb80f41da175421b043b56 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 10:28:17 +0100 Subject: [PATCH 1/9] refactor: Introduce MLD_MAX_KAPPA and use it consistently 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 --- mldsa/src/params.h | 6 ++++++ mldsa/src/polyvec.c | 4 ++-- mldsa/src/polyvec.h | 2 +- mldsa/src/polyvec_lazy.h | 10 +++++----- mldsa/src/sign.c | 7 ++----- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/mldsa/src/params.h b/mldsa/src/params.h index 5a63f77e0..db3bf6c53 100644 --- a/mldsa/src/params.h +++ b/mldsa/src/params.h @@ -70,6 +70,12 @@ #define MLDSA_POLYT0_PACKEDBYTES 416 #define MLDSA_POLYVECH_PACKEDBYTES (MLDSA_OMEGA + MLDSA_K) +/* Sampling y from counter kappa uses nonces kappa, ..., kappa+L-1, which fit in + * uint16_t iff kappa <= UINT16_MAX - MLDSA_L. With kappa = attempt*MLDSA_L this + * bounds the number of signing attempts by MLD_MAX_KAPPA / MLDSA_L; see + * MLD_MAX_SIGNING_ATTEMPTS in sign.c. */ +#define MLD_MAX_KAPPA (UINT16_MAX - MLDSA_L) + /* Layout of the packed public key pk[MLDSA_CRYPTO_PUBLICKEYBYTES] = (rho, t1): * * +-------------+--------------------------+ diff --git a/mldsa/src/polyvec.c b/mldsa/src/polyvec.c index b27868f17..8d427a8f8 100644 --- a/mldsa/src/polyvec.c +++ b/mldsa/src/polyvec.c @@ -37,8 +37,8 @@ void mld_polyvecl_uniform_gamma1(mld_polyvecl *v, #endif /* The caller passes the base counter kappa; component i is sampled from - * kappa + i. Safety: kappa <= UINT16_MAX - MLDSA_L and i < MLDSA_L, so the - * casts below are safe. See MLD_MAX_KAPPA comment in sign.c. */ + * kappa + i. Safety: kappa <= MLD_MAX_KAPPA and i < MLDSA_L, so the + * casts below are safe. See MLD_MAX_KAPPA comment in params.h. */ #if defined(MLD_CONFIG_SERIAL_FIPS202_ONLY) for (i = 0; i < MLDSA_L; i++) { diff --git a/mldsa/src/polyvec.h b/mldsa/src/polyvec.h index 610ed7bdb..8a09c6671 100644 --- a/mldsa/src/polyvec.h +++ b/mldsa/src/polyvec.h @@ -56,7 +56,7 @@ void mld_polyvecl_uniform_gamma1(mld_polyvecl *v, __contract__( requires(memory_no_alias(v, sizeof(mld_polyvecl))) requires(memory_no_alias(seed, MLDSA_CRHBYTES)) - requires(kappa <= UINT16_MAX - MLDSA_L) + requires(kappa <= MLD_MAX_KAPPA) assigns(memory_slice(v, sizeof(mld_polyvecl))) ensures(forall(k0, 0, MLDSA_L, array_bound(v->vec[k0].coeffs, 0, MLDSA_N, -(MLDSA_GAMMA1 - 1), MLDSA_GAMMA1 + 1))) diff --git a/mldsa/src/polyvec_lazy.h b/mldsa/src/polyvec_lazy.h index 501d44c95..baecdb822 100644 --- a/mldsa/src/polyvec_lazy.h +++ b/mldsa/src/polyvec_lazy.h @@ -345,7 +345,7 @@ static MLD_INLINE void mld_yvec_init_eager( __contract__( requires(memory_no_alias(y, sizeof(mld_yvec_eager))) requires(memory_no_alias(rhoprime, MLDSA_CRHBYTES)) - requires(kappa <= UINT16_MAX - MLDSA_L) + requires(kappa <= MLD_MAX_KAPPA) assigns(memory_slice(y, sizeof(mld_yvec_eager))) ensures(forall(k1, 0, MLDSA_L, array_bound(y->vec.vec[k1].coeffs, 0, MLDSA_N, -(MLDSA_GAMMA1 - 1), MLDSA_GAMMA1 + 1))) @@ -390,13 +390,13 @@ __contract__( requires(memory_no_alias(y, sizeof(mld_yvec_lazy))) requires(i < MLDSA_L) requires(memory_no_alias(y->rhoprime, MLDSA_CRHBYTES)) - requires(y->kappa <= UINT16_MAX - MLDSA_L) + requires(y->kappa <= MLD_MAX_KAPPA) assigns(memory_slice(buf, sizeof(mld_poly))) ensures(array_bound(buf->coeffs, 0, MLDSA_N, -(MLDSA_GAMMA1 - 1), MLDSA_GAMMA1 + 1)) ) { - /* Safety: y->kappa <= UINT16_MAX - MLDSA_L and i < MLDSA_L, so y->kappa + i - * fits in uint16_t. See MLD_MAX_KAPPA comment in sign.c. */ + /* Safety: y->kappa <= MLD_MAX_KAPPA and i < MLDSA_L, so y->kappa + i + * fits in uint16_t. See MLD_MAX_KAPPA comment in params.h. */ mld_poly_uniform_gamma1(buf, y->rhoprime, (uint16_t)(y->kappa + i)); } #endif /* !MLD_CONFIG_NO_SIGN_API && (MLD_CONFIG_REDUCE_RAM || MLD_UNIT_TEST) \ @@ -592,7 +592,7 @@ __contract__( requires(memory_no_alias(y, sizeof(mld_yvec_lazy))) requires(memory_no_alias(scratch, sizeof(mld_polyvecl))) requires(memory_no_alias(y->rhoprime, MLDSA_CRHBYTES)) - requires(y->kappa <= UINT16_MAX - MLDSA_L) + requires(y->kappa <= MLD_MAX_KAPPA) assigns(memory_slice(w, sizeof(mld_polyveck))) assigns(memory_slice(mat, sizeof(mld_polymat_lazy))) assigns(memory_slice(scratch, sizeof(mld_polyvecl))) diff --git a/mldsa/src/sign.c b/mldsa/src/sign.c index 7a9e47f88..217d8aede 100644 --- a/mldsa/src/sign.c +++ b/mldsa/src/sign.c @@ -489,11 +489,8 @@ __contract__( #endif /* !MLD_CONFIG_NO_SIGN_API || !MLD_CONFIG_NO_VERIFY_API */ #if !defined(MLD_CONFIG_NO_SIGN_API) -/* Sampling y from counter kappa uses nonces kappa, ..., kappa+L-1, which fit in - * uint16_t iff kappa <= UINT16_MAX - MLDSA_L. */ -#define MLD_MAX_KAPPA (UINT16_MAX - MLDSA_L) - -/* With kappa = attempt*MLDSA_L, the kappa bound becomes a bound on attempts. +/* MLD_MAX_KAPPA (see params.h) bounds the rejection-sampling counter kappa. + * With kappa = attempt*MLDSA_L, the kappa bound becomes a bound on attempts. * Bounding attempts (the reference loops indefinitely) gives predictable * termination and provable type-safety. */ #define MLD_MAX_SIGNING_ATTEMPTS (MLD_MAX_KAPPA / MLDSA_L) From 247dde3b22eb4366a7c1c4d568df4f72e03436b5 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 11:16:38 +0100 Subject: [PATCH 2/9] refactor: Add MLD_CONTEXT_UNUSED helper for unused context parameters 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 --- mldsa/src/common.h | 1 + mldsa/src/context.h | 9 +++++++++ mldsa/src/sign.c | 4 +--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/mldsa/src/common.h b/mldsa/src/common.h index b21646a46..ff2425310 100644 --- a/mldsa/src/common.h +++ b/mldsa/src/common.h @@ -226,6 +226,7 @@ #define MLD_FREE(v, T, N, context) \ do \ { \ + MLD_CONTEXT_UNUSED(context); \ mld_zeroize(mld_alloc_##v, sizeof(mld_alloc_##v)); \ (v) = NULL; \ } while (0) diff --git a/mldsa/src/context.h b/mldsa/src/context.h index 901fc6398..ee23196b8 100644 --- a/mldsa/src/context.h +++ b/mldsa/src/context.h @@ -60,6 +60,15 @@ (arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) #endif /* !MLD_CONFIG_CONTEXT_PARAMETER */ +/* Consume a context parameter carried only for the integration's benefit, + * avoiding -Wunused-parameter; expands to nothing when no context is + * configured. */ +#if defined(MLD_CONFIG_CONTEXT_PARAMETER) +#define MLD_CONTEXT_UNUSED(context) ((void)(context)) +#else +#define MLD_CONTEXT_UNUSED(context) ((void)0) +#endif + #if defined(MLD_CONFIG_CONTEXT_PARAMETER_TYPE) != \ defined(MLD_CONFIG_CONTEXT_PARAMETER) #error MLD_CONFIG_CONTEXT_PARAMETER_TYPE must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is defined diff --git a/mldsa/src/sign.c b/mldsa/src/sign.c index 217d8aede..22714796d 100644 --- a/mldsa/src/sign.c +++ b/mldsa/src/sign.c @@ -138,9 +138,7 @@ static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES], /* Skip PCT */ ((void)pk); ((void)sk); -#if defined(MLD_CONFIG_CONTEXT_PARAMETER) - ((void)context); -#endif + MLD_CONTEXT_UNUSED(context); return 0; } #endif /* !MLD_CONFIG_KEYGEN_PCT */ From 1542af692212eaa82f6ecd42eb0bfb821bb86651 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 12:42:40 +0100 Subject: [PATCH 3/9] sign: Make MLD_MAX_SIGNING_ATTEMPTS the effective attempt bound 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 --- mldsa/src/sign.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/mldsa/src/sign.c b/mldsa/src/sign.c index 22714796d..6b25d9ccb 100644 --- a/mldsa/src/sign.c +++ b/mldsa/src/sign.c @@ -487,11 +487,8 @@ __contract__( #endif /* !MLD_CONFIG_NO_SIGN_API || !MLD_CONFIG_NO_VERIFY_API */ #if !defined(MLD_CONFIG_NO_SIGN_API) -/* MLD_MAX_KAPPA (see params.h) bounds the rejection-sampling counter kappa. - * With kappa = attempt*MLDSA_L, the kappa bound becomes a bound on attempts. - * Bounding attempts (the reference loops indefinitely) gives predictable - * termination and provable type-safety. */ -#define MLD_MAX_SIGNING_ATTEMPTS (MLD_MAX_KAPPA / MLDSA_L) +/* MLD_MAX_KAPPA (see params.h) bounds the rejection-sampling counter kappa; + * MLD_MAX_SIGNING_ATTEMPTS below turns that into a bound on attempts. */ /** * Compute z = y + s1*c, check that z has coefficients smaller than @@ -585,9 +582,11 @@ __contract__( return 0; } -/* User-facing bound on signing attempts. See MLD_CONFIG_MAX_SIGNING_ATTEMPTS - * in mldsa_native_config.h. Default is chosen so that failure probability - * is < 2^{-256}, that is, signatures will practically always succeed. */ +/* Effective bound on signing attempts: the configured bound + * MLD_CONFIG_MAX_SIGNING_ATTEMPTS (see mldsa_native_config.h) if set, otherwise + * the hard type-safety bound MLD_MAX_KAPPA / MLDSA_L (see MLD_MAX_KAPPA in + * params.h). The default is chosen so that the failure probability is + * < 2^{-256}, that is, signatures will practically always succeed. */ #if defined(MLD_CONFIG_MAX_SIGNING_ATTEMPTS) #if !defined(MLD_ALLOW_NONCOMPLIANT_SIGNING_BOUND) && \ @@ -599,27 +598,26 @@ __contract__( #error Bad configuration: MLD_CONFIG_MAX_SIGNING_ATTEMPTS must be >= 1 #endif -#if MLD_CONFIG_MAX_SIGNING_ATTEMPTS > MLD_MAX_SIGNING_ATTEMPTS +#if MLD_CONFIG_MAX_SIGNING_ATTEMPTS > MLD_MAX_KAPPA / MLDSA_L #error Bad configuration: MLD_CONFIG_MAX_SIGNING_ATTEMPTS exceeds the maximum allowed value. #endif -#endif /* MLD_CONFIG_MAX_SIGNING_ATTEMPTS */ +#define MLD_MAX_SIGNING_ATTEMPTS MLD_CONFIG_MAX_SIGNING_ATTEMPTS +#else /* MLD_CONFIG_MAX_SIGNING_ATTEMPTS */ +#define MLD_MAX_SIGNING_ATTEMPTS (MLD_MAX_KAPPA / MLDSA_L) +#endif /* !MLD_CONFIG_MAX_SIGNING_ATTEMPTS */ MLD_MUST_CHECK_RETURN_VALUE static MLD_INLINE uint16_t mld_get_max_signing_attempts(void) __contract__( ensures(return_value >= 1) - ensures(return_value <= MLD_MAX_SIGNING_ATTEMPTS) + ensures(return_value <= MLD_MAX_KAPPA / MLDSA_L) ) { /* cassert(0) ensures CBMC uses the contract rather than inlining the body, * keeping proofs agnostic of the configured value. */ cassert(0); -#if defined(MLD_CONFIG_MAX_SIGNING_ATTEMPTS) - return MLD_CONFIG_MAX_SIGNING_ATTEMPTS; -#else return MLD_MAX_SIGNING_ATTEMPTS; -#endif } /** @@ -991,7 +989,7 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], decreases(max_signing_attempts - attempt) ) { - /* Safety: attempt < max_signing_attempts <= MLD_MAX_SIGNING_ATTEMPTS, so + /* Safety: attempt < max_signing_attempts <= MLD_MAX_KAPPA / MLDSA_L, so * kappa <= MLD_MAX_KAPPA and the cast is safe. */ const uint16_t kappa = (uint16_t)(attempt * MLDSA_L); ret = mld_attempt_signature_generation(sig, mu, rhoprime, kappa, mat, s1hat, From ac5eba28c16b712370746575e2aadafb2e2329b6 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Mon, 13 Jul 2026 11:31:18 +0100 Subject: [PATCH 4/9] sign: Add configurable signing hooks (resume/attempt/finish) 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 --- mldsa/mldsa_native.h | 11 ++++ mldsa/mldsa_native_config.h | 106 +++++++++++++++++++++++++++++++++--- mldsa/src/common.h | 12 +++- mldsa/src/context.h | 75 +++++++++++++++++++++++++ mldsa/src/sign.c | 34 ++++++++++-- mldsa/src/sign.h | 13 ++++- 6 files changed, 233 insertions(+), 18 deletions(-) diff --git a/mldsa/mldsa_native.h b/mldsa/mldsa_native.h index 7810345d3..155399b1f 100644 --- a/mldsa/mldsa_native.h +++ b/mldsa/mldsa_native.h @@ -103,6 +103,11 @@ * signature. With a FIPS 204 Appendix C compliant bound (>= 814) this * has probability < 2^-256. */ #define MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED (-4) +/* Signing was paused before completing, at the request of a caller-provided + * MLD_CONFIG_SIGN_HOOK_ATTEMPT hook (see mldsa_native_config.h). The caller + * resumes by re-invoking signing with the same inputs; the attempt hook, + * together with MLD_CONFIG_SIGN_HOOK_RESUME, decides where to continue. */ +#define MLD_ERR_SIGNING_PAUSED (-5) /********************* Namespacing and Qualifiers *****************************/ @@ -277,6 +282,8 @@ int MLD_API_NAMESPACE(keypair)( * @retval MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED The rejection-sampling loop exceeded * MLD_CONFIG_MAX_SIGNING_ATTEMPTS * iterations. + * @retval MLD_ERR_SIGNING_PAUSED A MLD_CONFIG_SIGN_HOOK_ATTEMPT hook + * paused signing; re-invoke to resume. * @retval MLD_ERR_FAIL Other kinds of failure. */ MLD_API_QUALIFIER @@ -554,6 +561,8 @@ int MLD_API_NAMESPACE(verify_extmu)( * @retval MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED The rejection-sampling loop exceeded * MLD_CONFIG_MAX_SIGNING_ATTEMPTS * iterations. + * @retval MLD_ERR_SIGNING_PAUSED A MLD_CONFIG_SIGN_HOOK_ATTEMPT hook + * paused signing; re-invoke to resume. * @retval MLD_ERR_FAIL Other kinds of failure. */ MLD_API_QUALIFIER @@ -650,6 +659,8 @@ int MLD_API_NAMESPACE(verify_pre_hash_internal)( * @retval MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED The rejection-sampling loop exceeded * MLD_CONFIG_MAX_SIGNING_ATTEMPTS * iterations. + * @retval MLD_ERR_SIGNING_PAUSED A MLD_CONFIG_SIGN_HOOK_ATTEMPT hook + * paused signing; re-invoke to resume. * @retval MLD_ERR_FAIL Other kinds of failure. */ MLD_API_QUALIFIER diff --git a/mldsa/mldsa_native_config.h b/mldsa/mldsa_native_config.h index b13f33e31..9336fca73 100644 --- a/mldsa/mldsa_native_config.h +++ b/mldsa/mldsa_native_config.h @@ -684,24 +684,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/mldsa/src/common.h b/mldsa/src/common.h index ff2425310..895dae850 100644 --- a/mldsa/src/common.h +++ b/mldsa/src/common.h @@ -271,15 +271,21 @@ * signature. With a FIPS 204 Appendix C compliant bound (>= 814) this * has probability < 2^-256. */ #define MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED (-4) +/* Signing was paused before completing, at the request of a caller-provided + * MLD_CONFIG_SIGN_HOOK_ATTEMPT hook (see mldsa_native_config.h). The caller + * resumes by re-invoking signing with the same inputs; the attempt hook, + * together with MLD_CONFIG_SIGN_HOOK_RESUME, decides where to continue. */ +#define MLD_ERR_SIGNING_PAUSED (-5) /* Disjunction over the full set of MLD_ERR_XXX failure codes. * * Intended for use in top-level `ensures` clauses that admit every * possible error. Narrower contracts should enumerate only the * specific errors they can actually return. */ -#define MLD_ANY_ERROR(err) \ - ((err) == MLD_ERR_FAIL || (err) == MLD_ERR_OUT_OF_MEMORY || \ - (err) == MLD_ERR_RNG_FAIL || (err) == MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED) +#define MLD_ANY_ERROR(err) \ + ((err) == MLD_ERR_FAIL || (err) == MLD_ERR_OUT_OF_MEMORY || \ + (err) == MLD_ERR_RNG_FAIL || (err) == MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED || \ + (err) == MLD_ERR_SIGNING_PAUSED) #endif /* !__ASSEMBLER__ */ diff --git a/mldsa/src/context.h b/mldsa/src/context.h index ee23196b8..f5e582d7c 100644 --- a/mldsa/src/context.h +++ b/mldsa/src/context.h @@ -7,6 +7,11 @@ /* This header is included by common.h once the configuration has been pulled * in; it is not meant to be included directly. */ +#if !defined(__ASSEMBLER__) + +#include +#include "cbmc.h" +#include "sys.h" /* * If the integration wants to provide a context parameter for use in @@ -74,4 +79,74 @@ #error MLD_CONFIG_CONTEXT_PARAMETER_TYPE must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is defined #endif +/* The signing hooks tie into the rejection-sampling loop. A pausing attempt + * hook only reproduces the uninterrupted signature if the randomness is fixed + * across calls, and thus requires the deterministic API. + * For now we impose that requirement on all three hooks uniformly: enabling any + * of them requires MLD_CONFIG_NO_RANDOMIZED_API. This also rules out + * MLD_CONFIG_KEYGEN_PCT (whose PCT needs the randomized signature(), see + * common.h). + * + * A logging-only use (attempt always returns 0; resume/finish merely observe) + * would be safe with the randomized API too, but the restriction is applied + * uniformly for now. */ +#if (defined(MLD_CONFIG_SIGN_HOOK_RESUME) || \ + defined(MLD_CONFIG_SIGN_HOOK_ATTEMPT) || \ + defined(MLD_CONFIG_SIGN_HOOK_FINISH)) && \ + !defined(MLD_CONFIG_NO_RANDOMIZED_API) +#error Signing hooks (MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH) require MLD_CONFIG_NO_RANDOMIZED_API +#endif /* (MLD_CONFIG_SIGN_HOOK_RESUME || MLD_CONFIG_SIGN_HOOK_ATTEMPT || \ + MLD_CONFIG_SIGN_HOOK_FINISH) && !MLD_CONFIG_NO_RANDOMIZED_API */ + +/* Signing hooks (MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH; documented + * in mldsa_native_config.h). The following macros route the call sites to + * mld_sign_hook_*, appending or dropping the context argument; each unset hook + * uses the dummy below. */ +#define mld_sign_resume mld_sign_hook_resume MLD_CONTEXT_PARAMETERS_0 +#define mld_sign_attempt mld_sign_hook_attempt MLD_CONTEXT_PARAMETERS_1 +#define mld_sign_finish mld_sign_hook_finish MLD_CONTEXT_PARAMETERS_1 + +/* We don't use mld_sign_resume here because MLD_CONTEXT_PARAMETERS_0 is + * unsuitable for function declarations: it misses `void` as the placeholder + * argument. */ +#if !defined(MLD_CONFIG_SIGN_HOOK_RESUME) +MLD_MUST_CHECK_RETURN_VALUE +static MLD_INLINE uint16_t mld_sign_hook_resume( +#if defined(MLD_CONFIG_CONTEXT_PARAMETER) + MLD_CONFIG_CONTEXT_PARAMETER_TYPE context +#else + void +#endif +) +__contract__(assigns() ensures(1)) +{ + MLD_CONTEXT_UNUSED(context); + return 0; +} +#endif /* !MLD_CONFIG_SIGN_HOOK_RESUME */ + +#if !defined(MLD_CONFIG_SIGN_HOOK_ATTEMPT) +MLD_MUST_CHECK_RETURN_VALUE +static MLD_INLINE int mld_sign_attempt( + uint16_t attempt, MLD_CONFIG_CONTEXT_PARAMETER_TYPE context) +__contract__(assigns() ensures(1)) +{ + ((void)attempt); + MLD_CONTEXT_UNUSED(context); + return 0; +} +#endif /* !MLD_CONFIG_SIGN_HOOK_ATTEMPT */ + +#if !defined(MLD_CONFIG_SIGN_HOOK_FINISH) +static MLD_INLINE void mld_sign_finish( + uint16_t attempt, MLD_CONFIG_CONTEXT_PARAMETER_TYPE context) +__contract__(assigns() ensures(1)) +{ + ((void)attempt); + MLD_CONTEXT_UNUSED(context); +} +#endif /* !MLD_CONFIG_SIGN_HOOK_FINISH */ + +#endif /* !__ASSEMBLER__ */ + #endif /* !MLD_CONTEXT_H */ diff --git a/mldsa/src/sign.c b/mldsa/src/sign.c index 6b25d9ccb..ce9773adc 100644 --- a/mldsa/src/sign.c +++ b/mldsa/src/sign.c @@ -130,7 +130,7 @@ static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES], return ret; } -#else /* MLD_CONFIG_KEYGEN_PCT */ +#else /* MLD_CONFIG_KEYGEN_PCT */ static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES], uint8_t const sk[MLDSA_CRYPTO_SECRETKEYBYTES], MLD_CONFIG_CONTEXT_PARAMETER_TYPE context) @@ -585,8 +585,7 @@ __contract__( /* Effective bound on signing attempts: the configured bound * MLD_CONFIG_MAX_SIGNING_ATTEMPTS (see mldsa_native_config.h) if set, otherwise * the hard type-safety bound MLD_MAX_KAPPA / MLDSA_L (see MLD_MAX_KAPPA in - * params.h). The default is chosen so that the failure probability is - * < 2^{-256}, that is, signatures will practically always succeed. */ + * params.h). */ #if defined(MLD_CONFIG_MAX_SIGNING_ATTEMPTS) #if !defined(MLD_ALLOW_NONCOMPLIANT_SIGNING_BOUND) && \ @@ -907,8 +906,8 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], { int ret; uint8_t *rho, *tr, *key, *mu, *rhoprime; + uint16_t attempt; const uint16_t max_signing_attempts = mld_get_max_signing_attempts(); - uint16_t attempt = 0; MLD_ALLOC(seedbuf, uint8_t, 2 * MLDSA_SEEDBYTES + MLDSA_TRBYTES + 2 * MLDSA_CRHBYTES, context); MLD_ALLOC(mat, mld_polymat, 1, context); @@ -923,6 +922,15 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], goto cleanup; } + /* If a resume hook is configured via MLD_CONFIG_SIGN_HOOK_RESUME, it provides + * the attempt to resume from after an earlier pause. Otherwise, we start at + * 0. Clamp to max_signing_attempts. */ + attempt = mld_sign_resume(context); + if (attempt > max_signing_attempts) + { + attempt = max_signing_attempts; + } + rho = seedbuf; tr = rho + MLDSA_SEEDBYTES; key = tr + MLDSA_TRBYTES; @@ -992,6 +1000,16 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], /* Safety: attempt < max_signing_attempts <= MLD_MAX_KAPPA / MLDSA_L, so * kappa <= MLD_MAX_KAPPA and the cast is safe. */ const uint16_t kappa = (uint16_t)(attempt * MLDSA_L); + + /* Query configurable signing hook whether signing should be paused. + * This is skipped by default and only used if the user sets the + * configuration option MLD_CONFIG_SIGN_HOOK_ATTEMPT. */ + if (mld_sign_attempt(attempt, context) != 0) + { + ret = MLD_ERR_SIGNING_PAUSED; + goto cleanup; + } + ret = mld_attempt_signature_generation(sig, mu, rhoprime, kappa, mat, s1hat, s2hat, t0hat, context); @@ -1002,6 +1020,13 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], * nonce. * - any other value (e.g. MLD_ERR_OUT_OF_MEMORY): an unrecoverable error * occurred, so we propagate it to the caller. */ + if (ret == 0) + { + /* Signing succeeded: record the attempt that succeeded. No-op in the + * default build. */ + mld_sign_finish(attempt, context); + goto cleanup; + } if (ret != MLD_ERR_FAIL) { goto cleanup; @@ -1652,6 +1677,5 @@ int mld_sign_pk_from_sk(uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES], #undef mld_attempt_signature_generation #undef mld_compute_pack_t0_t1 #undef mld_get_max_signing_attempts -#undef MLD_MAX_KAPPA #undef MLD_MAX_SIGNING_ATTEMPTS #undef MLD_PRE_HASH_OID_LEN diff --git a/mldsa/src/sign.h b/mldsa/src/sign.h index 661e0421d..9573e0413 100644 --- a/mldsa/src/sign.h +++ b/mldsa/src/sign.h @@ -211,6 +211,8 @@ __contract__( * @retval MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED The rejection-sampling loop exceeded * MLD_CONFIG_MAX_SIGNING_ATTEMPTS * iterations. + * @retval MLD_ERR_SIGNING_PAUSED A MLD_CONFIG_SIGN_HOOK_ATTEMPT hook + * paused signing; re-invoke to resume. * @retval MLD_ERR_FAIL Other kinds of failure. */ MLD_MUST_CHECK_RETURN_VALUE @@ -234,7 +236,8 @@ __contract__( assigns(memory_slice(sig, MLDSA_CRYPTO_BYTES)) ensures(return_value == 0 || return_value == MLD_ERR_FAIL || return_value == MLD_ERR_OUT_OF_MEMORY || - return_value == MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED)); + return_value == MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED || + return_value == MLD_ERR_SIGNING_PAUSED)); #if !defined(MLD_CONFIG_CORE_API_ONLY) /** @@ -495,6 +498,8 @@ __contract__( requires(memory_no_alias(sig, MLDSA_CRYPTO_BYTES)) * @retval MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED The rejection-sampling loop exceeded * MLD_CONFIG_MAX_SIGNING_ATTEMPTS * iterations. + * @retval MLD_ERR_SIGNING_PAUSED A MLD_CONFIG_SIGN_HOOK_ATTEMPT hook + * paused signing; re-invoke to resume. * @retval MLD_ERR_FAIL Other kinds of failure. */ MLD_MUST_CHECK_RETURN_VALUE @@ -513,7 +518,7 @@ __contract__( requires(memory_no_alias(rnd, MLDSA_RNDBYTES)) requires(memory_no_alias(sk, MLDSA_CRYPTO_SECRETKEYBYTES)) assigns(memory_slice(sig, MLDSA_CRYPTO_BYTES)) - ensures(return_value == 0 || return_value == MLD_ERR_FAIL || return_value == MLD_ERR_OUT_OF_MEMORY || return_value == MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED) + ensures(return_value == 0 || return_value == MLD_ERR_FAIL || return_value == MLD_ERR_OUT_OF_MEMORY || return_value == MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED || return_value == MLD_ERR_SIGNING_PAUSED) ); #endif /* !MLD_CONFIG_NO_SIGN_API */ @@ -598,6 +603,8 @@ __contract__( * @retval MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED The rejection-sampling loop exceeded * MLD_CONFIG_MAX_SIGNING_ATTEMPTS * iterations. + * @retval MLD_ERR_SIGNING_PAUSED A MLD_CONFIG_SIGN_HOOK_ATTEMPT hook + * paused signing; re-invoke to resume. * @retval MLD_ERR_FAIL Other kinds of failure. */ MLD_MUST_CHECK_RETURN_VALUE @@ -616,7 +623,7 @@ __contract__( requires(memory_no_alias(rnd, MLDSA_RNDBYTES)) requires(memory_no_alias(sk, MLDSA_CRYPTO_SECRETKEYBYTES)) assigns(memory_slice(sig, MLDSA_CRYPTO_BYTES)) - ensures(return_value == 0 || return_value == MLD_ERR_FAIL || return_value == MLD_ERR_OUT_OF_MEMORY || return_value == MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED) + ensures(return_value == 0 || return_value == MLD_ERR_FAIL || return_value == MLD_ERR_OUT_OF_MEMORY || return_value == MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED || return_value == MLD_ERR_SIGNING_PAUSED) ); #endif /* !MLD_CONFIG_NO_SIGN_API */ From b3043326e804293c620d30acaf3da06935cbf01f Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Mon, 13 Jul 2026 11:31:19 +0100 Subject: [PATCH 5/9] doc: Add README FAQ entry on bounding/restarting the signing loop 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 --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 25b6b1bef..a2b1914f9 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,10 @@ Yes. mldsa-native provides a compile-time option `MLD_CONFIG_REDUCE_RAM` that re To enable this mode, define `MLD_CONFIG_REDUCE_RAM` in [mldsa_native_config.h](mldsa/mldsa_native_config.h) or pass `-DMLD_CONFIG_REDUCE_RAM` as a compiler flag. +### Can I bound or restart the signing loop for real-time systems? + +Yes. Signing hooks allow you to pause and restart ML-DSA signature operations, thereby bounding their runtime. See [examples/restartable_sign](examples/restartable_sign) for an example. + ### Does mldsa-native use hedged or deterministic signing? By default, mldsa-native uses the randomized "hedged" signing variant as specified in FIPS 204 Section 3.4. The hedged variant uses both fresh randomness at signing time and precomputed randomness from the private key. This helps mitigate fault injection attacks and side-channel attacks while protecting against potential flaws in the random number generator. From 853754ff1d0fa2fecbb115563ddb777e5da4e655 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 11:17:25 +0100 Subject: [PATCH 6/9] proofs: Add CBMC harnesses for the signing hooks 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 --- proofs/cbmc/sign_attempt/Makefile | 35 +++++++++++++++++++ .../cbmc/sign_attempt/sign_attempt_harness.c | 14 ++++++++ proofs/cbmc/sign_finish/Makefile | 35 +++++++++++++++++++ proofs/cbmc/sign_finish/sign_finish_harness.c | 12 +++++++ proofs/cbmc/sign_resume/Makefile | 35 +++++++++++++++++++ proofs/cbmc/sign_resume/sign_resume_harness.c | 12 +++++++ proofs/cbmc/sign_signature_internal/Makefile | 3 ++ scripts/check-contracts | 4 +++ 8 files changed, 150 insertions(+) create mode 100644 proofs/cbmc/sign_attempt/Makefile create mode 100644 proofs/cbmc/sign_attempt/sign_attempt_harness.c create mode 100644 proofs/cbmc/sign_finish/Makefile create mode 100644 proofs/cbmc/sign_finish/sign_finish_harness.c create mode 100644 proofs/cbmc/sign_resume/Makefile create mode 100644 proofs/cbmc/sign_resume/sign_resume_harness.c diff --git a/proofs/cbmc/sign_attempt/Makefile b/proofs/cbmc/sign_attempt/Makefile new file mode 100644 index 000000000..bdd2659ed --- /dev/null +++ b/proofs/cbmc/sign_attempt/Makefile @@ -0,0 +1,35 @@ +# Copyright (c) The mldsa-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +include ../Makefile_params.common + +HARNESS_ENTRY = harness +HARNESS_FILE = sign_attempt_harness + +# This should be a unique identifier for this proof, and will appear on the +# Litani dashboard. It can be human-readable and contain spaces if you wish. +PROOF_UID = mld_sign_attempt + +DEFINES += +INCLUDES += + +REMOVE_FUNCTION_BODY += +UNWINDSET += + +PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c +PROJECT_SOURCES += $(SRCDIR)/mldsa/src/sign.c + +CHECK_FUNCTION_CONTRACTS=mld_sign_hook_attempt +USE_FUNCTION_CONTRACTS= + +APPLY_LOOP_CONTRACTS=on +USE_DYNAMIC_FRAMES=1 + +EXTERNAL_SAT_SOLVER= +CBMCFLAGS=--smt2 + +FUNCTION_NAME = mld_sign_hook_attempt + +CBMC_OBJECT_BITS = 8 + +include ../Makefile.common diff --git a/proofs/cbmc/sign_attempt/sign_attempt_harness.c b/proofs/cbmc/sign_attempt/sign_attempt_harness.c new file mode 100644 index 000000000..c326329cd --- /dev/null +++ b/proofs/cbmc/sign_attempt/sign_attempt_harness.c @@ -0,0 +1,14 @@ +// Copyright (c) The mldsa-native project authors +// SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +#include "sign.h" + +void harness(void) +{ + uint16_t attempt; + int rc; + /* `context` is consumed by the call macro (the CBMC config has no context + * parameter), exactly as at the call sites in sign.c. */ + rc = mld_sign_attempt(attempt, context); + ((void)rc); +} diff --git a/proofs/cbmc/sign_finish/Makefile b/proofs/cbmc/sign_finish/Makefile new file mode 100644 index 000000000..0d79567f0 --- /dev/null +++ b/proofs/cbmc/sign_finish/Makefile @@ -0,0 +1,35 @@ +# Copyright (c) The mldsa-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +include ../Makefile_params.common + +HARNESS_ENTRY = harness +HARNESS_FILE = sign_finish_harness + +# This should be a unique identifier for this proof, and will appear on the +# Litani dashboard. It can be human-readable and contain spaces if you wish. +PROOF_UID = mld_sign_finish + +DEFINES += +INCLUDES += + +REMOVE_FUNCTION_BODY += +UNWINDSET += + +PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c +PROJECT_SOURCES += $(SRCDIR)/mldsa/src/sign.c + +CHECK_FUNCTION_CONTRACTS=mld_sign_hook_finish +USE_FUNCTION_CONTRACTS= + +APPLY_LOOP_CONTRACTS=on +USE_DYNAMIC_FRAMES=1 + +EXTERNAL_SAT_SOLVER= +CBMCFLAGS=--smt2 + +FUNCTION_NAME = mld_sign_hook_finish + +CBMC_OBJECT_BITS = 8 + +include ../Makefile.common diff --git a/proofs/cbmc/sign_finish/sign_finish_harness.c b/proofs/cbmc/sign_finish/sign_finish_harness.c new file mode 100644 index 000000000..d9871f8c0 --- /dev/null +++ b/proofs/cbmc/sign_finish/sign_finish_harness.c @@ -0,0 +1,12 @@ +// Copyright (c) The mldsa-native project authors +// SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +#include "sign.h" + +/* `context` is consumed by the call macro (the CBMC config has no context + * parameter), exactly as at the call sites in sign.c. */ +void harness(void) +{ + uint16_t attempt; + mld_sign_finish(attempt, context); +} diff --git a/proofs/cbmc/sign_resume/Makefile b/proofs/cbmc/sign_resume/Makefile new file mode 100644 index 000000000..b863a78fc --- /dev/null +++ b/proofs/cbmc/sign_resume/Makefile @@ -0,0 +1,35 @@ +# Copyright (c) The mldsa-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +include ../Makefile_params.common + +HARNESS_ENTRY = harness +HARNESS_FILE = sign_resume_harness + +# This should be a unique identifier for this proof, and will appear on the +# Litani dashboard. It can be human-readable and contain spaces if you wish. +PROOF_UID = mld_sign_resume + +DEFINES += +INCLUDES += + +REMOVE_FUNCTION_BODY += +UNWINDSET += + +PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c +PROJECT_SOURCES += $(SRCDIR)/mldsa/src/sign.c + +CHECK_FUNCTION_CONTRACTS=mld_sign_hook_resume +USE_FUNCTION_CONTRACTS= + +APPLY_LOOP_CONTRACTS=on +USE_DYNAMIC_FRAMES=1 + +EXTERNAL_SAT_SOLVER= +CBMCFLAGS=--smt2 + +FUNCTION_NAME = mld_sign_hook_resume + +CBMC_OBJECT_BITS = 8 + +include ../Makefile.common diff --git a/proofs/cbmc/sign_resume/sign_resume_harness.c b/proofs/cbmc/sign_resume/sign_resume_harness.c new file mode 100644 index 000000000..8ceb113d3 --- /dev/null +++ b/proofs/cbmc/sign_resume/sign_resume_harness.c @@ -0,0 +1,12 @@ +// Copyright (c) The mldsa-native project authors +// SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +#include "sign.h" + +void harness(void) +{ + /* `context` is consumed by the call macro (the CBMC config has no context + * parameter), exactly as at the call sites in sign.c. */ + uint16_t attempt = mld_sign_resume(context); + ((void)attempt); +} diff --git a/proofs/cbmc/sign_signature_internal/Makefile b/proofs/cbmc/sign_signature_internal/Makefile index e282030c1..588dfbfd9 100644 --- a/proofs/cbmc/sign_signature_internal/Makefile +++ b/proofs/cbmc/sign_signature_internal/Makefile @@ -25,6 +25,9 @@ USE_FUNCTION_CONTRACTS+=mld_H USE_FUNCTION_CONTRACTS+=mld_polyvec_matrix_expand USE_FUNCTION_CONTRACTS+=mld_attempt_signature_generation USE_FUNCTION_CONTRACTS+=mld_get_max_signing_attempts +USE_FUNCTION_CONTRACTS+=mld_sign_hook_resume +USE_FUNCTION_CONTRACTS+=mld_sign_hook_attempt +USE_FUNCTION_CONTRACTS+=mld_sign_hook_finish USE_FUNCTION_CONTRACTS+=mld_zeroize APPLY_LOOP_CONTRACTS=on diff --git a/scripts/check-contracts b/scripts/check-contracts index 716bb13de..e1dcc19b2 100755 --- a/scripts/check-contracts +++ b/scripts/check-contracts @@ -68,6 +68,10 @@ NAMESPACE_ALIASES = { "sign_verify_internal": "verify_internal", "sign_verify_pre_hash_internal": "verify_pre_hash_internal", "sign_verify_pre_hash_shake256": "verify_pre_hash_shake256", + # The signing-hook dummies are spelled via the mld_sign_{attempt,finish} + # call-site macros, which expand to mld_sign_hook_{attempt,finish}. + "sign_attempt": "sign_hook_attempt", + "sign_finish": "sign_hook_finish", } From 9cc43cb514eb8d59538a72b3ab704a0c986b147f Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Mon, 13 Jul 2026 12:50:11 +0100 Subject: [PATCH 7/9] test: Add test_sign_hook exercising the signing hooks 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 --- .github/actions/config-variations/action.yml | 25 +- .github/actions/functest/action.yml | 8 +- .github/actions/multi-functest/action.yml | 10 + .github/workflows/ci.yml | 36 ++ Makefile | 37 +- scripts/tests | 47 ++ test/acvp/acvp_mldsa.c | 73 +-- .../platform/aarch64-virt/platform.mk | 6 +- test/baremetal/platform/avr/platform.mk | 3 +- test/configs/configs.yml | 36 ++ test/mk/components.mk | 17 +- test/mk/rules.mk | 30 + test/notrandombytes/notrandombytes.c | 19 + test/notrandombytes/notrandombytes.h | 8 + test/src/decode_hex.h | 87 +++ test/src/test_sign_hook.c | 599 ++++++++++++++++++ 16 files changed, 952 insertions(+), 89 deletions(-) create mode 100644 test/src/decode_hex.h create mode 100644 test/src/test_sign_hook.c diff --git a/.github/actions/config-variations/action.yml b/.github/actions/config-variations/action.yml index 51399f746..3a8ffb154 100644 --- a/.github/actions/config-variations/action.yml +++ b/.github/actions/config-variations/action.yml @@ -33,9 +33,12 @@ runs: func: true kat: true acvp: true + sign_hook: false # Signing hooks require NO_RANDOMIZED_API, incompatible with KEYGEN_PCT opt: ${{ inputs.opt }} examples: true - extra_args: "--exclude-example basic_deterministic" + # basic_deterministic and restartable_sign set MLD_CONFIG_NO_RANDOMIZED_API, + # which is incompatible with MLD_CONFIG_KEYGEN_PCT. + extra_args: "--exclude-example basic_deterministic --exclude-example restartable_sign" - name: "REDUCE_RAM" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'reduce-ram') }} uses: ./.github/actions/multi-functest @@ -62,9 +65,12 @@ runs: kat: true acvp: true alloc: true + sign_hook: false # Signing hooks require NO_RANDOMIZED_API, incompatible with KEYGEN_PCT opt: ${{ inputs.opt }} examples: true - extra_args: "--exclude-example basic_deterministic" + # basic_deterministic and restartable_sign set MLD_CONFIG_NO_RANDOMIZED_API, + # which is incompatible with MLD_CONFIG_KEYGEN_PCT. + extra_args: "--exclude-example basic_deterministic --exclude-example restartable_sign" - name: "PCT enabled + broken" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'pct-enabled-broken') }} shell: bash @@ -107,6 +113,7 @@ runs: extra_env: 'ASAN_OPTIONS=detect_leaks=1' examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "Custom zeroization (explicit_bzero)" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'custom-zeroize') }} @@ -122,6 +129,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "Custom native capability functions (static ON)" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'native-cap-ON') }} @@ -137,6 +145,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "Custom native capability functions (static OFF)" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'native-cap-OFF') }} @@ -152,6 +161,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "Custom native capability functions (ID_AA64PFR1_EL1 detection)" if: ${{ (inputs.tests == 'all' || contains(inputs.tests, 'native-cap-ID_AA64PFR1_EL1')) && runner.os == 'Linux' && runner.arch == 'ARM64' }} @@ -167,6 +177,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "Custom native capability functions (CPUID AVX2 detection)" if: ${{ (inputs.tests == 'all' || contains(inputs.tests, 'native-cap-CPUID_AVX2')) && runner.os == 'Linux' && runner.arch == 'X64' }} @@ -182,6 +193,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "No ASM" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'no-asm') }} @@ -197,6 +209,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "Serial FIPS202 (no batched Keccak)" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'serial-fips202') }} @@ -212,6 +225,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "Custom randombytes" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'custom-randombytes') }} @@ -227,6 +241,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: false # Uses its own randombytes implementation - name: "Custom memcpy" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'custom-memcpy') }} @@ -242,6 +257,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "Custom memset" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'custom-memset') }} @@ -257,6 +273,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "Custom stdlib (memcpy + memset)" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'custom-stdlib') }} @@ -272,6 +289,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "MLD_POLY_UNIFORM_NBLOCKS=1" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'nblocks-1') }} @@ -287,6 +305,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "MLD_POLY_UNIFORM_NBLOCKS=4" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'nblocks-4') }} @@ -302,6 +321,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "MLD_POLY_UNIFORM_NBLOCKS=6" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'nblocks-6') }} @@ -317,6 +337,7 @@ runs: opt: ${{ inputs.opt }} examples: false # Some examples use a custom config themselves alloc: false # Requires custom config + sign_hook: false # Requires custom config rng_fail: true - name: "Keygen-only API" if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'keygen-only') }} diff --git a/.github/actions/functest/action.yml b/.github/actions/functest/action.yml index 1dd9f0cb4..ad72ec5da 100644 --- a/.github/actions/functest/action.yml +++ b/.github/actions/functest/action.yml @@ -63,6 +63,9 @@ inputs: alloc: description: Determine whether to run alloc tests or not default: "true" + sign_hook: + description: Determine whether to run sign hook tests or not + default: "true" rng_fail: description: Determine whether to run rng fail tests or not default: "true" @@ -88,6 +91,7 @@ runs: echo EXAMPLES="${{ inputs.examples == 'true' && 'examples' || 'no-examples' }}" >> $GITHUB_ENV echo STACK="${{ inputs.stack == 'true' && 'stack' || 'no-stack' }}" >> $GITHUB_ENV echo ALLOC="${{ inputs.alloc == 'true' && 'alloc' || 'no-alloc' }}" >> $GITHUB_ENV + echo SIGN_HOOK="${{ inputs.sign_hook == 'true' && 'sign-hook' || 'no-sign-hook' }}" >> $GITHUB_ENV echo RNGFAIL="${{ inputs.rng_fail == 'true' && 'rng-fail' || 'no-rng-fail' }}" >> $GITHUB_ENV - name: Setup nix uses: ./.github/actions/setup-shell @@ -119,13 +123,13 @@ runs: - $(python3 --version) - $(${{ inputs.cross_prefix }}${CC} --version | grep -m1 "") EOF - - name: ${{ env.MODE }} ${{ inputs.opt }} tests (${{ env.FUNC }}, ${{ env.KAT }}, ${{ env.EXAMPLES }}, ${{ env.STACK }}, ${{ env.UNIT }}, ${{ env.ALLOC }}, ${{ env.RNGFAIL }}) + - name: ${{ env.MODE }} ${{ inputs.opt }} tests (${{ env.FUNC }}, ${{ env.KAT }}, ${{ env.EXAMPLES }}, ${{ env.STACK }}, ${{ env.UNIT }}, ${{ env.ALLOC }}, ${{ env.SIGN_HOOK }}, ${{ env.RNGFAIL }}) shell: ${{ env.SHELL }} env: GH_TOKEN: ${{ inputs.gh_token }} run: | make clean - ${{ inputs.extra_env }} ./scripts/tests all ${{ inputs.check_namespace == 'true' && '--check-namespace' || ''}} --exec-wrapper="${{ inputs.exec_wrapper }}" --cross-prefix="${{ inputs.cross_prefix }}" --cflags="${{ inputs.cflags }}" --ldflags="${{ inputs.ldflags }}" --opt=${{ inputs.opt }} --${{ env.FUNC }} --${{ env.KAT }} --${{ env.ACVP }} --${{ env.WYCHEPROOF }} --${{ env.EXAMPLES }} --${{ env.STACK }} --${{ env.UNIT }} --${{ env.ALLOC }} --${{ env.RNGFAIL }} -v ${{ inputs.extra_args }} + ${{ inputs.extra_env }} ./scripts/tests all ${{ inputs.check_namespace == 'true' && '--check-namespace' || ''}} --exec-wrapper="${{ inputs.exec_wrapper }}" --cross-prefix="${{ inputs.cross_prefix }}" --cflags="${{ inputs.cflags }}" --ldflags="${{ inputs.ldflags }}" --opt=${{ inputs.opt }} --${{ env.FUNC }} --${{ env.KAT }} --${{ env.ACVP }} --${{ env.WYCHEPROOF }} --${{ env.EXAMPLES }} --${{ env.STACK }} --${{ env.UNIT }} --${{ env.ALLOC }} --${{ env.SIGN_HOOK }} --${{ env.RNGFAIL }} -v ${{ inputs.extra_args }} - name: Post ${{ env.MODE }} Tests shell: ${{ env.SHELL }} if: success() || failure() diff --git a/.github/actions/multi-functest/action.yml b/.github/actions/multi-functest/action.yml index 74e8194c8..5f6a578ba 100644 --- a/.github/actions/multi-functest/action.yml +++ b/.github/actions/multi-functest/action.yml @@ -63,6 +63,9 @@ inputs: alloc: description: Determine whether to run alloc tests or not default: "true" + sign_hook: + description: Determine whether to run sign hook tests or not + default: "true" rng_fail: description: Determine whether to run rng fail tests or not default: "true" @@ -96,6 +99,7 @@ runs: check_namespace: ${{ inputs.check_namespace }} stack: ${{ inputs.stack }} alloc: ${{ inputs.alloc }} + sign_hook: ${{ inputs.sign_hook }} rng_fail: ${{ inputs.rng_fail }} extra_args: ${{ inputs.extra_args }} extra_env: ${{ inputs.extra_env }} @@ -122,6 +126,7 @@ runs: check_namespace: ${{ inputs.check_namespace }} stack: ${{ inputs.stack }} alloc: ${{ inputs.alloc }} + sign_hook: ${{ inputs.sign_hook }} rng_fail: ${{ inputs.rng_fail }} extra_args: ${{ inputs.extra_args }} extra_env: ${{ inputs.extra_env }} @@ -148,6 +153,7 @@ runs: check_namespace: ${{ inputs.check_namespace }} stack: ${{ inputs.stack }} alloc: ${{ inputs.alloc }} + sign_hook: ${{ inputs.sign_hook }} rng_fail: ${{ inputs.rng_fail }} extra_args: ${{ inputs.extra_args }} extra_env: ${{ inputs.extra_env }} @@ -174,6 +180,7 @@ runs: check_namespace: ${{ inputs.check_namespace }} stack: ${{ inputs.stack }} alloc: ${{ inputs.alloc }} + sign_hook: ${{ inputs.sign_hook }} rng_fail: ${{ inputs.rng_fail }} extra_args: ${{ inputs.extra_args }} extra_env: ${{ inputs.extra_env }} @@ -200,6 +207,7 @@ runs: check_namespace: ${{ inputs.check_namespace }} stack: ${{ inputs.stack }} alloc: ${{ inputs.alloc }} + sign_hook: ${{ inputs.sign_hook }} rng_fail: ${{ inputs.rng_fail }} extra_args: ${{ inputs.extra_args }} extra_env: ${{ inputs.extra_env }} @@ -226,6 +234,7 @@ runs: check_namespace: ${{ inputs.check_namespace }} stack: ${{ inputs.stack }} alloc: ${{ inputs.alloc }} + sign_hook: ${{ inputs.sign_hook }} rng_fail: ${{ inputs.rng_fail }} extra_args: ${{ inputs.extra_args }} extra_env: ${{ inputs.extra_env }} @@ -252,6 +261,7 @@ runs: check_namespace: ${{ inputs.check_namespace }} stack: ${{ inputs.stack }} alloc: ${{ inputs.alloc }} + sign_hook: ${{ inputs.sign_hook }} rng_fail: ${{ inputs.rng_fail }} extra_args: ${{ inputs.extra_args }} extra_env: ${{ inputs.extra_env }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccb06bcc7..6576aadba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -435,11 +435,47 @@ jobs: stack: false unit: true alloc: false + sign_hook: false # Requires custom config rng_fail: false check_namespace: false # Disable AArch64 SHA3 extension: valgrind cannot emulate it extra_env: "MK_COMPILER_SUPPORTS_SHA3=0" exec_wrapper: "valgrind --error-exitcode=1" + sign_hook_distribution: + name: "Sign-hook attempt distribution (${{ matrix.target.name }}, ML-DSA-${{ matrix.level }})" + strategy: + fail-fast: false + # One matrix entry per (architecture, parameter set): each level runs on + # its own runner so the three ~9-minute 5M-signature runs proceed in + # parallel rather than sequentially. + matrix: + external: + - ${{ github.repository_owner != 'pq-code-package' }} + target: + - runner: ubuntu-latest + name: x86_64 + - runner: ubuntu-24.04-arm + name: aarch64 + level: [44, 65, 87] + exclude: + - external: true + runs-on: ${{ matrix.target.runner }} + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + # Fresh random seed each run: the test draws fresh keys and signing + # randomness from it, so this samples the attempts-per-signature + # distribution anew every time. The seed is printed by the test, so a + # failure is reproducible by re-running with the same SIGN_HOOK_SEED. + - name: Generate random seed + run: echo "SIGN_HOOK_SEED=seed=$(openssl rand -hex 32)" >> "$GITHUB_ENV" + - uses: ./.github/actions/setup-shell + with: + gh_token: ${{ secrets.GITHUB_TOKEN }} + nix-shell: 'ci' + # At 5,000,000 signatures, a deviation by more than +-0.5% (the 5 + # per-mille tolerance) is expected with probability < 10^{-30}. + script: | + CFLAGS=\"-DMLD_SIGN_HOOK_DIST_ITERATIONS=5000000 -DMLD_SIGN_HOOK_RATIO_TOL_PERMILLE=5\" make run_sign_hook_${{ matrix.level }} OPT=1 AUTO=1 -j$(nproc) config_variations: name: "Config: ${{ matrix.test.name }} (${{ matrix.target.name }})" strategy: diff --git a/Makefile b/Makefile index cdfc6db7c..6a929db9a 100644 --- a/Makefile +++ b/Makefile @@ -2,14 +2,14 @@ # Copyright (c) The mldsa-native project authors # SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT -.PHONY: func kat acvp wycheproof stack unit alloc rng_fail \ - func_44 kat_44 acvp_44 wycheproof_44 stack_44 unit_44 alloc_44 rng_fail_44 \ - func_65 kat_65 acvp_65 wycheproof_65 stack_65 unit_65 alloc_65 rng_fail_65 \ - func_87 kat_87 acvp_87 wycheproof_87 stack_87 unit_87 alloc_87 rng_fail_87 \ - run_func run_kat run_acvp run_wycheproof run_stack run_unit run_alloc run_rng_fail \ - run_func_44 run_kat_44 run_stack_44 run_unit_44 run_alloc_44 run_rng_fail_44 \ - run_func_65 run_kat_65 run_stack_65 run_unit_65 run_alloc_65 run_rng_fail_65 \ - run_func_87 run_kat_87 run_stack_87 run_unit_87 run_alloc_87 run_rng_fail_87 \ +.PHONY: func kat acvp wycheproof stack unit alloc sign_hook rng_fail \ + func_44 kat_44 acvp_44 wycheproof_44 stack_44 unit_44 alloc_44 sign_hook_44 rng_fail_44 \ + func_65 kat_65 acvp_65 wycheproof_65 stack_65 unit_65 alloc_65 sign_hook_65 rng_fail_65 \ + func_87 kat_87 acvp_87 wycheproof_87 stack_87 unit_87 alloc_87 sign_hook_87 rng_fail_87 \ + run_func run_kat run_acvp run_wycheproof run_stack run_unit run_alloc run_sign_hook run_rng_fail \ + run_func_44 run_kat_44 run_stack_44 run_unit_44 run_alloc_44 run_sign_hook_44 run_rng_fail_44 \ + run_func_65 run_kat_65 run_stack_65 run_unit_65 run_alloc_65 run_sign_hook_65 run_rng_fail_65 \ + run_func_87 run_kat_87 run_stack_87 run_unit_87 run_alloc_87 run_sign_hook_87 run_rng_fail_87 \ bench_44 bench_65 bench_87 bench \ run_bench_44 run_bench_65 run_bench_87 run_bench \ bench_components_44 bench_components_65 bench_components_87 bench_components \ @@ -47,7 +47,7 @@ quickcheck: test build: func kat acvp wycheproof $(Q)echo " Everything builds fine!" -test: run_kat run_func run_acvp run_wycheproof run_unit run_alloc run_rng_fail run_abicheck +test: run_kat run_func run_acvp run_wycheproof run_unit run_alloc run_sign_hook run_rng_fail run_abicheck $(Q)echo " Everything checks fine!" run_kat_44: kat_44 @@ -161,6 +161,25 @@ run_alloc_87: alloc_87 $(W) $(MLDSA87_DIR)/bin/test_alloc87 run_alloc: run_alloc_44 run_alloc_65 run_alloc_87 +sign_hook_44: $(MLDSA44_DIR)/bin/test_sign_hook44 + $(Q)echo " SIGN_HOOK ML-DSA-44: $^" +sign_hook_65: $(MLDSA65_DIR)/bin/test_sign_hook65 + $(Q)echo " SIGN_HOOK ML-DSA-65: $^" +sign_hook_87: $(MLDSA87_DIR)/bin/test_sign_hook87 + $(Q)echo " SIGN_HOOK ML-DSA-87: $^" +sign_hook: sign_hook_44 sign_hook_65 sign_hook_87 + +# SIGN_HOOK_SEED (e.g. "seed=<64 hex chars>") is forwarded to the test as an +# optional PRNG seed; when unset it expands to nothing, so the test uses its +# deterministic default seed. +run_sign_hook_44: sign_hook_44 + $(W) $(MLDSA44_DIR)/bin/test_sign_hook44 $(SIGN_HOOK_SEED) +run_sign_hook_65: sign_hook_65 + $(W) $(MLDSA65_DIR)/bin/test_sign_hook65 $(SIGN_HOOK_SEED) +run_sign_hook_87: sign_hook_87 + $(W) $(MLDSA87_DIR)/bin/test_sign_hook87 $(SIGN_HOOK_SEED) +run_sign_hook: run_sign_hook_44 run_sign_hook_65 run_sign_hook_87 + rng_fail_44: $(MLDSA44_DIR)/bin/test_rng_fail44 $(Q)echo " RNG_FAIL ML-DSA-44: $^" rng_fail_65: $(MLDSA65_DIR)/bin/test_rng_fail65 diff --git a/scripts/tests b/scripts/tests index cb56ea3b5..62fb7e73b 100755 --- a/scripts/tests +++ b/scripts/tests @@ -217,6 +217,7 @@ class TEST_TYPES(Enum): RNG_FAIL = 22 WYCHEPROOF = 23 ABICHECK = 24 + SIGN_HOOK = 25 def is_benchmark(self): return self in [TEST_TYPES.BENCH, TEST_TYPES.BENCH_COMPONENTS] @@ -298,6 +299,8 @@ class TEST_TYPES(Enum): return "Unit Test" if self == TEST_TYPES.ALLOC: return "Alloc Test" + if self == TEST_TYPES.SIGN_HOOK: + return "Sign Hook Test" if self == TEST_TYPES.RNG_FAIL: return "RNG Failure Test" if self == TEST_TYPES.ABICHECK: @@ -375,6 +378,8 @@ class TEST_TYPES(Enum): return "unit" if self == TEST_TYPES.ALLOC: return "alloc" + if self == TEST_TYPES.SIGN_HOOK: + return "sign_hook" if self == TEST_TYPES.RNG_FAIL: return "rng_fail" if self == TEST_TYPES.ABICHECK: @@ -691,6 +696,19 @@ class Tests: self.check_fail() + def sign_hook(self): + def _sign_hook(opt): + self._compile_schemes(TEST_TYPES.SIGN_HOOK, opt) + if self.args.run: + self._run_schemes(TEST_TYPES.SIGN_HOOK, opt) + + if self.do_no_opt(): + _sign_hook(False) + if self.do_opt(): + _sign_hook(True) + + self.check_fail() + def rng_fail(self): def _rng_fail(opt): self._compile_schemes(TEST_TYPES.RNG_FAIL, opt) @@ -866,6 +884,7 @@ class Tests: stack = self.args.stack unit = self.args.unit alloc = self.args.alloc + sign_hook = self.args.sign_hook rng_fail = self.args.rng_fail abicheck = self.args.abicheck @@ -884,6 +903,8 @@ class Tests: self._compile_schemes(TEST_TYPES.UNIT, opt) if alloc is True: self._compile_schemes(TEST_TYPES.ALLOC, opt) + if sign_hook is True: + self._compile_schemes(TEST_TYPES.SIGN_HOOK, opt) if rng_fail is True: self._compile_schemes(TEST_TYPES.RNG_FAIL, opt) if abicheck is True and opt: @@ -915,6 +936,8 @@ class Tests: self._run_schemes(TEST_TYPES.UNIT, opt) if alloc is True: self._run_schemes(TEST_TYPES.ALLOC, opt) + if sign_hook is True: + self._run_schemes(TEST_TYPES.SIGN_HOOK, opt) if rng_fail is True: self._run_schemes(TEST_TYPES.RNG_FAIL, opt) if abicheck is True and opt: @@ -1331,6 +1354,21 @@ def cli(): help="Do not run alloc tests", ) + sign_hook_group = all_parser.add_mutually_exclusive_group() + sign_hook_group.add_argument( + "--sign-hook", + action="store_true", + dest="sign_hook", + help="Run sign hook tests", + default=True, + ) + sign_hook_group.add_argument( + "--no-sign-hook", + action="store_false", + dest="sign_hook", + help="Do not run sign hook tests", + ) + rng_fail_group = all_parser.add_mutually_exclusive_group() rng_fail_group.add_argument( "--rng-fail", @@ -1593,6 +1631,13 @@ def cli(): parents=[common_parser], ) + # sign_hook arguments + cmd_subparsers.add_parser( + "sign_hook", + help="Run the sign hook tests for all parameter sets", + parents=[common_parser], + ) + # rng_fail arguments cmd_subparsers.add_parser( "rng_fail", @@ -1642,6 +1687,8 @@ def cli(): Tests(args).size() elif args.cmd == "alloc": Tests(args).alloc() + elif args.cmd == "sign_hook": + Tests(args).sign_hook() elif args.cmd == "rng_fail": Tests(args).rng_fail() elif args.cmd == "abicheck": diff --git a/test/acvp/acvp_mldsa.c b/test/acvp/acvp_mldsa.c index d6a708d43..24973def3 100644 --- a/test/acvp/acvp_mldsa.c +++ b/test/acvp/acvp_mldsa.c @@ -11,6 +11,7 @@ #include "mldsa_native.h" +#include "../src/decode_hex.h" #include "../src/test_namespace.h" #define USAGE "acvp_mldsa{lvl} [keyGen|sigGen|sigVer] {test specific arguments}" @@ -97,78 +98,6 @@ typedef enum sigVerPreHashShake256 } acvp_mode; -/* Decode hex character [0-9A-Fa-f] into 0-15 */ -static unsigned char decode_hex_char(char hex) -{ - if (hex >= '0' && hex <= '9') - { - return (unsigned char)(hex - '0'); - } - else if (hex >= 'A' && hex <= 'F') - { - return (unsigned char)(10 + (unsigned char)(hex - 'A')); - } - else if (hex >= 'a' && hex <= 'f') - { - return (unsigned char)(10 + (unsigned char)(hex - 'a')); - } - else - { - return 0xFF; - } -} - -/* Decode the value of a `prefix=HEX` argument in place, overwriting the - * hex encoding with the raw bytes. Returns a pointer to the decoded bytes - * inside the argument string, or NULL on parse failure. */ -static unsigned char *decode_hex(const char *prefix, size_t out_len, char *hex) -{ - size_t i; - const char *arg = hex; - size_t hex_len = strlen(hex); - size_t prefix_len = strlen(prefix); - unsigned char *out; - - /* - * Check that hex starts with `prefix=` - * Use memcmp, not strcmp - */ - if (hex_len < prefix_len + 1 || memcmp(prefix, hex, prefix_len) != 0 || - hex[prefix_len] != '=') - { - goto hex_usage; - } - - hex += prefix_len + 1; - hex_len -= prefix_len + 1; - - if (hex_len != 2 * out_len) - { - goto hex_usage; - } - - out = (unsigned char *)hex; - for (i = 0; i < out_len; i++) - { - unsigned hex0 = decode_hex_char(hex[2 * i]); - unsigned hex1 = decode_hex_char(hex[2 * i + 1]); - if (hex0 == 0xFF || hex1 == 0xFF) - { - goto hex_usage; - } - - out[i] = ((hex0 << 4) | hex1) & 0XFF; - } - - return out; - -hex_usage: - fprintf(stderr, - "Argument %s invalid: Expected argument of the form '%s=HEX' with " - "HEX being a hex encoding of %u bytes\n", - arg, prefix, (unsigned)out_len); - return NULL; -} #if !defined(MLD_CONFIG_NO_SIGN_API) || !defined(MLD_CONFIG_NO_VERIFY_API) diff --git a/test/baremetal/platform/aarch64-virt/platform.mk b/test/baremetal/platform/aarch64-virt/platform.mk index 4376a6026..79afb7e9b 100644 --- a/test/baremetal/platform/aarch64-virt/platform.mk +++ b/test/baremetal/platform/aarch64-virt/platform.mk @@ -7,8 +7,10 @@ PLATFORM_PATH:=test/baremetal/platform/aarch64-virt CROSS_PREFIX=aarch64-none-elf- CC=gcc -# Reduce iterations -- bare-metal semihosting I/O is slow -CFLAGS += -DNTESTS=3 +# Reduce iterations -- bare-metal semihosting I/O is slow. The sign-hook +# distribution sample count defaults to 2000 (independent of NTESTS); override +# it down too, as 2000 signings under emulation would be prohibitively slow. +CFLAGS += -DNTESTS=3 -DMLD_SIGN_HOOK_DIST_ITERATIONS=3 CFLAGS += \ -O3 \ diff --git a/test/baremetal/platform/avr/platform.mk b/test/baremetal/platform/avr/platform.mk index cb6f8a1e4..9c226bae5 100644 --- a/test/baremetal/platform/avr/platform.mk +++ b/test/baremetal/platform/avr/platform.mk @@ -25,7 +25,8 @@ CFLAGS += \ -DAVR_PLATFORM \ -fno-fat-lto-objects \ -DMLD_CONFIG_REDUCE_RAM \ - -DNTESTS=1 + -DNTESTS=1 \ + -DMLD_SIGN_HOOK_DIST_ITERATIONS=1 # Trap on UB; needs -fno-wrapv as avr-gcc defaults to -fwrapv. # On UB, abort() in avr_wrapper.c is called. diff --git a/test/configs/configs.yml b/test/configs/configs.yml index e1b364061..352bfe465 100644 --- a/test/configs/configs.yml +++ b/test/configs/configs.yml @@ -467,6 +467,42 @@ configs: MLD_CONFIG_FILE: comment: "/* No need to set this -- we _are_ already in a custom config */" + - path: test/configs/test_sign_hook_config.h + description: "Restartable signing driven by the SIGN_HOOK_{RESUME,ATTEMPT,FINISH} hooks" + defines: + MLD_CONFIG_NAMESPACE_PREFIX: mld + # Resuming is only sound with the randomized API disabled: the caller + # drives signing via the deterministic internal API with fixed `rnd`. + MLD_CONFIG_NO_RANDOMIZED_API: true + MLD_CONFIG_CONTEXT_PARAMETER: true + MLD_CONFIG_CONTEXT_PARAMETER_TYPE: + content: | + #if !defined(__ASSEMBLER__) + #include + /* The context type and the hook implementations live in + * test/src/test_sign_hook.c; here we only forward-declare them. */ + struct test_sign_hook_ctx; /* Forward declaration */ + #endif /* !__ASSEMBLER__ */ + #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE struct test_sign_hook_ctx * + MLD_CONFIG_SIGN_HOOK_RESUME: + content: | + /* Enable all three signing hooks. Since MLD_CONFIG_CONTEXT_PARAMETER + * is set, the context is the last argument of each. The hooks are + * implemented in test/src/test_sign_hook.c. */ + #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + struct test_sign_hook_ctx; /* Forward declaration */ + uint16_t mld_sign_hook_resume(struct test_sign_hook_ctx *context); + int mld_sign_hook_attempt(uint16_t attempt, + struct test_sign_hook_ctx *context); + void mld_sign_hook_finish(uint16_t attempt, + struct test_sign_hook_ctx *context); + #endif /* !__ASSEMBLER__ */ + MLD_CONFIG_FILE: + comment: "/* No need to set this -- we _are_ already in a custom config */" + - path: proofs/cbmc/mldsa_native_config_cbmc.h description: "mldsa-native configuration used for CBMC proofs" defines: diff --git a/test/mk/components.mk b/test/mk/components.mk index 52ba35b2e..b61a8185a 100644 --- a/test/mk/components.mk +++ b/test/mk/components.mk @@ -27,8 +27,9 @@ WYCHEPROOF_TESTS = wycheproof_mldsa BENCH_TESTS = bench_mldsa bench_components_mldsa UNIT_TESTS = test_unit ALLOC_TESTS = test_alloc +SIGN_HOOK_TESTS = test_sign_hook RNG_FAIL_TESTS = test_rng_fail -ALL_TESTS = $(BASIC_TESTS) $(ACVP_TESTS) $(WYCHEPROOF_TESTS) $(BENCH_TESTS) $(UNIT_TESTS) $(ALLOC_TESTS) $(RNG_FAIL_TESTS) +ALL_TESTS = $(BASIC_TESTS) $(ACVP_TESTS) $(WYCHEPROOF_TESTS) $(BENCH_TESTS) $(UNIT_TESTS) $(ALLOC_TESTS) $(SIGN_HOOK_TESTS) $(RNG_FAIL_TESTS) MLDSA44_DIR = $(BUILD_DIR)/mldsa44 MLDSA65_DIR = $(BUILD_DIR)/mldsa65 @@ -46,6 +47,10 @@ MLDSA44_ALLOC_OBJS = $(call MAKE_OBJS,$(MLDSA44_DIR)/alloc,$(LIB_SRCS)) MLDSA65_ALLOC_OBJS = $(call MAKE_OBJS,$(MLDSA65_DIR)/alloc,$(LIB_SRCS)) MLDSA87_ALLOC_OBJS = $(call MAKE_OBJS,$(MLDSA87_DIR)/alloc,$(LIB_SRCS)) +MLDSA44_SIGN_HOOK_OBJS = $(call MAKE_OBJS,$(MLDSA44_DIR)/sign_hook,$(SOURCES) $(FIPS202_SRCS)) +MLDSA65_SIGN_HOOK_OBJS = $(call MAKE_OBJS,$(MLDSA65_DIR)/sign_hook,$(SOURCES) $(FIPS202_SRCS)) +MLDSA87_SIGN_HOOK_OBJS = $(call MAKE_OBJS,$(MLDSA87_DIR)/sign_hook,$(SOURCES) $(FIPS202_SRCS)) + CFLAGS += -Imldsa $(BUILD_DIR)/libmldsa44.a: $(MLDSA44_OBJS) @@ -60,6 +65,11 @@ $(BUILD_DIR)/libmldsa44_alloc.a: $(MLDSA44_ALLOC_OBJS) $(BUILD_DIR)/libmldsa65_alloc.a: $(MLDSA65_ALLOC_OBJS) $(BUILD_DIR)/libmldsa87_alloc.a: $(MLDSA87_ALLOC_OBJS) +# Sign-hook test libraries with the sign-hook config +$(BUILD_DIR)/libmldsa44_sign_hook.a: $(MLDSA44_SIGN_HOOK_OBJS) +$(BUILD_DIR)/libmldsa65_sign_hook.a: $(MLDSA65_SIGN_HOOK_OBJS) +$(BUILD_DIR)/libmldsa87_sign_hook.a: $(MLDSA87_SIGN_HOOK_OBJS) + $(BUILD_DIR)/libmldsa.a: $(MLDSA44_OBJS) $(MLDSA65_OBJS) $(MLDSA87_OBJS) # Generic CFLAGS @@ -96,6 +106,10 @@ $(MLDSA44_DIR)/bin/test_alloc44: CFLAGS += -DMLD_CONFIG_FILE=\"../test/configs/t $(MLDSA65_DIR)/bin/test_alloc65: CFLAGS += -DMLD_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" $(MLDSA87_DIR)/bin/test_alloc87: CFLAGS += -DMLD_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" +$(MLDSA44_DIR)/bin/test_sign_hook44: CFLAGS += -DMLD_CONFIG_FILE=\"../test/configs/test_sign_hook_config.h\" +$(MLDSA65_DIR)/bin/test_sign_hook65: CFLAGS += -DMLD_CONFIG_FILE=\"../test/configs/test_sign_hook_config.h\" +$(MLDSA87_DIR)/bin/test_sign_hook87: CFLAGS += -DMLD_CONFIG_FILE=\"../test/configs/test_sign_hook_config.h\" + define ADD_SOURCE # Record this binary's test sources -- entrypoint test/$(3)/$(2).c plus extras # $(4) -- in the per-binary TEST_SRCS. A custom build (CUSTOM_BUILD, see @@ -123,6 +137,7 @@ $(foreach scheme,mldsa44 mldsa65 mldsa87, \ $(eval $(call ADD_SOURCE,$(scheme),test_rng_fail,src)) \ $(eval $(call ADD_SOURCE,$(scheme),test_unit,src,,_unit)) \ $(eval $(call ADD_SOURCE,$(scheme),test_alloc,src,,_alloc)) \ + $(eval $(call ADD_SOURCE,$(scheme),test_sign_hook,src,,_sign_hook)) \ ) # The set of test binaries, per parameter set. diff --git a/test/mk/rules.mk b/test/mk/rules.mk index 856768eaf..835cbfc32 100644 --- a/test/mk/rules.mk +++ b/test/mk/rules.mk @@ -117,6 +117,36 @@ $(BUILD_DIR)/mldsa87/alloc/%.S.o: %.S $(CONFIG) $(Q)[ -d $(@D) ] || mkdir -p $(@D) $(Q)$(CC) -c -o $@ $(CFLAGS) $< +$(BUILD_DIR)/mldsa44/sign_hook/%.c.o: %.c $(CONFIG) + $(Q)echo " CC $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/mldsa44/sign_hook/%.S.o: %.S $(CONFIG) + $(Q)echo " AS $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/mldsa65/sign_hook/%.c.o: %.c $(CONFIG) + $(Q)echo " CC $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/mldsa65/sign_hook/%.S.o: %.S $(CONFIG) + $(Q)echo " AS $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/mldsa87/sign_hook/%.c.o: %.c $(CONFIG) + $(Q)echo " CC $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + +$(BUILD_DIR)/mldsa87/sign_hook/%.S.o: %.S $(CONFIG) + $(Q)echo " AS $@" + $(Q)[ -d $(@D) ] || mkdir -p $(@D) + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + $(BUILD_DIR)/abicheck/bin/%: $(CONFIG) $(Q)echo " LD $@" $(Q)[ -d $(@D) ] || mkdir -p $(@D) diff --git a/test/notrandombytes/notrandombytes.c b/test/notrandombytes/notrandombytes.c index ef5a423a9..f56b5e444 100644 --- a/test/notrandombytes/notrandombytes.c +++ b/test/notrandombytes/notrandombytes.c @@ -44,6 +44,25 @@ void randombytes_reset(void) outleft = 0; } +void randombytes_seed(const uint8_t *s, size_t len) +{ + size_t i; + /* Fold the supplied bytes into the seed words, little-endian, so the same + * seed yields the same stream on any host. At most sizeof(seed) bytes are + * consumed; leftover seed words keep their default value. */ + if (len > sizeof(seed)) + { + len = sizeof(seed); + } + for (i = 0; i < len; ++i) + { + uint32_t byte = s[i]; + seed[i / 4] = (seed[i / 4] & ~((uint32_t)0xFFu << (8 * (i % 4)))) | + (byte << (8 * (i % 4))); + } + randombytes_reset(); +} + #define ROTATE(x, b) (((x) << (b)) | ((x) >> (32 - (b)))) #define MUSH(i, b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x, b)); diff --git a/test/notrandombytes/notrandombytes.h b/test/notrandombytes/notrandombytes.h index bdf978792..94caa12e3 100644 --- a/test/notrandombytes/notrandombytes.h +++ b/test/notrandombytes/notrandombytes.h @@ -29,6 +29,14 @@ */ void randombytes_reset(void); + +/** + * Reseed the (testing-only) PRNG. Folds up to the full 128-byte SURF seed from + * `s`, little-endian per 32-bit word (host-independent stream); leftover words + * keep their default. len == 0 leaves the default (pi-digit) seed. + */ +void randombytes_seed(const uint8_t *s, size_t len); + int randombytes(uint8_t *buf, size_t n); #endif /* !NOTRANDOMBYTES_H */ diff --git a/test/src/decode_hex.h b/test/src/decode_hex.h new file mode 100644 index 000000000..058749640 --- /dev/null +++ b/test/src/decode_hex.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) The mldsa-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ +#ifndef MLD_TEST_DECODE_HEX_H +#define MLD_TEST_DECODE_HEX_H + +#include +#include +#include + +/* Decode hex character [0-9A-Fa-f] into 0-15. Returns 0xFF for any other + * character: a valid nibble is 0-15, so 0xFF is an out-of-band value that never + * collides with a real digit and thus serves as the "not hex" sentinel. */ +static unsigned char decode_hex_char(char hex) +{ + if (hex >= '0' && hex <= '9') + { + return (unsigned char)(hex - '0'); + } + else if (hex >= 'A' && hex <= 'F') + { + return (unsigned char)(10 + (unsigned char)(hex - 'A')); + } + else if (hex >= 'a' && hex <= 'f') + { + return (unsigned char)(10 + (unsigned char)(hex - 'a')); + } + else + { + return 0xFF; + } +} + +/* Decode the value of a `prefix=HEX` argument in place, overwriting the + * hex encoding with the raw bytes. Returns a pointer to the decoded bytes + * inside the argument string, or NULL on parse failure. */ +static unsigned char *decode_hex(const char *prefix, size_t out_len, char *hex) +{ + size_t i; + const char *arg = hex; + size_t hex_len = strlen(hex); + size_t prefix_len = strlen(prefix); + unsigned char *out; + + /* + * Check that hex starts with `prefix=` + * Use memcmp, not strcmp + */ + if (hex_len < prefix_len + 1 || memcmp(prefix, hex, prefix_len) != 0 || + hex[prefix_len] != '=') + { + goto hex_usage; + } + + hex += prefix_len + 1; + hex_len -= prefix_len + 1; + + if (hex_len != 2 * out_len) + { + goto hex_usage; + } + + out = (unsigned char *)hex; + for (i = 0; i < out_len; i++) + { + unsigned hex0 = decode_hex_char(hex[2 * i]); + unsigned hex1 = decode_hex_char(hex[2 * i + 1]); + if (hex0 == 0xFF || hex1 == 0xFF) + { + goto hex_usage; + } + + out[i] = ((hex0 << 4) | hex1) & 0XFF; + } + + return out; + +hex_usage: + fprintf(stderr, + "Argument %s invalid: Expected argument of the form '%s=HEX' with " + "HEX being a hex encoding of %u bytes\n", + arg, prefix, (unsigned)out_len); + return NULL; +} + +#endif /* !MLD_TEST_DECODE_HEX_H */ diff --git a/test/src/test_sign_hook.c b/test/src/test_sign_hook.c new file mode 100644 index 000000000..904584a6c --- /dev/null +++ b/test/src/test_sign_hook.c @@ -0,0 +1,599 @@ +/* + * Copyright (c) The mldsa-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* References + * ========== + * + * - [FIPS204] + * FIPS 204 Module-Lattice-Based Digital Signature Standard + * National Institute of Standards and Technology + * https://csrc.nist.gov/pubs/fips/204/final + */ + +#include +#include +#include +#include + +/* Expose the deterministic internal signing API and struct test_sign_hook_ctx + * (declared by the custom config in MLD_CONFIG_CONTEXT_PARAMETER_TYPE). */ +#define MLD_BUILD_INTERNAL +#include "../../mldsa/mldsa_native.h" +#include "../notrandombytes/notrandombytes.h" +#include "expected_test_vectors.h" + +#ifndef NTESTS +#define NTESTS 1000 +#endif + +/* Number of randomized iterations in test_restartable. Overridable from the + * build for longer/shorter runs. */ +#ifndef MLD_SIGN_HOOK_ITERATIONS +/* Even if NTESTS < 10, we want to run the test at least once; so round up. */ +#define MLD_SIGN_HOOK_ITERATIONS (((NTESTS) + 9) / 10) +#endif + +/* Number of one-shot signing operations in test_distribution. Overridable from + * the build; bump this up for a more accurate attempts distribution. Kept + * independent of NTESTS so the distribution check has a fixed sample budget + * (see MLD_SIGN_HOOK_MIN_CHECK_N for the resulting tolerance and confidence). + * + * Targets where signing is expensive (e.g. full-system emulation) override this + * down via -DMLD_SIGN_HOOK_DIST_ITERATIONS. */ +#ifndef MLD_SIGN_HOOK_DIST_ITERATIONS +#define MLD_SIGN_HOOK_DIST_ITERATIONS 2000 +#endif + +/* Expected mean number of signing attempts per signature, in thousandths (i.e. + * the true mean times 1000, kept integer to avoid floating point). ML-DSA + * signing is rejection sampling, so this is the mean 1/p of a geometric + * distribution and is parameter-set dependent. + * + * These values are obtained empirically: the mean over 3 x 100,000,000 + * signatures, each with a fresh key (so the estimate is key-averaged, not tied + * to one key), which pins the mean to +-0.001; an independent BoringSSL + * measurement agrees to within 0.15%. + * + * They exceed the expected repetitions of 4.25 / 5.10 / 3.85 tabulated in + * @[FIPS204, Table 1], because that figure models only the first-stage + * rejection (the ||z|| and ||r0|| low-bits checks) and omits the second-stage + * @[FIPS204, Algorithm 7, line 28] checks (the ||ct0|| norm and the hint weight + * <= omega). */ +#if MLD_CONFIG_PARAMETER_SET == 44 +#define MLD_SIGN_HOOK_EXPECTED_RATIO_X1000 4361 +#elif MLD_CONFIG_PARAMETER_SET == 65 +#define MLD_SIGN_HOOK_EXPECTED_RATIO_X1000 5138 +#elif MLD_CONFIG_PARAMETER_SET == 87 +#define MLD_SIGN_HOOK_EXPECTED_RATIO_X1000 3906 +#endif + +/* Signature size for the configured parameter set. */ +#define MLDSA_SIG_BYTES MLDSA_BYTES(MLD_CONFIG_PARAMETER_SET) + +/* The distribution test only checks the measured attempts/signature ratio + * against the expected mean once it has enough samples for the check to be + * meaningful. + * + * At N = 2000, the relative standard error is ~2% (widest level), so the + * +-10% (100 per-mille) tolerance is a ~5-sigma band: a healthy implementation + * exceeds it (a spurious failure) with probability ~10^{-6} per level. + * + * Dedicated runs can raise N and lower MLD_SIGN_HOOK_RATIO_TOL_PERMILLE. */ +#define MLD_SIGN_HOOK_MIN_CHECK_N 2000 +/* Relative tolerance in per mille. Overridable from the build: dedicated + * high-sample runs tighten it (see above). */ +#ifndef MLD_SIGN_HOOK_RATIO_TOL_PERMILLE +#define MLD_SIGN_HOOK_RATIO_TOL_PERMILLE 100 +#endif + +/* The signing hooks and this whole test exercise the internal signing API, so + * there is nothing to test when signing is disabled. */ +#if !defined(MLD_CONFIG_NO_SIGN_API) + +#include "decode_hex.h" + +/* + * This test exercises the restartable-signing hooks enabled by + * MLD_CONFIG_SIGN_HOOK_RESUME, MLD_CONFIG_SIGN_HOOK_ATTEMPT and + * MLD_CONFIG_SIGN_HOOK_FINISH. + * + * The custom config (test/configs/test_sign_hook_config.h) only enables the + * hooks and forward-declares struct test_sign_hook_ctx; the context type and + * the three hook implementations live here. The attempt hook pauses signing -- + * returning MLD_ERR_SIGNING_PAUSED -- after `attempts_per_call` uninterrupted + * attempts, recording the resume point in the context; the resume hook + * continues from there on the next call. attempts_per_call == -1 disables + * pausing (one-shot signing). + */ + +/* Histogram size. Bucket i (0-based) counts signatures that took i+1 attempts; + * the last bucket also counts every signature of >= MLD_SIGN_HOOK_HIST_N + * attempts. */ +#ifndef MLD_SIGN_HOOK_HIST_N +#define MLD_SIGN_HOOK_HIST_N 30 +#endif + +/* Statistics accumulated across all signing operations that share a context, + * maintained by the attempt and finish hooks. */ +struct test_sign_hook_stats +{ + uint64_t signatures; /* completed signatures (finish-hook calls) */ + uint64_t attempts; /* attempts actually performed */ + uint64_t histogram[MLD_SIGN_HOOK_HIST_N]; /* attempts per signature */ +}; + +/* Per-operation state for the signing hooks, carried via the context parameter. + * The test sets attempts_per_call / record_stats before each signing operation; + * the hooks maintain the rest. */ +struct test_sign_hook_ctx +{ + int attempts_per_call; /* uninterrupted attempts per call before pausing; + * -1 = never pause (one-shot) */ + int paused_attempt; /* attempt the next call resumes from; reset to 0 */ + int final_attempt; /* attempt at which signing succeeded; -1 until + * finish runs */ + int record_stats; /* if nonzero, this operation contributes to `stats`; + * lets the test count each logical signature once + * despite re-signing */ + struct test_sign_hook_stats stats; /* run-wide statistics */ +}; + +/* + * Signing hooks (declared by the config, defined here). Resume returns the + * attempt to continue from; attempt pauses after attempts_per_call + * uninterrupted attempts and otherwise counts the attempt; finish records the + * completed signature. + */ +uint16_t mld_sign_hook_resume(struct test_sign_hook_ctx *context) +{ + /* Resume from where the previous call paused (0 on a fresh op). */ + return (uint16_t)context->paused_attempt; +} + +int mld_sign_hook_attempt(uint16_t attempt, struct test_sign_hook_ctx *context) +{ + /* Having resumed at paused_attempt, run attempts_per_call attempts + * uninterrupted, then pause: when `attempt` reaches paused_attempt + + * attempts_per_call, record it as the new resume point and pause. -1 means + * never pause. */ + if (context->attempts_per_call >= 0 && + (int)attempt == context->paused_attempt + context->attempts_per_call) + { + context->paused_attempt = (int)attempt; + return 1; /* pause before this attempt */ + } + /* Count only attempts that actually proceed (and only when this operation + * opted in), so the statistics are independent of how the work is split + * across calls. */ + if (context->record_stats) + { + context->stats.attempts++; + } + return 0; /* proceed */ +} + +void mld_sign_hook_finish(uint16_t attempt, struct test_sign_hook_ctx *context) +{ + if (context->record_stats) + { + /* A signature that succeeds at attempt index `attempt` took attempt + 1 + * attempts; bucket it (last bucket is saturating). */ + int n = (int)attempt + 1; + int bucket = + (n < MLD_SIGN_HOOK_HIST_N) ? (n - 1) : (MLD_SIGN_HOOK_HIST_N - 1); + context->stats.signatures++; + context->stats.histogram[bucket]++; + } + /* Record the successful attempt and reset the resume point so a subsequent + * operation starts fresh. */ + context->final_attempt = (int)attempt; + context->paused_attempt = 0; +} + +#define CHECK(x) \ + do \ + { \ + if (!(x)) \ + { \ + fprintf(stderr, "ERROR (%s,%d)\n", __FILE__, __LINE__); \ + return 1; \ + } \ + } while (0) + +/* + * Accessors for struct test_sign_hook_ctx. The test only touches the context + * through these, so its layout can be restructured without changing the tests. + */ + +/* Initialize a fresh context: zero statistics, no pending operation. */ +static void test_sign_ctx_init(struct test_sign_hook_ctx *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); +} + +/* Begin a signing operation: pause every `attempts_per_call` attempts (-1 = + * never), and record statistics for this operation iff `record_stats`. A value + * of 0 would ask the attempt hook to pause before the very first attempt with + * no progress -- livelock -- so we clamp it up to 1. */ +static void test_sign_ctx_begin_op(struct test_sign_hook_ctx *ctx, + int attempts_per_call, int record_stats) +{ + if (attempts_per_call == 0) + { + attempts_per_call = 1; + } + ctx->attempts_per_call = attempts_per_call; + ctx->paused_attempt = 0; + ctx->final_attempt = -1; + ctx->record_stats = record_stats; +} + +static uint64_t test_sign_ctx_signatures(const struct test_sign_hook_ctx *ctx) +{ + return ctx->stats.signatures; +} +static uint64_t test_sign_ctx_attempts(const struct test_sign_hook_ctx *ctx) +{ + return ctx->stats.attempts; +} +static uint64_t test_sign_ctx_histogram(const struct test_sign_hook_ctx *ctx, + int i) +{ + return ctx->stats.histogram[i]; +} + +/* + * Core helper: sign (m, pre) with secret key `sk` and randomness `rnd`, pausing + * every `attempts_per_call` attempts (-1 = never pause), looping over the + * paused calls until the signature completes. The completed (fixed-size) + * signature is written to sig and -- if `expected` is non-NULL -- checked to + * equal expected/MLDSA_SIG_BYTES. + * + * `ctx` is the shared hook context: its statistics accumulate across calls, so + * the same context is threaded through every test. Statistics are recorded for + * this operation only when `record_stats` is nonzero, so each logical signature + * is counted once even though the test re-signs it several ways. + * + * Returns 0 on success, 1 on a signing error or a mismatch against `expected`. + */ +static int sign_and_compare(struct test_sign_hook_ctx *ctx, + int attempts_per_call, int record_stats, + uint8_t sig[MLDSA_SIG_BYTES], + const uint8_t *expected, const uint8_t *m, + size_t mlen, const uint8_t *pre, size_t prelen, + const uint8_t *rnd, const uint8_t *sk) +{ + int rc; + + test_sign_ctx_begin_op(ctx, attempts_per_call, record_stats); + + for (;;) + { + rc = mld_signature_internal(sig, m, mlen, pre, prelen, rnd, sk, + 0 /* externalmu */, ctx); + if (rc == 0) + { + break; + } + if (rc == MLD_ERR_SIGNING_PAUSED) + { + continue; /* resume from the recorded attempt */ + } + fprintf(stderr, "ERROR: signing failed with rc=%d\n", rc); + return 1; + } + + if (expected != NULL && memcmp(sig, expected, MLDSA_SIG_BYTES) != 0) + { + fprintf(stderr, "ERROR: signature mismatch (attempts_per_call=%d)\n", + attempts_per_call); + return 1; + } + + return 0; +} + +/* + * Build the pure-ML-DSA domain-separation prefix for context string + * `ctx`/`ctxlen` using mldsa-native's own API. Returns the prefix length, or 0 + * on error. + */ +static size_t make_pre(uint8_t pre[MLD_DOMAIN_SEPARATION_MAX_BYTES], + const uint8_t *ctx, size_t ctxlen) +{ + return mld_prepare_domain_separation_prefix(pre, NULL, 0, ctx, ctxlen, + MLD_PREHASH_NONE); +} + +/* + * Smoke test: run the core helper against the known test vector. Signing the + * test-vector message with the test-vector key and randomness must reproduce + * the recorded signature, both one-shot and single-step. + */ +static int test_known_answer(struct test_sign_hook_ctx *ctx) +{ + uint8_t sig[MLDSA_SIG_BYTES]; + uint8_t pre[MLD_DOMAIN_SEPARATION_MAX_BYTES]; + size_t prelen = + make_pre(pre, (const uint8_t *)TEST_VECTOR_CTX, TEST_VECTOR_CTX_LEN); + + CHECK(prelen != 0); + + /* Statistics are gathered only by the distribution test, so neither run here + * records. */ + CHECK(sign_and_compare(ctx, -1, 0 /* don't record */, sig, test_vector_sig, + (const uint8_t *)TEST_VECTOR_MSG, TEST_VECTOR_MSG_LEN, + pre, prelen, test_vector_rnd, test_vector_sk) == 0); + CHECK(sign_and_compare(ctx, 1, 0 /* don't record */, sig, test_vector_sig, + (const uint8_t *)TEST_VECTOR_MSG, TEST_VECTOR_MSG_LEN, + pre, prelen, test_vector_rnd, test_vector_sk) == 0); + + printf("Known-answer smoke test PASSED.\n"); + return 0; +} + +/* + * Restartable-signing equivalence test. Uses the fixed test-vector message and + * key; for each of `iterations` rounds, pick fresh signing randomness, produce + * the signature one-shot (the reference), then re-sign with the same randomness + * and check that we obtain the same signature both (a) single-step (one attempt + * per call) and (b) with a random number of attempts per call. + */ +static int test_restartable(struct test_sign_hook_ctx *ctx, int iterations) +{ + uint8_t pre[MLD_DOMAIN_SEPARATION_MAX_BYTES]; + size_t prelen = + make_pre(pre, (const uint8_t *)TEST_VECTOR_CTX, TEST_VECTOR_CTX_LEN); + int i; + + CHECK(prelen != 0); + + for (i = 0; i < iterations; i++) + { + uint8_t rnd[MLDSA_RNDBYTES]; + uint8_t ref_sig[MLDSA_SIG_BYTES]; + uint8_t sig[MLDSA_SIG_BYTES]; + uint8_t rand_byte; + int rand_apc; + + /* Fresh signing randomness, chosen at runtime. The reference and the + * re-signs below all use the same message, key and randomness. */ + CHECK(randombytes(rnd, sizeof(rnd)) == 0); + CHECK(randombytes(&rand_byte, 1) == 0); + /* Random attempts-per-call in [1, 4], biased toward 1 so that most + * iterations actually exercise multiple pause/resume cycles. The mean + * number of attempts per signature is ~4-5, so a uniform draw over [1, 4] + * would leave apc=3, 4 mostly completing in one call and collapsing to + * the one-shot baseline. Distribution: P(1)=1/2, P(2)=1/4, P(3)=P(4)=1/8. + */ + { + unsigned int b = (unsigned int)rand_byte % 8u; + if (b < 4u) + { + rand_apc = 1; + } + else if (b < 6u) + { + rand_apc = 2; + } + else if (b < 7u) + { + rand_apc = 3; + } + else + { + rand_apc = 4; + } + } + + /* Reference signature, produced one-shot. Statistics are gathered only by + * the distribution test, so this does not record. */ + CHECK(sign_and_compare(ctx, -1, 0 /* don't record */, ref_sig, NULL, + (const uint8_t *)TEST_VECTOR_MSG, + TEST_VECTOR_MSG_LEN, pre, prelen, rnd, + test_vector_sk) == 0); + + /* (a) Single-step: pause after every attempt. */ + CHECK(sign_and_compare(ctx, 1, 0 /* don't record */, sig, ref_sig, + (const uint8_t *)TEST_VECTOR_MSG, + TEST_VECTOR_MSG_LEN, pre, prelen, rnd, + test_vector_sk) == 0); + + /* (b) Random number of attempts per call. */ + CHECK(sign_and_compare(ctx, rand_apc, 0 /* don't record */, sig, ref_sig, + (const uint8_t *)TEST_VECTOR_MSG, + TEST_VECTOR_MSG_LEN, pre, prelen, rnd, + test_vector_sk) == 0); + } + + printf("Restartable signing equivalence test PASSED (%d iteration(s)).\n", + iterations); + return 0; +} + +/* + * Distribution test: run `iterations` uninterrupted (one-shot) signing + * operations, each with a fresh key and fresh signing randomness, recording + * statistics for every one. Drawing a new key each iteration makes the measured + * mean an estimate of the key-averaged expected attempts per signature (the + * rejection probability is mildly key-dependent via the r0- and hint-checks), + * not a single-key conditional mean. Run with a large iteration count for an + * accurate picture. + * + * This is the only test that generates keys, so it is compiled out under + * MLD_CONFIG_NO_KEYPAIR_API; the other two use the fixed test-vector key. + */ +#if !defined(MLD_CONFIG_NO_KEYPAIR_API) +static int test_distribution(struct test_sign_hook_ctx *ctx, int iterations) +{ + uint8_t pre[MLD_DOMAIN_SEPARATION_MAX_BYTES]; + size_t prelen = + make_pre(pre, (const uint8_t *)TEST_VECTOR_CTX, TEST_VECTOR_CTX_LEN); + int i; + + CHECK(prelen != 0); + + for (i = 0; i < iterations; i++) + { + uint8_t kgseed[MLDSA_SEEDBYTES]; + uint8_t rnd[MLDSA_RNDBYTES]; + uint8_t pk[MLDSA_PUBLICKEYBYTES(MLD_CONFIG_PARAMETER_SET)]; + uint8_t sk[MLDSA_SECRETKEYBYTES(MLD_CONFIG_PARAMETER_SET)]; + uint8_t sig[MLDSA_SIG_BYTES]; + + /* Fresh key and fresh signing randomness each iteration. keypair_internal + * takes the context parameter (MLD_CONFIG_CONTEXT_PARAMETER is set) but + * does not invoke the signing hooks, so passing `ctx` here is inert. */ + CHECK(randombytes(kgseed, sizeof(kgseed)) == 0); + CHECK(mld_keypair_internal(pk, sk, kgseed, ctx) == 0); + CHECK(randombytes(rnd, sizeof(rnd)) == 0); + + CHECK(sign_and_compare(ctx, -1, 1 /* record */, sig, NULL, + (const uint8_t *)TEST_VECTOR_MSG, + TEST_VECTOR_MSG_LEN, pre, prelen, rnd, sk) == 0); + } + + { + /* Measured attempts/signature ratio, in thousandths (integer), against the + * expected geometric mean; both shown to three decimals. */ + uint64_t a = test_sign_ctx_attempts(ctx); + uint64_t s = test_sign_ctx_signatures(ctx); + uint64_t ratio_x1000 = (s != 0) ? (1000u * a + s / 2) / s : 0; + printf(" Measured attempts/signature: %llu.%03llu (expected %d.%03d)\n", + (unsigned long long)(ratio_x1000 / 1000), + (unsigned long long)(ratio_x1000 % 1000), + MLD_SIGN_HOOK_EXPECTED_RATIO_X1000 / 1000, + MLD_SIGN_HOOK_EXPECTED_RATIO_X1000 % 1000); + + /* With enough samples, check that ratio against the expected mean. All + * integer: compare 1000*attempts against EXPECTED_RATIO_X1000*signatures, + * allowing a +-TOL_PERMILLE per-mille relative band. */ + if (iterations >= MLD_SIGN_HOOK_MIN_CHECK_N) + { + uint64_t lhs = 1000u * a; + uint64_t mid = (uint64_t)MLD_SIGN_HOOK_EXPECTED_RATIO_X1000 * s; + uint64_t tol = (uint64_t)MLD_SIGN_HOOK_RATIO_TOL_PERMILLE * mid / 1000u; + uint64_t diff = (lhs > mid) ? (lhs - mid) : (mid - lhs); + if (diff > tol) + { + int exp_int = MLD_SIGN_HOOK_EXPECTED_RATIO_X1000 / 1000; + int exp_frac = MLD_SIGN_HOOK_EXPECTED_RATIO_X1000 % 1000; + int tol_int = MLD_SIGN_HOOK_RATIO_TOL_PERMILLE / 10; + int tol_frac = MLD_SIGN_HOOK_RATIO_TOL_PERMILLE % 10; + /* Report the measured value on stderr too: on failure the harness + * surfaces stderr, not stdout, so include it here to make the failure + * diagnosable (and reproducible via the printed PRNG seed). */ + fprintf(stderr, + "ERROR: attempts/signature ratio out of range: measured " + "%llu.%03llu, expected %d.%03d +- %d.%d%%\n", + (unsigned long long)(ratio_x1000 / 1000), + (unsigned long long)(ratio_x1000 % 1000), exp_int, exp_frac, + tol_int, tol_frac); + return 1; + } + } + } + + printf("Distribution test PASSED (%d signature(s)).\n", iterations); + return 0; +} +#endif /* !MLD_CONFIG_NO_KEYPAIR_API */ + +/* Print the run-wide statistics accumulated in the shared context. The + * histogram is shown both numerically and as a bar chart, where each bar is + * `*` repeated round(100 * count / signatures) times (i.e. percent of all + * signatures that took that many attempts). */ +static void print_stats(const struct test_sign_hook_ctx *ctx) +{ + uint64_t total = test_sign_ctx_signatures(ctx); + int i; + printf("\nSigning statistics:\n"); + printf(" Signatures: %llu\n", (unsigned long long)total); + printf(" Attempts: %llu\n", + (unsigned long long)test_sign_ctx_attempts(ctx)); + printf(" Attempts-per-signature histogram (bar = %% of signatures):\n"); + for (i = 0; i < MLD_SIGN_HOOK_HIST_N; i++) + { + uint64_t count = test_sign_ctx_histogram(ctx, i); + const char *rel = (i == MLD_SIGN_HOOK_HIST_N - 1) ? ">=" : " "; + /* Bar length: percent of all signatures, rounded to nearest integer. */ + int bar = (total != 0) ? (int)((100 * count + total / 2) / total) : 0; + int j; + printf(" [%s%2d|%6llu]: ", rel, i + 1, (unsigned long long)count); + for (j = 0; j < bar; j++) + { + printf("*"); + } + printf("\n"); + } +} + +/* Bytes of PRNG seed accepted on the command line (argv[1] = "seed=HEX"). */ +#define MLD_SIGN_HOOK_SEED_BYTES 32 + +#if defined(main) +int main(int argc, char *argv[]); +#endif +int main(int argc, char *argv[]) +{ + int r = 0; + /* A single context shared across all tests, so the statistics maintained by + * the hooks accumulate over the entire run. */ + struct test_sign_hook_ctx ctx; + test_sign_ctx_init(&ctx); + + /* Optional argv[1] = "seed=HEX": a hex seed for the (testing-only) PRNG, + * printed so a run is reproducible by re-passing it. Absent, the default + * pi-digit seed keeps the run deterministic. */ + if (argc > 1) + { + unsigned char *seed = decode_hex("seed", MLD_SIGN_HOOK_SEED_BYTES, argv[1]); + int i; + if (seed == NULL) + { + return 1; /* decode_hex already printed a usage message */ + } + randombytes_seed(seed, MLD_SIGN_HOOK_SEED_BYTES); + printf("PRNG seed (hex): "); + for (i = 0; i < MLD_SIGN_HOOK_SEED_BYTES; i++) + { + printf("%02x", seed[i]); + } + printf("\n"); + } + else + { + printf("PRNG seed: default (deterministic); pass seed=HEX to randomize\n"); + } + + r |= test_known_answer(&ctx); + r |= test_restartable(&ctx, MLD_SIGN_HOOK_ITERATIONS); +#if !defined(MLD_CONFIG_NO_KEYPAIR_API) + r |= test_distribution(&ctx, MLD_SIGN_HOOK_DIST_ITERATIONS); +#endif + + if (r) + { + return 1; + } + + print_stats(&ctx); + + printf("\nAll good! Restartable signing matches one-shot signing.\n"); + return 0; +} + +#else /* !MLD_CONFIG_NO_SIGN_API */ + +#if defined(main) +int main(void); +#endif +int main(void) +{ + printf("Signing API disabled; nothing to test.\n"); + return 0; +} + +#endif /* MLD_CONFIG_NO_SIGN_API */ From 27360a4fe12dbefcd8184365b9b827d3c31416f2 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 11:17:34 +0100 Subject: [PATCH 8/9] autogen: Regenerate configs for the signing hooks 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 --- BIBLIOGRAPHY.md | 5 + .../mldsa_native/mldsa_native_config.h | 106 ++- .../mldsa_native/mldsa_native_config.h | 106 ++- .../mldsa_native/mldsa_native_config.h | 106 ++- .../mldsa_native/mldsa_native_config.h | 106 ++- .../mldsa_native/mldsa_native_config.h | 106 ++- .../mldsa_native/mldsa_native_config.h | 106 ++- .../mldsa_native/mldsa_native_config.h | 106 ++- .../mldsa_native/mldsa_native_config.h | 106 ++- .../mldsa_native/mldsa_native_config.h | 106 ++- .../mldsa_native/mldsa_native_config.h | 106 ++- .../mldsa_native/mldsa_native_config.h | 106 ++- mldsa/mldsa_native.c | 7 + mldsa/mldsa_native_asm.S | 7 + mldsa/src/poly_kl.c | 1 - proofs/cbmc/mldsa_native_config_cbmc.h | 106 ++- scripts/autogen | 8 +- test/configs/break_pct_config.h | 106 ++- test/configs/custom_heap_alloc_config.h | 106 ++- test/configs/custom_memcpy_config.h | 106 ++- test/configs/custom_memset_config.h | 106 ++- .../custom_native_capability_config_0.h | 106 ++- .../custom_native_capability_config_1.h | 106 ++- ...stom_native_capability_config_CPUID_AVX2.h | 106 ++- ...native_capability_config_ID_AA64PFR1_EL1.h | 106 ++- test/configs/custom_randombytes_config.h | 106 ++- test/configs/custom_stdlib_config.h | 106 ++- test/configs/custom_zeroize_config.h | 106 ++- test/configs/low_signing_bound_config.h | 106 ++- test/configs/no_asm_config.h | 106 ++- test/configs/serial_fips202_config.h | 106 ++- test/configs/test_alloc_config.h | 106 ++- test/configs/test_sign_hook_config.h | 857 ++++++++++++++++++ 33 files changed, 3555 insertions(+), 192 deletions(-) create mode 100644 test/configs/test_sign_hook_config.h diff --git a/BIBLIOGRAPHY.md b/BIBLIOGRAPHY.md index 399624239..8156d7aa0 100644 --- a/BIBLIOGRAPHY.md +++ b/BIBLIOGRAPHY.md @@ -82,6 +82,7 @@ source code and documentation. - [examples/monolithic_build_native/mldsa_native/mldsa_native_config.h](examples/monolithic_build_native/mldsa_native/mldsa_native_config.h) - [examples/multilevel_build/mldsa_native/mldsa_native_config.h](examples/multilevel_build/mldsa_native/mldsa_native_config.h) - [examples/multilevel_build_native/mldsa_native/mldsa_native_config.h](examples/multilevel_build_native/mldsa_native/mldsa_native_config.h) + - [examples/restartable_sign/mldsa_native/mldsa_native_config.h](examples/restartable_sign/mldsa_native/mldsa_native_config.h) - [integration/liboqs/config_aarch64.h](integration/liboqs/config_aarch64.h) - [integration/liboqs/config_c.h](integration/liboqs/config_c.h) - [integration/liboqs/config_x86_64.h](integration/liboqs/config_x86_64.h) @@ -103,6 +104,7 @@ source code and documentation. - [test/configs/no_asm_config.h](test/configs/no_asm_config.h) - [test/configs/serial_fips202_config.h](test/configs/serial_fips202_config.h) - [test/configs/test_alloc_config.h](test/configs/test_alloc_config.h) + - [test/configs/test_sign_hook_config.h](test/configs/test_sign_hook_config.h) ### `FIPS202` @@ -133,6 +135,7 @@ source code and documentation. - [examples/monolithic_build_native/mldsa_native/mldsa_native_config.h](examples/monolithic_build_native/mldsa_native/mldsa_native_config.h) - [examples/multilevel_build/mldsa_native/mldsa_native_config.h](examples/multilevel_build/mldsa_native/mldsa_native_config.h) - [examples/multilevel_build_native/mldsa_native/mldsa_native_config.h](examples/multilevel_build_native/mldsa_native/mldsa_native_config.h) + - [examples/restartable_sign/mldsa_native/mldsa_native_config.h](examples/restartable_sign/mldsa_native/mldsa_native_config.h) - [mldsa/mldsa_native.h](mldsa/mldsa_native.h) - [mldsa/mldsa_native_config.h](mldsa/mldsa_native_config.h) - [mldsa/src/ct.h](mldsa/src/ct.h) @@ -166,6 +169,8 @@ source code and documentation. - [test/configs/no_asm_config.h](test/configs/no_asm_config.h) - [test/configs/serial_fips202_config.h](test/configs/serial_fips202_config.h) - [test/configs/test_alloc_config.h](test/configs/test_alloc_config.h) + - [test/configs/test_sign_hook_config.h](test/configs/test_sign_hook_config.h) + - [test/src/test_sign_hook.c](test/src/test_sign_hook.c) ### `HYBRID` diff --git a/examples/basic_deterministic/mldsa_native/mldsa_native_config.h b/examples/basic_deterministic/mldsa_native/mldsa_native_config.h index f144907db..624e6a0c9 100644 --- a/examples/basic_deterministic/mldsa_native/mldsa_native_config.h +++ b/examples/basic_deterministic/mldsa_native/mldsa_native_config.h @@ -700,24 +700,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/examples/basic_lowram/mldsa_native/mldsa_native_config.h b/examples/basic_lowram/mldsa_native/mldsa_native_config.h index 6768cf257..dbdc6450c 100644 --- a/examples/basic_lowram/mldsa_native/mldsa_native_config.h +++ b/examples/basic_lowram/mldsa_native/mldsa_native_config.h @@ -699,24 +699,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/examples/bring_your_own_fips202/mldsa_native/mldsa_native_config.h b/examples/bring_your_own_fips202/mldsa_native/mldsa_native_config.h index 54a5e0c01..171930829 100644 --- a/examples/bring_your_own_fips202/mldsa_native/mldsa_native_config.h +++ b/examples/bring_your_own_fips202/mldsa_native/mldsa_native_config.h @@ -700,24 +700,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/examples/bring_your_own_fips202_static/mldsa_native/mldsa_native_config.h b/examples/bring_your_own_fips202_static/mldsa_native/mldsa_native_config.h index d2c20f1a6..05da64b85 100644 --- a/examples/bring_your_own_fips202_static/mldsa_native/mldsa_native_config.h +++ b/examples/bring_your_own_fips202_static/mldsa_native/mldsa_native_config.h @@ -701,24 +701,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/examples/custom_backend/mldsa_native/mldsa_native_config.h b/examples/custom_backend/mldsa_native/mldsa_native_config.h index 4e572a895..97bf3ebae 100644 --- a/examples/custom_backend/mldsa_native/mldsa_native_config.h +++ b/examples/custom_backend/mldsa_native/mldsa_native_config.h @@ -696,24 +696,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/examples/monolithic_build/mldsa_native/mldsa_native_config.h b/examples/monolithic_build/mldsa_native/mldsa_native_config.h index caf2d892d..cde87c0fc 100644 --- a/examples/monolithic_build/mldsa_native/mldsa_native_config.h +++ b/examples/monolithic_build/mldsa_native/mldsa_native_config.h @@ -699,24 +699,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/examples/monolithic_build_multilevel/mldsa_native/mldsa_native_config.h b/examples/monolithic_build_multilevel/mldsa_native/mldsa_native_config.h index 934beef66..6a673f43a 100644 --- a/examples/monolithic_build_multilevel/mldsa_native/mldsa_native_config.h +++ b/examples/monolithic_build_multilevel/mldsa_native/mldsa_native_config.h @@ -700,24 +700,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/examples/monolithic_build_multilevel_native/mldsa_native/mldsa_native_config.h b/examples/monolithic_build_multilevel_native/mldsa_native/mldsa_native_config.h index cd69940ad..0bdf90f77 100644 --- a/examples/monolithic_build_multilevel_native/mldsa_native/mldsa_native_config.h +++ b/examples/monolithic_build_multilevel_native/mldsa_native/mldsa_native_config.h @@ -706,24 +706,116 @@ static MLD_INLINE int mld_randombytes(uint8_t *ptr, size_t len) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/examples/monolithic_build_native/mldsa_native/mldsa_native_config.h b/examples/monolithic_build_native/mldsa_native/mldsa_native_config.h index de2b1a7d5..39f952710 100644 --- a/examples/monolithic_build_native/mldsa_native/mldsa_native_config.h +++ b/examples/monolithic_build_native/mldsa_native/mldsa_native_config.h @@ -699,24 +699,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/examples/multilevel_build/mldsa_native/mldsa_native_config.h b/examples/multilevel_build/mldsa_native/mldsa_native_config.h index 27bd53862..6f9435702 100644 --- a/examples/multilevel_build/mldsa_native/mldsa_native_config.h +++ b/examples/multilevel_build/mldsa_native/mldsa_native_config.h @@ -699,24 +699,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/examples/multilevel_build_native/mldsa_native/mldsa_native_config.h b/examples/multilevel_build_native/mldsa_native/mldsa_native_config.h index c17bee364..3645eca35 100644 --- a/examples/multilevel_build_native/mldsa_native/mldsa_native_config.h +++ b/examples/multilevel_build_native/mldsa_native/mldsa_native_config.h @@ -697,24 +697,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/mldsa/mldsa_native.c b/mldsa/mldsa_native.c index f1c4f66c8..e201a245b 100644 --- a/mldsa/mldsa_native.c +++ b/mldsa/mldsa_native.c @@ -159,6 +159,7 @@ #undef MLD_ERR_FAIL #undef MLD_ERR_OUT_OF_MEMORY #undef MLD_ERR_RNG_FAIL +#undef MLD_ERR_SIGNING_PAUSED #undef MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED #undef MLD_H #undef MLD_MAX3_ @@ -213,6 +214,7 @@ #undef MLD_ERR_FAIL #undef MLD_ERR_OUT_OF_MEMORY #undef MLD_ERR_RNG_FAIL +#undef MLD_ERR_SIGNING_PAUSED #undef MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED #undef MLD_EXTERNAL_API #undef MLD_FIPS202X4_HEADER_FILE @@ -294,6 +296,7 @@ #undef MLDSA_SK_TR_OFFSET #undef MLDSA_TAU #undef MLDSA_TRBYTES +#undef MLD_MAX_KAPPA #undef MLD_PARAMS_H /* mldsa/src/poly_kl.h */ #undef MLD_POLYETA_UNPACK_LOWER_BOUND @@ -436,6 +439,10 @@ #undef MLD_CONTEXT_PARAMETERS_7 #undef MLD_CONTEXT_PARAMETERS_8 #undef MLD_CONTEXT_PARAMETERS_9 +#undef MLD_CONTEXT_UNUSED +#undef mld_sign_attempt +#undef mld_sign_finish +#undef mld_sign_resume /* mldsa/src/ct.h */ #undef MLD_CT_H #undef MLD_USE_ASM_VALUE_BARRIER diff --git a/mldsa/mldsa_native_asm.S b/mldsa/mldsa_native_asm.S index 3a04809d6..ea2f1fd3b 100644 --- a/mldsa/mldsa_native_asm.S +++ b/mldsa/mldsa_native_asm.S @@ -182,6 +182,7 @@ #undef MLD_ERR_FAIL #undef MLD_ERR_OUT_OF_MEMORY #undef MLD_ERR_RNG_FAIL +#undef MLD_ERR_SIGNING_PAUSED #undef MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED #undef MLD_H #undef MLD_MAX3_ @@ -236,6 +237,7 @@ #undef MLD_ERR_FAIL #undef MLD_ERR_OUT_OF_MEMORY #undef MLD_ERR_RNG_FAIL +#undef MLD_ERR_SIGNING_PAUSED #undef MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED #undef MLD_EXTERNAL_API #undef MLD_FIPS202X4_HEADER_FILE @@ -317,6 +319,7 @@ #undef MLDSA_SK_TR_OFFSET #undef MLDSA_TAU #undef MLDSA_TRBYTES +#undef MLD_MAX_KAPPA #undef MLD_PARAMS_H /* mldsa/src/poly_kl.h */ #undef MLD_POLYETA_UNPACK_LOWER_BOUND @@ -459,6 +462,10 @@ #undef MLD_CONTEXT_PARAMETERS_7 #undef MLD_CONTEXT_PARAMETERS_8 #undef MLD_CONTEXT_PARAMETERS_9 +#undef MLD_CONTEXT_UNUSED +#undef mld_sign_attempt +#undef mld_sign_finish +#undef mld_sign_resume /* mldsa/src/ct.h */ #undef MLD_CT_H #undef MLD_USE_ASM_VALUE_BARRIER diff --git a/mldsa/src/poly_kl.c b/mldsa/src/poly_kl.c index 5be537d46..befe58a8b 100644 --- a/mldsa/src/poly_kl.c +++ b/mldsa/src/poly_kl.c @@ -905,5 +905,4 @@ void mld_polyz_unpack(mld_poly *r, const uint8_t a[MLDSA_POLYZ_PACKEDBYTES]) #undef mld_poly_use_hint_c #undef mld_polyz_unpack_c #undef MLD_POLY_UNIFORM_ETA_NBLOCKS -#undef MLD_POLY_UNIFORM_ETA_NBLOCKS #undef MLD_POLY_UNIFORM_GAMMA1_NBLOCKS diff --git a/proofs/cbmc/mldsa_native_config_cbmc.h b/proofs/cbmc/mldsa_native_config_cbmc.h index bff69aa6c..1cd6d6783 100644 --- a/proofs/cbmc/mldsa_native_config_cbmc.h +++ b/proofs/cbmc/mldsa_native_config_cbmc.h @@ -698,24 +698,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/scripts/autogen b/scripts/autogen index bd281fea1..42737dc91 100755 --- a/scripts/autogen +++ b/scripts/autogen @@ -3284,8 +3284,12 @@ def gen_header_guards(): def gen_source_undefs(source_file): - # Get list of #define's clauses in this source file (ignore filename) - undef_list = list(map(lambda c: c[1], get_defines_from_file(source_file))) + # Get list of #define's clauses in this source file (ignore filename). + # Deduplicate while preserving order: a macro defined in multiple + # conditional branches (e.g. #if/#else) needs only a single #undef. + undef_list = list( + dict.fromkeys(map(lambda c: c[1], get_defines_from_file(source_file))) + ) if not undef_list: return diff --git a/test/configs/break_pct_config.h b/test/configs/break_pct_config.h index 3db26af9f..e0ea25ec8 100644 --- a/test/configs/break_pct_config.h +++ b/test/configs/break_pct_config.h @@ -704,24 +704,116 @@ static MLD_INLINE int mld_break_pct(void) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/custom_heap_alloc_config.h b/test/configs/custom_heap_alloc_config.h index de95fae6e..915431c2e 100644 --- a/test/configs/custom_heap_alloc_config.h +++ b/test/configs/custom_heap_alloc_config.h @@ -717,24 +717,116 @@ static inline void *mld_posix_memalign(size_t align, size_t sz) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/custom_memcpy_config.h b/test/configs/custom_memcpy_config.h index f4910fd08..a1b9d00af 100644 --- a/test/configs/custom_memcpy_config.h +++ b/test/configs/custom_memcpy_config.h @@ -707,24 +707,116 @@ static MLD_INLINE void *mld_memcpy(void *dest, const void *src, size_t n) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/custom_memset_config.h b/test/configs/custom_memset_config.h index 6dc9e9ee3..d056b206a 100644 --- a/test/configs/custom_memset_config.h +++ b/test/configs/custom_memset_config.h @@ -706,24 +706,116 @@ static MLD_INLINE void *mld_memset(void *s, int c, size_t n) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/custom_native_capability_config_0.h b/test/configs/custom_native_capability_config_0.h index b0821ffb1..5f6231d41 100644 --- a/test/configs/custom_native_capability_config_0.h +++ b/test/configs/custom_native_capability_config_0.h @@ -706,24 +706,116 @@ static MLD_INLINE int mld_sys_check_capability(mld_sys_cap cap) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/custom_native_capability_config_1.h b/test/configs/custom_native_capability_config_1.h index cc5124cd2..d77a01175 100644 --- a/test/configs/custom_native_capability_config_1.h +++ b/test/configs/custom_native_capability_config_1.h @@ -705,24 +705,116 @@ static MLD_INLINE int mld_sys_check_capability(mld_sys_cap cap) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/custom_native_capability_config_CPUID_AVX2.h b/test/configs/custom_native_capability_config_CPUID_AVX2.h index cd0288a01..b69280ece 100644 --- a/test/configs/custom_native_capability_config_CPUID_AVX2.h +++ b/test/configs/custom_native_capability_config_CPUID_AVX2.h @@ -737,24 +737,116 @@ static MLD_INLINE int mld_sys_check_capability(mld_sys_cap cap) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/custom_native_capability_config_ID_AA64PFR1_EL1.h b/test/configs/custom_native_capability_config_ID_AA64PFR1_EL1.h index 6840de097..3367e2d5f 100644 --- a/test/configs/custom_native_capability_config_ID_AA64PFR1_EL1.h +++ b/test/configs/custom_native_capability_config_ID_AA64PFR1_EL1.h @@ -724,24 +724,116 @@ static MLD_INLINE int mld_sys_check_capability(mld_sys_cap cap) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/custom_randombytes_config.h b/test/configs/custom_randombytes_config.h index 3f4857660..286db6b82 100644 --- a/test/configs/custom_randombytes_config.h +++ b/test/configs/custom_randombytes_config.h @@ -699,24 +699,116 @@ static MLD_INLINE int mld_randombytes(uint8_t *ptr, size_t len) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/custom_stdlib_config.h b/test/configs/custom_stdlib_config.h index e97babbda..6a9b4a92b 100644 --- a/test/configs/custom_stdlib_config.h +++ b/test/configs/custom_stdlib_config.h @@ -715,24 +715,116 @@ static MLD_INLINE void *mld_memset(void *s, int c, size_t n) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/custom_zeroize_config.h b/test/configs/custom_zeroize_config.h index b07e5a5a4..23443d68d 100644 --- a/test/configs/custom_zeroize_config.h +++ b/test/configs/custom_zeroize_config.h @@ -700,24 +700,116 @@ static MLD_INLINE void mld_zeroize(void *ptr, size_t len) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/low_signing_bound_config.h b/test/configs/low_signing_bound_config.h index 170269488..3f264c509 100644 --- a/test/configs/low_signing_bound_config.h +++ b/test/configs/low_signing_bound_config.h @@ -702,24 +702,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/no_asm_config.h b/test/configs/no_asm_config.h index 42a4a1ba0..093eafebe 100644 --- a/test/configs/no_asm_config.h +++ b/test/configs/no_asm_config.h @@ -701,24 +701,116 @@ static MLD_INLINE void mld_zeroize(void *ptr, size_t len) /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/serial_fips202_config.h b/test/configs/serial_fips202_config.h index 75a7426f7..35bd0e84c 100644 --- a/test/configs/serial_fips202_config.h +++ b/test/configs/serial_fips202_config.h @@ -699,24 +699,116 @@ /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER */ /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ /* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/test_alloc_config.h b/test/configs/test_alloc_config.h index 8847d3853..c4b3b6b0e 100644 --- a/test/configs/test_alloc_config.h +++ b/test/configs/test_alloc_config.h @@ -707,24 +707,116 @@ void custom_free(struct test_ctx_t *ctx, void *p, size_t sz, const char *file, /** * MLD_CONFIG_CONTEXT_PARAMETER * - * Set this to add a context parameter that is provided to public - * API functions and is then available in custom callbacks. + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). * - * The type of the context parameter is configured via - * MLD_CONFIG_CONTEXT_PARAMETER_TYPE. + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. */ #define MLD_CONFIG_CONTEXT_PARAMETER /** * MLD_CONFIG_CONTEXT_PARAMETER_TYPE * - * Set this to define the type for the context parameter used by - * MLD_CONFIG_CONTEXT_PARAMETER. + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. * - * This is only relevant if MLD_CONFIG_CONTEXT_PARAMETER is set. + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. */ #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE struct test_ctx_t * +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + #include "src/sys.h" + static MLD_INLINE uint16_t mld_sign_hook_resume(void) + { + ... return the attempt to resume from ... + } + static MLD_INLINE int mld_sign_hook_attempt(uint16_t attempt) + { + ... return non-zero to pause here; for resume, store attempt ... + return 0; + } + static MLD_INLINE void mld_sign_hook_finish(uint16_t attempt) + { + ... mark the operation complete (attempt = successful attempt) ... + } + #endif +*/ + /** * MLD_CONFIG_REDUCE_RAM * diff --git a/test/configs/test_sign_hook_config.h b/test/configs/test_sign_hook_config.h new file mode 100644 index 000000000..41c6322ea --- /dev/null +++ b/test/configs/test_sign_hook_config.h @@ -0,0 +1,857 @@ +/* + * Copyright (c) The mldsa-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* References + * ========== + * + * - [FIPS140_3_IG] + * Implementation Guidance for FIPS 140-3 and the Cryptographic Module + * Validation Program + * National Institute of Standards and Technology + * https://csrc.nist.gov/projects/cryptographic-module-validation-program/fips-140-3-ig-announcements + * + * - [FIPS204] + * FIPS 204 Module-Lattice-Based Digital Signature Standard + * National Institute of Standards and Technology + * https://csrc.nist.gov/pubs/fips/204/final + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mldsa-native repository. + * Do not modify it directly. + */ + +/* + * Test configuration: Restartable signing driven by the + * SIGN_HOOK_{RESUME,ATTEMPT,FINISH} hooks + * + * This configuration differs from the default mldsa/mldsa_native_config.h in + * the following places: + * - MLD_CONFIG_NAMESPACE_PREFIX + * - MLD_CONFIG_NO_RANDOMIZED_API + * - MLD_CONFIG_CONTEXT_PARAMETER + * - MLD_CONFIG_CONTEXT_PARAMETER_TYPE + * - MLD_CONFIG_SIGN_HOOK_RESUME + */ + + +#ifndef MLD_CONFIG_H +#define MLD_CONFIG_H + +/** + * MLD_CONFIG_PARAMETER_SET + * + * Specifies the parameter set for ML-DSA + * - MLD_CONFIG_PARAMETER_SET=44 corresponds to ML-DSA-44 + * - MLD_CONFIG_PARAMETER_SET=65 corresponds to ML-DSA-65 + * - MLD_CONFIG_PARAMETER_SET=87 corresponds to ML-DSA-87 + * + * If you want to support multiple parameter sets, build the + * library multiple times and set MLD_CONFIG_MULTILEVEL_BUILD. + * See MLD_CONFIG_MULTILEVEL_BUILD for how to do this while + * minimizing code duplication. + * + * This can also be set using CFLAGS. + */ +#ifndef MLD_CONFIG_PARAMETER_SET +#define MLD_CONFIG_PARAMETER_SET \ + 44 /* Change this for different security strengths */ +#endif + +/** + * MLD_CONFIG_FILE + * + * If defined, this is a header that will be included instead + * of the default configuration file mldsa/mldsa_native_config.h. + * + * When you need to build mldsa-native in multiple configurations, + * using varying MLD_CONFIG_FILE can be more convenient + * then configuring everything through CFLAGS. + * + * To use, MLD_CONFIG_FILE _must_ be defined prior + * to the inclusion of any mldsa-native headers. For example, + * it can be set by passing `-DMLD_CONFIG_FILE="..."` + * on the command line. + */ +/* No need to set this -- we _are_ already in a custom config */ +/* #define MLD_CONFIG_FILE "mldsa_native_config.h" */ + +/** + * MLD_CONFIG_NAMESPACE_PREFIX + * + * The prefix to use to namespace global symbols from mldsa/. + * + * In a multi-level build, level-dependent symbols will + * additionally be prefixed with the parameter set (44/65/87). + * + * This can also be set using CFLAGS. + */ +#define MLD_CONFIG_NAMESPACE_PREFIX mld + +/** + * MLD_CONFIG_MULTILEVEL_BUILD + * + * Set this if the build is part of a multi-level build supporting + * multiple parameter sets. + * + * If you need only a single parameter set, keep this unset. + * + * To build mldsa-native with support for all parameter sets, + * build it three times -- once per parameter set -- and set the + * option MLD_CONFIG_MULTILEVEL_WITH_SHARED for exactly one of + * them, and MLD_CONFIG_MULTILEVEL_NO_SHARED for the others. + * MLD_CONFIG_MULTILEVEL_BUILD should be set for all of them. + * + * See examples/multilevel_build for an example. + * + * This can also be set using CFLAGS. + */ +/* #define MLD_CONFIG_MULTILEVEL_BUILD */ + +/** + * MLD_CONFIG_EXTERNAL_API_QUALIFIER + * + * If set, this option provides an additional function + * qualifier to be added to declarations of mldsa-native's + * public API. + * + * The primary use case for this option are single-CU builds + * where the public API exposed by mldsa-native is wrapped by + * another API in the consuming application. In this case, + * even mldsa-native's public API can be marked `static`. + */ +/* #define MLD_CONFIG_EXTERNAL_API_QUALIFIER */ + +/** + * MLD_CONFIG_NO_KEYPAIR_API + * + * By default, mldsa-native includes support for generating key + * pairs. If you don't need this, set MLD_CONFIG_NO_KEYPAIR_API + * to exclude keypair, keypair_internal, + * pk_from_sk, and all internal APIs only needed by + * those functions. + */ +/* #define MLD_CONFIG_NO_KEYPAIR_API */ + +/** + * MLD_CONFIG_NO_SIGN_API + * + * By default, mldsa-native includes support for creating + * signatures. If you don't need this, set MLD_CONFIG_NO_SIGN_API + * to exclude signature, + * signature_extmu, signature_internal, + * signature_pre_hash_internal, + * signature_pre_hash_shake256, and all internal APIs + * only needed by those functions. + */ +/* #define MLD_CONFIG_NO_SIGN_API */ + +/** + * MLD_CONFIG_NO_VERIFY_API + * + * By default, mldsa-native includes support for verifying + * signatures. If you don't need this, set + * MLD_CONFIG_NO_VERIFY_API to exclude verify, + * verify_extmu, verify_internal, + * verify_pre_hash_internal, + * verify_pre_hash_shake256, and all internal APIs + * only needed by those functions. + */ +/* #define MLD_CONFIG_NO_VERIFY_API */ + +/** + * MLD_CONFIG_CORE_API_ONLY + * + * Set this to remove all public APIs except + * keypair_internal, signature_internal, + * and verify_internal. + */ +/* #define MLD_CONFIG_CORE_API_ONLY */ + +/** + * MLD_CONFIG_NO_RANDOMIZED_API + * + * If this option is set, mldsa-native will be built without the + * randomized API functions (keypair, + * signature, and signature_extmu). + * This allows users to build mldsa-native without providing a + * randombytes() implementation if they only need the + * internal deterministic API + * (keypair_internal, signature_internal). + * + * @note This option is incompatible with MLD_CONFIG_KEYGEN_PCT + * as the current PCT implementation requires + * signature(). + */ +#define MLD_CONFIG_NO_RANDOMIZED_API + +/** + * MLD_CONFIG_CONSTANTS_ONLY + * + * If you only need the size constants (MLDSA_PUBLICKEYBYTES, etc.) + * but no function declarations, set MLD_CONFIG_CONSTANTS_ONLY. + * + * This only affects the public header mldsa_native.h, not + * the implementation. + */ +/* #define MLD_CONFIG_CONSTANTS_ONLY */ +/****************************************************************************** + * + * Build-only configuration options + * + * The remaining configurations are build-options only. + * They do not affect the API described in mldsa_native.h. + * + *****************************************************************************/ +#if defined(MLD_BUILD_INTERNAL) + +/** + * MLD_CONFIG_MULTILEVEL_WITH_SHARED + * + * This is for multi-level builds of mldsa-native only. If you + * need only a single parameter set, keep this unset. + * + * If this is set, all MLD_CONFIG_PARAMETER_SET-independent + * code will be included in the build, including code needed only + * for other parameter sets. + * + * Example: TODO: add example + * + * To build mldsa-native with support for all parameter sets, + * build it three times -- once per parameter set -- and set the + * option MLD_CONFIG_MULTILEVEL_WITH_SHARED for exactly one of + * them, and MLD_CONFIG_MULTILEVEL_NO_SHARED for the others. + * + * See examples/multilevel_build_mldsa for an example. + * + * This can also be set using CFLAGS. + */ +/* #define MLD_CONFIG_MULTILEVEL_WITH_SHARED */ + +/** + * MLD_CONFIG_MULTILEVEL_NO_SHARED + * + * This is for multi-level builds of mldsa-native only. If you + * need only a single parameter set, keep this unset. + * + * If this is set, no MLD_CONFIG_PARAMETER_SET-independent code + * will be included in the build. + * + * To build mldsa-native with support for all parameter sets, + * build it three times -- once per parameter set -- and set the + * option MLD_CONFIG_MULTILEVEL_WITH_SHARED for exactly one of + * them, and MLD_CONFIG_MULTILEVEL_NO_SHARED for the others. + * + * See examples/multilevel_build_mldsa for an example. + * + * This can also be set using CFLAGS. + */ +/* #define MLD_CONFIG_MULTILEVEL_NO_SHARED */ + +/** + * MLD_CONFIG_MONOBUILD_KEEP_SHARED_HEADERS + * + * This is only relevant for single compilation unit (SCU) + * builds of mldsa-native. In this case, it determines whether + * directives defined in parameter-set-independent headers should + * be #undef'ined or not at the of the SCU file. This is needed + * in multilevel builds. + * + * See examples/multilevel_build_native for an example. + * + * This can also be set using CFLAGS. + */ +/* #define MLD_CONFIG_MONOBUILD_KEEP_SHARED_HEADERS */ + +/** + * MLD_CONFIG_USE_NATIVE_BACKEND_ARITH + * + * Determines whether an native arithmetic backend should be used. + * + * The arithmetic backend covers performance-critical functions + * such as the number-theoretic transform (NTT). + * + * If this option is unset, the C backend will be used. + * + * If this option is set, the arithmetic backend to be use is + * determined by MLD_CONFIG_ARITH_BACKEND_FILE: If the latter is + * unset, the default backend for your the target architecture + * will be used. If set, it must be the name of a backend metadata + * file. + * + * This can also be set using CFLAGS. + */ +#if !defined(MLD_CONFIG_USE_NATIVE_BACKEND_ARITH) +/* #define MLD_CONFIG_USE_NATIVE_BACKEND_ARITH */ +#endif + +/** + * MLD_CONFIG_ARITH_BACKEND_FILE + * + * The arithmetic backend to use. + * + * If MLD_CONFIG_USE_NATIVE_BACKEND_ARITH is unset, this option + * is ignored. + * + * If MLD_CONFIG_USE_NATIVE_BACKEND_ARITH is set, this option must + * either be undefined or the filename of an arithmetic backend. + * If unset, the default backend will be used. + * + * This can be set using CFLAGS. + */ +#if defined(MLD_CONFIG_USE_NATIVE_BACKEND_ARITH) && \ + !defined(MLD_CONFIG_ARITH_BACKEND_FILE) +#define MLD_CONFIG_ARITH_BACKEND_FILE "native/meta.h" +#endif + +/** + * MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202 + * + * Determines whether an native FIPS202 backend should be used. + * + * The FIPS202 backend covers 1x/2x/4x-fold Keccak-f1600, which is + * the performance bottleneck of SHA3 and SHAKE. + * + * If this option is unset, the C backend will be used. + * + * If this option is set, the FIPS202 backend to be use is + * determined by MLD_CONFIG_FIPS202_BACKEND_FILE: If the latter is + * unset, the default backend for your the target architecture + * will be used. If set, it must be the name of a backend metadata + * file. + * + * This can also be set using CFLAGS. + */ +#if !defined(MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202) +/* #define MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202 */ +#endif + +/** + * MLD_CONFIG_FIPS202_BACKEND_FILE + * + * The FIPS-202 backend to use. + * + * If MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202 is set, this option + * must either be undefined or the filename of a FIPS202 backend. + * If unset, the default backend will be used. + * + * This can be set using CFLAGS. + */ +#if defined(MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202) && \ + !defined(MLD_CONFIG_FIPS202_BACKEND_FILE) +#define MLD_CONFIG_FIPS202_BACKEND_FILE "fips202/native/auto.h" +#endif + +/** + * MLD_CONFIG_FIPS202_CUSTOM_HEADER + * + * Custom header to use for FIPS-202 + * + * This should only be set if you intend to use a custom + * FIPS-202 implementation, different from the one shipped + * with mldsa-native. + * + * If set, it must be the name of a file serving as the + * replacement for mldsa/src/fips202/fips202.h, and exposing + * the same API (see FIPS202.md). + */ +/* #define MLD_CONFIG_FIPS202_CUSTOM_HEADER "SOME_FILE.h" */ + +/** + * MLD_CONFIG_FIPS202X4_CUSTOM_HEADER + * + * Custom header to use for FIPS-202-X4 + * + * This should only be set if you intend to use a custom + * FIPS-202 implementation, different from the one shipped + * with mldsa-native. + * + * If set, it must be the name of a file serving as the + * replacement for mldsa/src/fips202/fips202x4.h, and exposing + * the same API (see FIPS202.md). + */ +/* #define MLD_CONFIG_FIPS202X4_CUSTOM_HEADER "SOME_FILE.h" */ + +/** + * MLD_CONFIG_CUSTOM_ZEROIZE + * + * In compliance with @[FIPS204, Section 3.6.3], mldsa-native, + * zeroizes intermediate stack buffers before returning from + * function calls. + * + * Set this option and define `mld_zeroize` if you want to + * use a custom method to zeroize intermediate stack buffers. + * The default implementation uses SecureZeroMemory on Windows + * and a memset + compiler barrier otherwise. If neither of those + * is available on the target platform, compilation will fail, + * and you will need to use MLD_CONFIG_CUSTOM_ZEROIZE to provide + * a custom implementation of `mld_zeroize()`. + * + * @warning The explicit stack zeroization conducted by mldsa-native reduces + * the likelihood of data leaking on the stack, but does not + * eliminate it. The C standard makes no guarantee about where a + * compiler allocates structures and whether/where it makes copies + * of them. Also, in addition to entire structures, there may also + * be potentially exploitable leakage of individual values on the + * stack. + * + * If you need bullet-proof zeroization of the stack, you need to + * consider additional measures instead of what this feature + * provides. In this case, you can set mld_zeroize to a no-op. + */ +/* #define MLD_CONFIG_CUSTOM_ZEROIZE + #if !defined(__ASSEMBLER__) + #include + #include "src/src.h" + static MLD_INLINE void mld_zeroize(void *ptr, size_t len) + { + ... your implementation ... + } + #endif +*/ + +/** + * MLD_CONFIG_CUSTOM_RANDOMBYTES + * + * mldsa-native does not provide a secure randombytes + * implementation. Such an implementation has to provided by the + * consumer. + * + * If this option is not set, mldsa-native expects a function + * int randombytes(uint8_t *out, size_t outlen). + * + * Set this option and define `mld_randombytes` if you want to + * use a custom method to sample randombytes with a different name + * or signature. + */ +/* #define MLD_CONFIG_CUSTOM_RANDOMBYTES + #if !defined(__ASSEMBLER__) + #include + #include "src/src.h" + static MLD_INLINE int mld_randombytes(uint8_t *ptr, size_t len) + { + ... your implementation ... + return 0; + } + #endif +*/ + +/** + * MLD_CONFIG_CUSTOM_CAPABILITY_FUNC + * + * mldsa-native backends may rely on specific hardware features. + * Those backends will only be included in an mldsa-native build + * if support for the respective features is enabled at + * compile-time. However, when building for a heteroneous set + * of CPUs to run the resulting binary/library on, feature + * detection at _runtime_ is needed to decided whether a backend + * can be used or not. + * + * Set this option and define `mld_sys_check_capability` if you + * want to use a custom method to dispatch between implementations. + * + * Return value 1 indicates that a capability is supported. + * Return value 0 indicates that a capability is not supported. + * + * If this option is not set, mldsa-native uses compile-time + * feature detection only to decide which backend to use. + * + * If you compile mldsa-native on a system with different + * capabilities than the system that the resulting binary/library + * will be run on, you must use this option. + */ +/* #define MLD_CONFIG_CUSTOM_CAPABILITY_FUNC + static MLD_INLINE int mld_sys_check_capability(mld_sys_cap cap) + { + ... your implementation ... + } +*/ + +/** + * MLD_CONFIG_CUSTOM_ALLOC_FREE + * + * Set this option and define `MLD_CUSTOM_ALLOC` and + * `MLD_CUSTOM_FREE` if you want to use custom allocation for + * large local structures or buffers. + * + * By default, all buffers/structures are allocated on the stack. + * If this option is set, most of them will be allocated via + * MLD_CUSTOM_ALLOC. + * + * Parameters to MLD_CUSTOM_ALLOC: + * - T* v: Target pointer to declare. + * - T: Type of structure to be allocated + * - N: Number of elements to be allocated. + * + * Parameters to MLD_CUSTOM_FREE: + * - T* v: Target pointer to free. May be NULL. + * - T: Type of structure to be freed. + * - N: Number of elements to be freed. + * + * @warning This option is experimental. Its scope, configuration and + * function/macro signatures may change at any time. We expect a + * stable API for v2. + * + * @note Even if this option is set, some allocations further down + * the call stack will still be made from the stack. Those will + * likely be added to the scope of this option in the future. + * + * @note MLD_CUSTOM_ALLOC need not guarantee a successful + * allocation nor include error handling. Upon failure, the + * target pointer should simply be set to NULL. The calling + * code will handle this case and invoke MLD_CUSTOM_FREE. + */ +/* #define MLD_CONFIG_CUSTOM_ALLOC_FREE + #if !defined(__ASSEMBLER__) + #include + #define MLD_CUSTOM_ALLOC(v, T, N) \ + T* (v) = (T *)aligned_alloc(MLD_DEFAULT_ALIGN, \ + MLD_ALIGN_UP(sizeof(T) * (N))) + #define MLD_CUSTOM_FREE(v, T, N) free(v) + #endif +*/ + +/** + * MLD_CONFIG_CUSTOM_MEMCPY + * + * Set this option and define `mld_memcpy` if you want to + * use a custom method to copy memory instead of the standard + * library memcpy function. + * + * The custom implementation must have the same signature and + * behavior as the standard memcpy function: + * void *mld_memcpy(void *dest, const void *src, size_t n) + */ +/* #define MLD_CONFIG_CUSTOM_MEMCPY + #if !defined(__ASSEMBLER__) + #include + #include "src/src.h" + static MLD_INLINE void *mld_memcpy(void *dest, const void *src, size_t n) + { + ... your implementation ... + } + #endif +*/ + +/** + * MLD_CONFIG_CUSTOM_MEMSET + * + * Set this option and define `mld_memset` if you want to + * use a custom method to set memory instead of the standard + * library memset function. + * + * The custom implementation must have the same signature and + * behavior as the standard memset function: + * void *mld_memset(void *s, int c, size_t n) + */ +/* #define MLD_CONFIG_CUSTOM_MEMSET + #if !defined(__ASSEMBLER__) + #include + #include "src/src.h" + static MLD_INLINE void *mld_memset(void *s, int c, size_t n) + { + ... your implementation ... + } + #endif +*/ + +/** + * MLD_CONFIG_INTERNAL_API_QUALIFIER + * + * If set, this option provides an additional qualifier + * to be added to declarations of internal API functions and data. + * + * The primary use case for this option are single-CU builds, + * in which case this option can be set to `static`. + */ +/* #define MLD_CONFIG_INTERNAL_API_QUALIFIER */ + +/** + * MLD_CONFIG_CT_TESTING_ENABLED + * + * If set, mldsa-native annotates data as secret / public using + * valgrind's annotations VALGRIND_MAKE_MEM_UNDEFINED and + * VALGRIND_MAKE_MEM_DEFINED, enabling various checks for secret- + * dependent control flow of variable time execution (depending + * on the exact version of valgrind installed). + */ +/* #define MLD_CONFIG_CT_TESTING_ENABLED */ + +/** + * MLD_CONFIG_NO_ASM + * + * If this option is set, mldsa-native will be built without + * use of native code or inline assembly. + * + * By default, inline assembly is used to implement value barriers. + * Without inline assembly, mldsa-native will use a global volatile + * 'opt blocker' instead; see ct.h. + * + * Inline assembly is also used to implement a secure zeroization + * function on non-Windows platforms. If this option is set and + * the target platform is not Windows, you MUST set + * MLD_CONFIG_CUSTOM_ZEROIZE and provide a custom zeroization + * function. + * + * If this option is set, MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202 and + * and MLD_CONFIG_USE_NATIVE_BACKEND_ARITH will be ignored, and no + * native backends will be used. + */ +/* #define MLD_CONFIG_NO_ASM */ + +/** + * MLD_CONFIG_NO_ASM_VALUE_BARRIER + * + * If this option is set, mldsa-native will be built without + * use of native code or inline assembly for value barriers. + * + * By default, inline assembly (if available) is used to implement + * value barriers. + * Without inline assembly, mldsa-native will use a global volatile + * 'opt blocker' instead; see ct.h. + */ +/* #define MLD_CONFIG_NO_ASM_VALUE_BARRIER */ + +/** + * MLD_CONFIG_KEYGEN_PCT + * + * Compliance with @[FIPS140_3_IG, p.87] requires a + * Pairwise Consistency Test (PCT) to be carried out on a freshly + * generated keypair before it can be exported. + * + * Set this option if such a check should be implemented. + * In this case, keypair_internal and + * keypair will return a non-zero error code if the + * PCT failed. + * + * @note This feature will drastically lower the performance of + * key generation. + * + * @note This option is incompatible with MLD_CONFIG_NO_SIGN_API + * and MLD_CONFIG_NO_VERIFY_API as the current PCT implementation + * requires signature() and verify(). + */ +/* #define MLD_CONFIG_KEYGEN_PCT */ + +/** + * MLD_CONFIG_KEYGEN_PCT_BREAKAGE_TEST + * + * If this option is set, the user must provide a runtime + * function `static inline int mld_break_pct() { ... }` to + * indicate whether the PCT should be made fail. + * + * This option only has an effect if MLD_CONFIG_KEYGEN_PCT is set. + */ +/* #define MLD_CONFIG_KEYGEN_PCT_BREAKAGE_TEST + #if !defined(__ASSEMBLER__) + #include "src/src.h" + static MLD_INLINE int mld_break_pct(void) + { + ... return 0/1 depending on whether PCT should be broken ... + } + #endif +*/ + +/** + * MLD_CONFIG_MAX_SIGNING_ATTEMPTS + * + * Upper bound on the number of rejection-sampling iterations + * performed by ML-DSA signing (@[FIPS204, Algorithm 7]). + * + * If a valid signature is not produced within this many + * attempts, signing returns MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED. + * This is useful in timing-sensitive environments that + * require a deterministic worst-case bound on signing time. + * + * For FIPS 204 compliance, this value MUST be at least 814, + * cf. @[FIPS204, Appendix C], which is chosen so that the + * signing failure rate is < 2^{-256}. + * + * Default: Largest possible value before internal counters + * would overflow. This is larger than the FIPS204 bound. + * + * In particular, in the default configuration, the signing + * failure rate is < 2^{-256}. + */ +/* #define MLD_CONFIG_MAX_SIGNING_ATTEMPTS 814 */ + +/** + * MLD_CONFIG_SERIAL_FIPS202_ONLY + * + * Set this to use a FIPS202 implementation with global state + * that supports only one active Keccak computation at a time + * (e.g. some hardware accelerators). + * + * If this option is set, ML-DSA will use FIPS202 operations + * serially, ensuring that only one SHAKE context is active + * at any given time. + * + * This allows offloading Keccak computations to a hardware + * accelerator that holds only a single Keccak state locally, + * rather than requiring support for multiple concurrent + * Keccak states. + * + * @note Depending on the target CPU, this may reduce + * performance when using software FIPS202 implementations. + * Only enable this when you have to. + */ +/* #define MLD_CONFIG_SERIAL_FIPS202_ONLY */ + +/** + * MLD_CONFIG_CONTEXT_PARAMETER + * + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). + * + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. + */ +#define MLD_CONFIG_CONTEXT_PARAMETER + +/** + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE + * + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. + * + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. + */ +#if !defined(__ASSEMBLER__) +#include +/* The context type and the hook implementations live in + * test/src/test_sign_hook.c; here we only forward-declare them. */ +struct test_sign_hook_ctx; /* Forward declaration */ +#endif /* !__ASSEMBLER__ */ +#define MLD_CONFIG_CONTEXT_PARAMETER_TYPE struct test_sign_hook_ctx * + + +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* Enable all three signing hooks. Since MLD_CONFIG_CONTEXT_PARAMETER + * is set, the context is the last argument of each. The hooks are + * implemented in test/src/test_sign_hook.c. */ +#define MLD_CONFIG_SIGN_HOOK_RESUME +#define MLD_CONFIG_SIGN_HOOK_ATTEMPT +#define MLD_CONFIG_SIGN_HOOK_FINISH +#if !defined(__ASSEMBLER__) +struct test_sign_hook_ctx; /* Forward declaration */ +uint16_t mld_sign_hook_resume(struct test_sign_hook_ctx *context); +int mld_sign_hook_attempt(uint16_t attempt, struct test_sign_hook_ctx *context); +void mld_sign_hook_finish(uint16_t attempt, struct test_sign_hook_ctx *context); +#endif /* !__ASSEMBLER__ */ + + +/** + * MLD_CONFIG_REDUCE_RAM + * + * Set this to reduce RAM usage. This trades memory for performance. + * + * For expected memory usage, see the MLD_TOTAL_ALLOC_* constants defined in + * mldsa_native.h. + * + * This option is useful for embedded systems with tight RAM constraints but + * relaxed performance requirements. + * + */ +/* #define MLD_CONFIG_REDUCE_RAM */ + +/************************* Config internals ********************************/ + +#endif /* MLD_BUILD_INTERNAL */ + +/* Default namespace + * + * Don't change this. If you need a different namespace, re-define + * MLD_CONFIG_NAMESPACE_PREFIX above instead, and remove the following. + * + * The default MLDSA namespace is + * + * PQCP_MLDSA_NATIVE_MLDSA_ + * + * e.g., PQCP_MLDSA_NATIVE_MLDSA44_ + */ + +#if defined(MLD_CONFIG_MULTILEVEL_BUILD) +/* In a multi-level build the parameter set is appended by the namespacing + * machinery, so the default prefix must not embed it. */ +#define MLD_DEFAULT_NAMESPACE_PREFIX PQCP_MLDSA_NATIVE_MLDSA +#elif MLD_CONFIG_PARAMETER_SET == 44 +#define MLD_DEFAULT_NAMESPACE_PREFIX PQCP_MLDSA_NATIVE_MLDSA44 +#elif MLD_CONFIG_PARAMETER_SET == 65 +#define MLD_DEFAULT_NAMESPACE_PREFIX PQCP_MLDSA_NATIVE_MLDSA65 +#elif MLD_CONFIG_PARAMETER_SET == 87 +#define MLD_DEFAULT_NAMESPACE_PREFIX PQCP_MLDSA_NATIVE_MLDSA87 +#endif + +#endif /* !MLD_CONFIG_H */ From 142223d5e25f8daa325d31a16cf6967de0e9f394 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Mon, 13 Jul 2026 11:31:20 +0100 Subject: [PATCH 9/9] Examples: Add example for restartable signing via signing hooks 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 --- .github/workflows/base.yml | 3 + examples/README.md | 7 + examples/restartable_sign/.gitignore | 3 + examples/restartable_sign/Makefile | 100 +++ examples/restartable_sign/README.md | 50 ++ examples/restartable_sign/auto.mk | 1 + .../restartable_sign/expected_test_vectors.h | 1 + examples/restartable_sign/main.c | 192 ++++ .../mldsa_native/mldsa_native.h | 1 + .../mldsa_native/mldsa_native_config.h | 848 ++++++++++++++++++ examples/restartable_sign/mldsa_native/src | 1 + scripts/tests | 10 + test/configs/configs.yml | 28 + 13 files changed, 1245 insertions(+) create mode 100644 examples/restartable_sign/.gitignore create mode 100644 examples/restartable_sign/Makefile create mode 100644 examples/restartable_sign/README.md create mode 120000 examples/restartable_sign/auto.mk create mode 120000 examples/restartable_sign/expected_test_vectors.h create mode 100644 examples/restartable_sign/main.c create mode 120000 examples/restartable_sign/mldsa_native/mldsa_native.h create mode 100644 examples/restartable_sign/mldsa_native/mldsa_native_config.h create mode 120000 examples/restartable_sign/mldsa_native/src diff --git a/.github/workflows/base.yml b/.github/workflows/base.yml index a20f49c5f..06f76cca1 100644 --- a/.github/workflows/base.yml +++ b/.github/workflows/base.yml @@ -243,6 +243,9 @@ jobs: - name: basic_lowram run: | CFLAGS="-O0" make run -C examples/basic_lowram + - name: restartable_sign + run: | + CFLAGS="-O0" make run -C examples/restartable_sign - name: bring_your_own_fips202 run: | CFLAGS="-O0" make run -C examples/bring_your_own_fips202 diff --git a/examples/README.md b/examples/README.md index 625e8279e..0739a2758 100644 --- a/examples/README.md +++ b/examples/README.md @@ -16,6 +16,13 @@ See [basic_deterministic](basic_deterministic) for a basic example of how to bui See [basic_lowram](basic_lowram) for a basic example of how to build a single instance of mldsa-native with reduced RAM usage (`MLD_CONFIG_REDUCE_RAM`). This is useful for embedded systems with tight RAM constraints. +## Restartable signing + +See [restartable_sign](restartable_sign) for a minimal example of restartable signing using the signing hooks +(`MLD_CONFIG_SIGN_HOOK_RESUME` / `_ATTEMPT` / `_FINISH`). It pauses the rejection-sampling loop and resumes it on a +later call -- bounding per-call runtime while reproducing the same signature as an uninterrupted run -- keeping the +resume state in a global variable. + ## Multi-level build (C only) See [multilevel_build](multilevel_build) for an example of how to build one instance of mldsa-native per security level, diff --git a/examples/restartable_sign/.gitignore b/examples/restartable_sign/.gitignore new file mode 100644 index 000000000..eb98a94f1 --- /dev/null +++ b/examples/restartable_sign/.gitignore @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +build diff --git a/examples/restartable_sign/Makefile b/examples/restartable_sign/Makefile new file mode 100644 index 000000000..ad4576e9e --- /dev/null +++ b/examples/restartable_sign/Makefile @@ -0,0 +1,100 @@ +# Copyright (c) The mlkem-native project authors +# Copyright (c) The mldsa-native project authors +# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + +.PHONY: build run clean +.DEFAULT_GOAL := all + +CC ?= gcc + +# Adjust CFLAGS if needed +CFLAGS := \ + -Wall \ + -Wextra \ + -Werror=unused-result \ + -Wpedantic \ + -Werror \ + -Wmissing-prototypes \ + -Wshadow \ + -Wpointer-arith \ + -Wredundant-decls \ + -Wconversion \ + -Wsign-conversion \ + -Wno-long-long \ + -Wno-unknown-pragmas \ + -Wno-unused-command-line-argument \ + -O3 \ + -fomit-frame-pointer \ + -std=c99 \ + -pedantic \ + -MMD \ + $(CFLAGS) + +# If you want to use the native backends, the compiler needs to know about +# the target architecture. Here, we import the default host detection from +# mldsa-native's tests, but you can write your own or specialize accordingly. +AUTO ?= 1 +include auto.mk + +# The following only concerns the cross-compilation tests. +# You can likely ignore the following for your application. +# +# Append cross-prefix for cross compilation +# When called from the root Makefile, CROSS_PREFIX has already been added here +ifeq (,$(findstring $(CROSS_PREFIX),$(CC))) +CC := $(CROSS_PREFIX)$(CC) +endif + +# Part A: +# +# mldsa-native source and header files +# +# If you are not concerned about minimizing for a specific backend, +# you can just include _all_ source files into your build. +# +# In this example, we compile the individual mldsa-native source files directly. +# Alternatively, you can compile the 'monobuild' source file mldsa_native.c. +# See examples/monolithic_build for that. +MLD_SOURCE=$(wildcard \ + mldsa_native/src/*.c \ + mldsa_native/src/**/*.c \ + mldsa_native/src/**/**/*.c \ + mldsa_native/src/**/**/**/*.c) + +INC=-Imldsa_native + +# Part B: +# +# Your application source code +APP_SOURCE=$(wildcard *.c) + +ALL_SOURCE=$(MLD_SOURCE) $(APP_SOURCE) + +BUILD_DIR=build +BIN=test_binary + +BINARY_NAME_FULL_44=$(BUILD_DIR)/$(BIN)44 +BINARY_NAME_FULL_65=$(BUILD_DIR)/$(BIN)65 +BINARY_NAME_FULL_87=$(BUILD_DIR)/$(BIN)87 +BINARIES_FULL=$(BINARY_NAME_FULL_44) $(BINARY_NAME_FULL_65) $(BINARY_NAME_FULL_87) + +$(BINARY_NAME_FULL_44): CFLAGS += -DMLD_CONFIG_PARAMETER_SET=44 +$(BINARY_NAME_FULL_65): CFLAGS += -DMLD_CONFIG_PARAMETER_SET=65 +$(BINARY_NAME_FULL_87): CFLAGS += -DMLD_CONFIG_PARAMETER_SET=87 + +$(BINARIES_FULL): $(ALL_SOURCE) + echo "$@" + mkdir -p $(BUILD_DIR) + $(CC) $(CFLAGS) $(INC) $^ -o $@ + +all: build + +build: $(BINARIES_FULL) + +run: $(BINARIES_FULL) + $(EXEC_WRAPPER) ./$(BINARY_NAME_FULL_44) + $(EXEC_WRAPPER) ./$(BINARY_NAME_FULL_65) + $(EXEC_WRAPPER) ./$(BINARY_NAME_FULL_87) + +clean: + rm -rf $(BUILD_DIR) diff --git a/examples/restartable_sign/README.md b/examples/restartable_sign/README.md new file mode 100644 index 000000000..84291f313 --- /dev/null +++ b/examples/restartable_sign/README.md @@ -0,0 +1,50 @@ +[//]: # (SPDX-License-Identifier: CC-BY-4.0) + + +This directory contains a minimal example of *restartable signing*: pausing the +ML-DSA rejection-sampling loop and resuming it later, so that each signing call +has a bounded runtime while the overall result is identical to an uninterrupted +signature. + +## Use Case + +ML-DSA signing retries until a candidate signature passes rejection sampling, +so a single call has an unbounded runtime. On a system with strict timing +constraints you may want to cap the work done per call and continue later. +The signing hooks let you do that while retaining functional equivalence to +an uninterrupted ML-DSA signature operation. + +## Configuration + +The configuration file [mldsa_native_config.h](mldsa_native/mldsa_native_config.h) sets: +- `MLD_CONFIG_SIGN_HOOK_RESUME` / `_ATTEMPT` / `_FINISH`: the three hooks around + the rejection-sampling loop. The attempt hook may pause signing (which then + returns `MLD_ERR_SIGNING_PAUSED`); the resume hook continues from the paused + attempt on the next call. +- `MLD_CONFIG_NO_RANDOMIZED_API`: resuming is only sound when the randomness is + fixed across calls, so the caller drives the deterministic internal API. +- `MLD_CONFIG_NAMESPACE_PREFIX`: Symbol prefix (set to `mldsa`) + +## Resume state: global vs. context + +This example keeps the resume state in a **global variable**, so the hooks take +no arguments beyond the attempt counter. That is the simplest integration and +suits a single-threaded caller signing one message at a time. + +For concurrent or interleaved signing, keep the state **per caller** instead: +enable `MLD_CONFIG_CONTEXT_PARAMETER`, give it your state type, and every public +API function gains a trailing context argument that is forwarded to the hooks. +See [test/src/test_sign_hook.c](../../test/src/test_sign_hook.c) for that +variant. + +## Notes + +- This is incompatible with `MLD_CONFIG_KEYGEN_PCT` (pairwise consistency test): + the PCT runs a single internal signature that cannot be resumed. + +## Usage + +```bash +make build # Build the example +make run # Run the example +``` diff --git a/examples/restartable_sign/auto.mk b/examples/restartable_sign/auto.mk new file mode 120000 index 000000000..ce5c161cb --- /dev/null +++ b/examples/restartable_sign/auto.mk @@ -0,0 +1 @@ +../../test/mk/auto.mk \ No newline at end of file diff --git a/examples/restartable_sign/expected_test_vectors.h b/examples/restartable_sign/expected_test_vectors.h new file mode 120000 index 000000000..5c5d41ccb --- /dev/null +++ b/examples/restartable_sign/expected_test_vectors.h @@ -0,0 +1 @@ +../../test/test_vectors/expected_test_vectors.h \ No newline at end of file diff --git a/examples/restartable_sign/main.c b/examples/restartable_sign/main.c new file mode 100644 index 000000000..af351173d --- /dev/null +++ b/examples/restartable_sign/main.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) The mldsa-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +#include +#include +#include +#include + +#include +#include "expected_test_vectors.h" + +/* Convenience abbreviations for the secret-key and signature sizes, derived + * from the configured parameter set. */ +#define MLDSA_SK_BYTES MLDSA_SECRETKEYBYTES(MLD_CONFIG_PARAMETER_SET) +#define MLDSA_SIG_BYTES MLDSA_BYTES(MLD_CONFIG_PARAMETER_SET) + +/* For testing, we use fixed test-vector randomness (test_vector_rnd) so that we + * produce the same keys and signatures as the basic example -- and, crucially, + * the same signature regardless of how the signing work is split across + * restarts. A real integration must use cryptographic randomness for key + * generation. + */ + +#define CHECK(x) \ + do \ + { \ + int rc; \ + rc = (x); \ + if (!rc) \ + { \ + fprintf(stderr, "ERROR (%s,%d)\n", __FILE__, __LINE__); \ + return 1; \ + } \ + } while (0) + +/* + * Signing hooks. + * + * This example keeps all restart state in a single global, so the hooks take + * no context argument (the build leaves MLD_CONFIG_CONTEXT_PARAMETER unset). + * This is the simplest possible integration and fine for a single-threaded + * caller that signs one message at a time. + * + * For anything more -- concurrent or interleaved signing operations, or state + * owned by a caller object -- enable MLD_CONFIG_CONTEXT_PARAMETER instead. Each + * public API function then takes a trailing context argument that is forwarded + * to the hooks, so the state below moves out of the global and into that + * context. See test/src/test_sign_hook.c for the context-based variant. + */ + +/* The three hooks this integration must provide. mldsa-native calls them from + * its signing loop; the matching declarations in the configuration file are + * gated behind MLD_BUILD_INTERNAL, so we repeat them here to define the hooks + * with only the public API in scope. */ +uint16_t mld_sign_hook_resume(void); +int mld_sign_hook_attempt(uint16_t attempt); +void mld_sign_hook_finish(uint16_t attempt); + +/* How many rejection-sampling attempts each signing call runs before pausing; + * set by the driver before each operation. 0 means never pause (one-shot). */ +static unsigned attempts_per_call = 0; + +/* Attempt from which the next signing call resumes. Advanced by the attempt + * hook when it pauses, reset to 0 by the finish hook on success. */ +static uint16_t resume_attempt = 0; + +/* Resume from wherever the previous call paused (0 on a fresh operation). */ +uint16_t mld_sign_hook_resume(void) { return resume_attempt; } + +/* Called before each attempt. Return non-zero to pause; signing then returns + * MLD_ERR_SIGNING_PAUSED and the next call resumes from `attempt`. */ +int mld_sign_hook_attempt(uint16_t attempt) +{ + if (attempts_per_call != 0 && attempt >= resume_attempt + attempts_per_call) + { + resume_attempt = attempt; + return 1; /* pause before this attempt */ + } + return 0; /* proceed */ +} + +/* Called once signing succeeds; reset the resume point for the next operation. + */ +void mld_sign_hook_finish(uint16_t attempt) +{ + (void)attempt; + resume_attempt = 0; +} + +#if !defined(MLD_CONFIG_NO_SIGN_API) +/* + * Drive one signing operation to completion, pausing every `apc` attempts + * (0 = one-shot). Repeatedly calls the deterministic signing API with the same + * (message, sk, rnd); each MLD_ERR_SIGNING_PAUSED just means "call again to + * continue". Returns the number of restarts (extra calls beyond the first), or + * -1 on a genuine error. + */ +static int sign_restartable(uint8_t sig[MLDSA_SIG_BYTES], const uint8_t *pre, + size_t prelen, const uint8_t *rnd, + const uint8_t *sk, unsigned apc) +{ + int restarts = 0; + + /* Start a fresh operation and select the per-call attempt budget. */ + resume_attempt = 0; + attempts_per_call = apc; + + for (;;) + { + int rc = mldsa_signature_internal(sig, (const uint8_t *)TEST_VECTOR_MSG, + TEST_VECTOR_MSG_LEN, pre, prelen, rnd, sk, + 0 /* externalmu */); + if (rc == 0) + { + return restarts; /* done */ + } + if (rc == MLD_ERR_SIGNING_PAUSED) + { + restarts++; + continue; /* resume from resume_attempt */ + } + fprintf(stderr, "ERROR: signing failed with rc=%d\n", rc); + return -1; + } +} +#endif /* !MLD_CONFIG_NO_SIGN_API */ + +#if !defined(MLD_CONFIG_NO_SIGN_API) +static int example_sign(const uint8_t sk[MLDSA_SK_BYTES]) +{ + uint8_t sig[MLDSA_SIG_BYTES]; + uint8_t ref_sig[MLDSA_SIG_BYTES]; + uint8_t pre[TEST_VECTOR_CTX_LEN + 2]; /* prefix string (0, ctxlen, ctx) */ + int restarts; + + /* Prepare pre = (0, ctxlen, ctx) for pure ML-DSA domain separation. */ + pre[0] = 0; + pre[1] = TEST_VECTOR_CTX_LEN; + memcpy(pre + 2, TEST_VECTOR_CTX, TEST_VECTOR_CTX_LEN); + + /* Single-step: pause after every attempt, so the operation is spread over as + * many calls as it takes attempts. */ + printf("Signing message (single-step, pausing every attempt)... "); + restarts = sign_restartable(sig, pre, sizeof(pre), test_vector_rnd, sk, 1); + CHECK(restarts >= 0); + printf("DONE after %d restart(s)\n", restarts); + + /* One-shot: never pause, i.e. ordinary uninterrupted signing. */ + printf("Signing message (one-shot, no pausing)... "); + restarts = + sign_restartable(ref_sig, pre, sizeof(pre), test_vector_rnd, sk, 0); + CHECK(restarts == 0); + printf("DONE\n"); + + /* The key property: splitting the work across restarts neither repeats nor + * skips a rejection-sampling attempt, so both runs produce the very same + * signature -- and it is the reference test vector. */ + CHECK(memcmp(sig, ref_sig, MLDSA_SIG_BYTES) == 0); + CHECK(sizeof(test_vector_sig) == MLDSA_SIG_BYTES); + CHECK(memcmp(sig, test_vector_sig, MLDSA_SIG_BYTES) == 0); + printf("Single-step and one-shot signatures match the reference vector.\n"); + return 0; +} +#else /* !MLD_CONFIG_NO_SIGN_API */ +static int example_sign(const uint8_t sk[MLDSA_SK_BYTES]) +{ + (void)sk; + printf("Signing message... SKIPPED (sign API disabled)\n"); + return 0; +} +#endif /* MLD_CONFIG_NO_SIGN_API */ + +int main(void) +{ + printf("ML-DSA-%d Restartable Signing Example\n", MLD_CONFIG_PARAMETER_SET); + printf("====================================\n\n"); + + printf("Message: %s\n", TEST_VECTOR_MSG); + printf("Context: %s\n\n", TEST_VECTOR_CTX); + + /* Sign with the reference secret key; the example is solely about splitting + * the signing loop across restarts and reproducing the one-shot signature. */ + if (example_sign(test_vector_sk) != 0) + { + return 1; + } + + printf("\nAll tests passed! Restartable signing works.\n"); + return 0; +} diff --git a/examples/restartable_sign/mldsa_native/mldsa_native.h b/examples/restartable_sign/mldsa_native/mldsa_native.h new file mode 120000 index 000000000..f25191336 --- /dev/null +++ b/examples/restartable_sign/mldsa_native/mldsa_native.h @@ -0,0 +1 @@ +../../../mldsa/mldsa_native.h \ No newline at end of file diff --git a/examples/restartable_sign/mldsa_native/mldsa_native_config.h b/examples/restartable_sign/mldsa_native/mldsa_native_config.h new file mode 100644 index 000000000..98b1ba63a --- /dev/null +++ b/examples/restartable_sign/mldsa_native/mldsa_native_config.h @@ -0,0 +1,848 @@ +/* + * Copyright (c) The mldsa-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +/* References + * ========== + * + * - [FIPS140_3_IG] + * Implementation Guidance for FIPS 140-3 and the Cryptographic Module + * Validation Program + * National Institute of Standards and Technology + * https://csrc.nist.gov/projects/cryptographic-module-validation-program/fips-140-3-ig-announcements + * + * - [FIPS204] + * FIPS 204 Module-Lattice-Based Digital Signature Standard + * National Institute of Standards and Technology + * https://csrc.nist.gov/pubs/fips/204/final + */ + +/* + * WARNING: This file is auto-generated from scripts/autogen + * in the mldsa-native repository. + * Do not modify it directly. + */ + +/* + * Test configuration: Restartable signing driven by the signing hooks, using + * global resume state + * + * This configuration differs from the default mldsa/mldsa_native_config.h in + * the following places: + * - MLD_CONFIG_NAMESPACE_PREFIX + * - MLD_CONFIG_NO_RANDOMIZED_API + * - MLD_CONFIG_SIGN_HOOK_RESUME + */ + + +#ifndef MLD_CONFIG_H +#define MLD_CONFIG_H + +/** + * MLD_CONFIG_PARAMETER_SET + * + * Specifies the parameter set for ML-DSA + * - MLD_CONFIG_PARAMETER_SET=44 corresponds to ML-DSA-44 + * - MLD_CONFIG_PARAMETER_SET=65 corresponds to ML-DSA-65 + * - MLD_CONFIG_PARAMETER_SET=87 corresponds to ML-DSA-87 + * + * If you want to support multiple parameter sets, build the + * library multiple times and set MLD_CONFIG_MULTILEVEL_BUILD. + * See MLD_CONFIG_MULTILEVEL_BUILD for how to do this while + * minimizing code duplication. + * + * This can also be set using CFLAGS. + */ +#ifndef MLD_CONFIG_PARAMETER_SET +#define MLD_CONFIG_PARAMETER_SET \ + 44 /* Change this for different security strengths */ +#endif + +/** + * MLD_CONFIG_FILE + * + * If defined, this is a header that will be included instead + * of the default configuration file mldsa/mldsa_native_config.h. + * + * When you need to build mldsa-native in multiple configurations, + * using varying MLD_CONFIG_FILE can be more convenient + * then configuring everything through CFLAGS. + * + * To use, MLD_CONFIG_FILE _must_ be defined prior + * to the inclusion of any mldsa-native headers. For example, + * it can be set by passing `-DMLD_CONFIG_FILE="..."` + * on the command line. + */ +/* No need to set this -- we _are_ already in a custom config */ +/* #define MLD_CONFIG_FILE "mldsa_native_config.h" */ + +/** + * MLD_CONFIG_NAMESPACE_PREFIX + * + * The prefix to use to namespace global symbols from mldsa/. + * + * In a multi-level build, level-dependent symbols will + * additionally be prefixed with the parameter set (44/65/87). + * + * This can also be set using CFLAGS. + */ +#define MLD_CONFIG_NAMESPACE_PREFIX mldsa + +/** + * MLD_CONFIG_MULTILEVEL_BUILD + * + * Set this if the build is part of a multi-level build supporting + * multiple parameter sets. + * + * If you need only a single parameter set, keep this unset. + * + * To build mldsa-native with support for all parameter sets, + * build it three times -- once per parameter set -- and set the + * option MLD_CONFIG_MULTILEVEL_WITH_SHARED for exactly one of + * them, and MLD_CONFIG_MULTILEVEL_NO_SHARED for the others. + * MLD_CONFIG_MULTILEVEL_BUILD should be set for all of them. + * + * See examples/multilevel_build for an example. + * + * This can also be set using CFLAGS. + */ +/* #define MLD_CONFIG_MULTILEVEL_BUILD */ + +/** + * MLD_CONFIG_EXTERNAL_API_QUALIFIER + * + * If set, this option provides an additional function + * qualifier to be added to declarations of mldsa-native's + * public API. + * + * The primary use case for this option are single-CU builds + * where the public API exposed by mldsa-native is wrapped by + * another API in the consuming application. In this case, + * even mldsa-native's public API can be marked `static`. + */ +/* #define MLD_CONFIG_EXTERNAL_API_QUALIFIER */ + +/** + * MLD_CONFIG_NO_KEYPAIR_API + * + * By default, mldsa-native includes support for generating key + * pairs. If you don't need this, set MLD_CONFIG_NO_KEYPAIR_API + * to exclude keypair, keypair_internal, + * pk_from_sk, and all internal APIs only needed by + * those functions. + */ +/* #define MLD_CONFIG_NO_KEYPAIR_API */ + +/** + * MLD_CONFIG_NO_SIGN_API + * + * By default, mldsa-native includes support for creating + * signatures. If you don't need this, set MLD_CONFIG_NO_SIGN_API + * to exclude signature, + * signature_extmu, signature_internal, + * signature_pre_hash_internal, + * signature_pre_hash_shake256, and all internal APIs + * only needed by those functions. + */ +/* #define MLD_CONFIG_NO_SIGN_API */ + +/** + * MLD_CONFIG_NO_VERIFY_API + * + * By default, mldsa-native includes support for verifying + * signatures. If you don't need this, set + * MLD_CONFIG_NO_VERIFY_API to exclude verify, + * verify_extmu, verify_internal, + * verify_pre_hash_internal, + * verify_pre_hash_shake256, and all internal APIs + * only needed by those functions. + */ +/* #define MLD_CONFIG_NO_VERIFY_API */ + +/** + * MLD_CONFIG_CORE_API_ONLY + * + * Set this to remove all public APIs except + * keypair_internal, signature_internal, + * and verify_internal. + */ +/* #define MLD_CONFIG_CORE_API_ONLY */ + +/** + * MLD_CONFIG_NO_RANDOMIZED_API + * + * If this option is set, mldsa-native will be built without the + * randomized API functions (keypair, + * signature, and signature_extmu). + * This allows users to build mldsa-native without providing a + * randombytes() implementation if they only need the + * internal deterministic API + * (keypair_internal, signature_internal). + * + * @note This option is incompatible with MLD_CONFIG_KEYGEN_PCT + * as the current PCT implementation requires + * signature(). + */ +#define MLD_CONFIG_NO_RANDOMIZED_API + +/** + * MLD_CONFIG_CONSTANTS_ONLY + * + * If you only need the size constants (MLDSA_PUBLICKEYBYTES, etc.) + * but no function declarations, set MLD_CONFIG_CONSTANTS_ONLY. + * + * This only affects the public header mldsa_native.h, not + * the implementation. + */ +/* #define MLD_CONFIG_CONSTANTS_ONLY */ +/****************************************************************************** + * + * Build-only configuration options + * + * The remaining configurations are build-options only. + * They do not affect the API described in mldsa_native.h. + * + *****************************************************************************/ +#if defined(MLD_BUILD_INTERNAL) + +/** + * MLD_CONFIG_MULTILEVEL_WITH_SHARED + * + * This is for multi-level builds of mldsa-native only. If you + * need only a single parameter set, keep this unset. + * + * If this is set, all MLD_CONFIG_PARAMETER_SET-independent + * code will be included in the build, including code needed only + * for other parameter sets. + * + * Example: TODO: add example + * + * To build mldsa-native with support for all parameter sets, + * build it three times -- once per parameter set -- and set the + * option MLD_CONFIG_MULTILEVEL_WITH_SHARED for exactly one of + * them, and MLD_CONFIG_MULTILEVEL_NO_SHARED for the others. + * + * See examples/multilevel_build_mldsa for an example. + * + * This can also be set using CFLAGS. + */ +/* #define MLD_CONFIG_MULTILEVEL_WITH_SHARED */ + +/** + * MLD_CONFIG_MULTILEVEL_NO_SHARED + * + * This is for multi-level builds of mldsa-native only. If you + * need only a single parameter set, keep this unset. + * + * If this is set, no MLD_CONFIG_PARAMETER_SET-independent code + * will be included in the build. + * + * To build mldsa-native with support for all parameter sets, + * build it three times -- once per parameter set -- and set the + * option MLD_CONFIG_MULTILEVEL_WITH_SHARED for exactly one of + * them, and MLD_CONFIG_MULTILEVEL_NO_SHARED for the others. + * + * See examples/multilevel_build_mldsa for an example. + * + * This can also be set using CFLAGS. + */ +/* #define MLD_CONFIG_MULTILEVEL_NO_SHARED */ + +/** + * MLD_CONFIG_MONOBUILD_KEEP_SHARED_HEADERS + * + * This is only relevant for single compilation unit (SCU) + * builds of mldsa-native. In this case, it determines whether + * directives defined in parameter-set-independent headers should + * be #undef'ined or not at the of the SCU file. This is needed + * in multilevel builds. + * + * See examples/multilevel_build_native for an example. + * + * This can also be set using CFLAGS. + */ +/* #define MLD_CONFIG_MONOBUILD_KEEP_SHARED_HEADERS */ + +/** + * MLD_CONFIG_USE_NATIVE_BACKEND_ARITH + * + * Determines whether an native arithmetic backend should be used. + * + * The arithmetic backend covers performance-critical functions + * such as the number-theoretic transform (NTT). + * + * If this option is unset, the C backend will be used. + * + * If this option is set, the arithmetic backend to be use is + * determined by MLD_CONFIG_ARITH_BACKEND_FILE: If the latter is + * unset, the default backend for your the target architecture + * will be used. If set, it must be the name of a backend metadata + * file. + * + * This can also be set using CFLAGS. + */ +#if !defined(MLD_CONFIG_USE_NATIVE_BACKEND_ARITH) +/* #define MLD_CONFIG_USE_NATIVE_BACKEND_ARITH */ +#endif + +/** + * MLD_CONFIG_ARITH_BACKEND_FILE + * + * The arithmetic backend to use. + * + * If MLD_CONFIG_USE_NATIVE_BACKEND_ARITH is unset, this option + * is ignored. + * + * If MLD_CONFIG_USE_NATIVE_BACKEND_ARITH is set, this option must + * either be undefined or the filename of an arithmetic backend. + * If unset, the default backend will be used. + * + * This can be set using CFLAGS. + */ +#if defined(MLD_CONFIG_USE_NATIVE_BACKEND_ARITH) && \ + !defined(MLD_CONFIG_ARITH_BACKEND_FILE) +#define MLD_CONFIG_ARITH_BACKEND_FILE "native/meta.h" +#endif + +/** + * MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202 + * + * Determines whether an native FIPS202 backend should be used. + * + * The FIPS202 backend covers 1x/2x/4x-fold Keccak-f1600, which is + * the performance bottleneck of SHA3 and SHAKE. + * + * If this option is unset, the C backend will be used. + * + * If this option is set, the FIPS202 backend to be use is + * determined by MLD_CONFIG_FIPS202_BACKEND_FILE: If the latter is + * unset, the default backend for your the target architecture + * will be used. If set, it must be the name of a backend metadata + * file. + * + * This can also be set using CFLAGS. + */ +#if !defined(MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202) +/* #define MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202 */ +#endif + +/** + * MLD_CONFIG_FIPS202_BACKEND_FILE + * + * The FIPS-202 backend to use. + * + * If MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202 is set, this option + * must either be undefined or the filename of a FIPS202 backend. + * If unset, the default backend will be used. + * + * This can be set using CFLAGS. + */ +#if defined(MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202) && \ + !defined(MLD_CONFIG_FIPS202_BACKEND_FILE) +#define MLD_CONFIG_FIPS202_BACKEND_FILE "fips202/native/auto.h" +#endif + +/** + * MLD_CONFIG_FIPS202_CUSTOM_HEADER + * + * Custom header to use for FIPS-202 + * + * This should only be set if you intend to use a custom + * FIPS-202 implementation, different from the one shipped + * with mldsa-native. + * + * If set, it must be the name of a file serving as the + * replacement for mldsa/src/fips202/fips202.h, and exposing + * the same API (see FIPS202.md). + */ +/* #define MLD_CONFIG_FIPS202_CUSTOM_HEADER "SOME_FILE.h" */ + +/** + * MLD_CONFIG_FIPS202X4_CUSTOM_HEADER + * + * Custom header to use for FIPS-202-X4 + * + * This should only be set if you intend to use a custom + * FIPS-202 implementation, different from the one shipped + * with mldsa-native. + * + * If set, it must be the name of a file serving as the + * replacement for mldsa/src/fips202/fips202x4.h, and exposing + * the same API (see FIPS202.md). + */ +/* #define MLD_CONFIG_FIPS202X4_CUSTOM_HEADER "SOME_FILE.h" */ + +/** + * MLD_CONFIG_CUSTOM_ZEROIZE + * + * In compliance with @[FIPS204, Section 3.6.3], mldsa-native, + * zeroizes intermediate stack buffers before returning from + * function calls. + * + * Set this option and define `mld_zeroize` if you want to + * use a custom method to zeroize intermediate stack buffers. + * The default implementation uses SecureZeroMemory on Windows + * and a memset + compiler barrier otherwise. If neither of those + * is available on the target platform, compilation will fail, + * and you will need to use MLD_CONFIG_CUSTOM_ZEROIZE to provide + * a custom implementation of `mld_zeroize()`. + * + * @warning The explicit stack zeroization conducted by mldsa-native reduces + * the likelihood of data leaking on the stack, but does not + * eliminate it. The C standard makes no guarantee about where a + * compiler allocates structures and whether/where it makes copies + * of them. Also, in addition to entire structures, there may also + * be potentially exploitable leakage of individual values on the + * stack. + * + * If you need bullet-proof zeroization of the stack, you need to + * consider additional measures instead of what this feature + * provides. In this case, you can set mld_zeroize to a no-op. + */ +/* #define MLD_CONFIG_CUSTOM_ZEROIZE + #if !defined(__ASSEMBLER__) + #include + #include "src/src.h" + static MLD_INLINE void mld_zeroize(void *ptr, size_t len) + { + ... your implementation ... + } + #endif +*/ + +/** + * MLD_CONFIG_CUSTOM_RANDOMBYTES + * + * mldsa-native does not provide a secure randombytes + * implementation. Such an implementation has to provided by the + * consumer. + * + * If this option is not set, mldsa-native expects a function + * int randombytes(uint8_t *out, size_t outlen). + * + * Set this option and define `mld_randombytes` if you want to + * use a custom method to sample randombytes with a different name + * or signature. + */ +/* #define MLD_CONFIG_CUSTOM_RANDOMBYTES + #if !defined(__ASSEMBLER__) + #include + #include "src/src.h" + static MLD_INLINE int mld_randombytes(uint8_t *ptr, size_t len) + { + ... your implementation ... + return 0; + } + #endif +*/ + +/** + * MLD_CONFIG_CUSTOM_CAPABILITY_FUNC + * + * mldsa-native backends may rely on specific hardware features. + * Those backends will only be included in an mldsa-native build + * if support for the respective features is enabled at + * compile-time. However, when building for a heteroneous set + * of CPUs to run the resulting binary/library on, feature + * detection at _runtime_ is needed to decided whether a backend + * can be used or not. + * + * Set this option and define `mld_sys_check_capability` if you + * want to use a custom method to dispatch between implementations. + * + * Return value 1 indicates that a capability is supported. + * Return value 0 indicates that a capability is not supported. + * + * If this option is not set, mldsa-native uses compile-time + * feature detection only to decide which backend to use. + * + * If you compile mldsa-native on a system with different + * capabilities than the system that the resulting binary/library + * will be run on, you must use this option. + */ +/* #define MLD_CONFIG_CUSTOM_CAPABILITY_FUNC + static MLD_INLINE int mld_sys_check_capability(mld_sys_cap cap) + { + ... your implementation ... + } +*/ + +/** + * MLD_CONFIG_CUSTOM_ALLOC_FREE + * + * Set this option and define `MLD_CUSTOM_ALLOC` and + * `MLD_CUSTOM_FREE` if you want to use custom allocation for + * large local structures or buffers. + * + * By default, all buffers/structures are allocated on the stack. + * If this option is set, most of them will be allocated via + * MLD_CUSTOM_ALLOC. + * + * Parameters to MLD_CUSTOM_ALLOC: + * - T* v: Target pointer to declare. + * - T: Type of structure to be allocated + * - N: Number of elements to be allocated. + * + * Parameters to MLD_CUSTOM_FREE: + * - T* v: Target pointer to free. May be NULL. + * - T: Type of structure to be freed. + * - N: Number of elements to be freed. + * + * @warning This option is experimental. Its scope, configuration and + * function/macro signatures may change at any time. We expect a + * stable API for v2. + * + * @note Even if this option is set, some allocations further down + * the call stack will still be made from the stack. Those will + * likely be added to the scope of this option in the future. + * + * @note MLD_CUSTOM_ALLOC need not guarantee a successful + * allocation nor include error handling. Upon failure, the + * target pointer should simply be set to NULL. The calling + * code will handle this case and invoke MLD_CUSTOM_FREE. + */ +/* #define MLD_CONFIG_CUSTOM_ALLOC_FREE + #if !defined(__ASSEMBLER__) + #include + #define MLD_CUSTOM_ALLOC(v, T, N) \ + T* (v) = (T *)aligned_alloc(MLD_DEFAULT_ALIGN, \ + MLD_ALIGN_UP(sizeof(T) * (N))) + #define MLD_CUSTOM_FREE(v, T, N) free(v) + #endif +*/ + +/** + * MLD_CONFIG_CUSTOM_MEMCPY + * + * Set this option and define `mld_memcpy` if you want to + * use a custom method to copy memory instead of the standard + * library memcpy function. + * + * The custom implementation must have the same signature and + * behavior as the standard memcpy function: + * void *mld_memcpy(void *dest, const void *src, size_t n) + */ +/* #define MLD_CONFIG_CUSTOM_MEMCPY + #if !defined(__ASSEMBLER__) + #include + #include "src/src.h" + static MLD_INLINE void *mld_memcpy(void *dest, const void *src, size_t n) + { + ... your implementation ... + } + #endif +*/ + +/** + * MLD_CONFIG_CUSTOM_MEMSET + * + * Set this option and define `mld_memset` if you want to + * use a custom method to set memory instead of the standard + * library memset function. + * + * The custom implementation must have the same signature and + * behavior as the standard memset function: + * void *mld_memset(void *s, int c, size_t n) + */ +/* #define MLD_CONFIG_CUSTOM_MEMSET + #if !defined(__ASSEMBLER__) + #include + #include "src/src.h" + static MLD_INLINE void *mld_memset(void *s, int c, size_t n) + { + ... your implementation ... + } + #endif +*/ + +/** + * MLD_CONFIG_INTERNAL_API_QUALIFIER + * + * If set, this option provides an additional qualifier + * to be added to declarations of internal API functions and data. + * + * The primary use case for this option are single-CU builds, + * in which case this option can be set to `static`. + */ +/* #define MLD_CONFIG_INTERNAL_API_QUALIFIER */ + +/** + * MLD_CONFIG_CT_TESTING_ENABLED + * + * If set, mldsa-native annotates data as secret / public using + * valgrind's annotations VALGRIND_MAKE_MEM_UNDEFINED and + * VALGRIND_MAKE_MEM_DEFINED, enabling various checks for secret- + * dependent control flow of variable time execution (depending + * on the exact version of valgrind installed). + */ +/* #define MLD_CONFIG_CT_TESTING_ENABLED */ + +/** + * MLD_CONFIG_NO_ASM + * + * If this option is set, mldsa-native will be built without + * use of native code or inline assembly. + * + * By default, inline assembly is used to implement value barriers. + * Without inline assembly, mldsa-native will use a global volatile + * 'opt blocker' instead; see ct.h. + * + * Inline assembly is also used to implement a secure zeroization + * function on non-Windows platforms. If this option is set and + * the target platform is not Windows, you MUST set + * MLD_CONFIG_CUSTOM_ZEROIZE and provide a custom zeroization + * function. + * + * If this option is set, MLD_CONFIG_USE_NATIVE_BACKEND_FIPS202 and + * and MLD_CONFIG_USE_NATIVE_BACKEND_ARITH will be ignored, and no + * native backends will be used. + */ +/* #define MLD_CONFIG_NO_ASM */ + +/** + * MLD_CONFIG_NO_ASM_VALUE_BARRIER + * + * If this option is set, mldsa-native will be built without + * use of native code or inline assembly for value barriers. + * + * By default, inline assembly (if available) is used to implement + * value barriers. + * Without inline assembly, mldsa-native will use a global volatile + * 'opt blocker' instead; see ct.h. + */ +/* #define MLD_CONFIG_NO_ASM_VALUE_BARRIER */ + +/** + * MLD_CONFIG_KEYGEN_PCT + * + * Compliance with @[FIPS140_3_IG, p.87] requires a + * Pairwise Consistency Test (PCT) to be carried out on a freshly + * generated keypair before it can be exported. + * + * Set this option if such a check should be implemented. + * In this case, keypair_internal and + * keypair will return a non-zero error code if the + * PCT failed. + * + * @note This feature will drastically lower the performance of + * key generation. + * + * @note This option is incompatible with MLD_CONFIG_NO_SIGN_API + * and MLD_CONFIG_NO_VERIFY_API as the current PCT implementation + * requires signature() and verify(). + */ +/* #define MLD_CONFIG_KEYGEN_PCT */ + +/** + * MLD_CONFIG_KEYGEN_PCT_BREAKAGE_TEST + * + * If this option is set, the user must provide a runtime + * function `static inline int mld_break_pct() { ... }` to + * indicate whether the PCT should be made fail. + * + * This option only has an effect if MLD_CONFIG_KEYGEN_PCT is set. + */ +/* #define MLD_CONFIG_KEYGEN_PCT_BREAKAGE_TEST + #if !defined(__ASSEMBLER__) + #include "src/src.h" + static MLD_INLINE int mld_break_pct(void) + { + ... return 0/1 depending on whether PCT should be broken ... + } + #endif +*/ + +/** + * MLD_CONFIG_MAX_SIGNING_ATTEMPTS + * + * Upper bound on the number of rejection-sampling iterations + * performed by ML-DSA signing (@[FIPS204, Algorithm 7]). + * + * If a valid signature is not produced within this many + * attempts, signing returns MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED. + * This is useful in timing-sensitive environments that + * require a deterministic worst-case bound on signing time. + * + * For FIPS 204 compliance, this value MUST be at least 814, + * cf. @[FIPS204, Appendix C], which is chosen so that the + * signing failure rate is < 2^{-256}. + * + * Default: Largest possible value before internal counters + * would overflow. This is larger than the FIPS204 bound. + * + * In particular, in the default configuration, the signing + * failure rate is < 2^{-256}. + */ +/* #define MLD_CONFIG_MAX_SIGNING_ATTEMPTS 814 */ + +/** + * MLD_CONFIG_SERIAL_FIPS202_ONLY + * + * Set this to use a FIPS202 implementation with global state + * that supports only one active Keccak computation at a time + * (e.g. some hardware accelerators). + * + * If this option is set, ML-DSA will use FIPS202 operations + * serially, ensuring that only one SHAKE context is active + * at any given time. + * + * This allows offloading Keccak computations to a hardware + * accelerator that holds only a single Keccak state locally, + * rather than requiring support for multiple concurrent + * Keccak states. + * + * @note Depending on the target CPU, this may reduce + * performance when using software FIPS202 implementations. + * Only enable this when you have to. + */ +/* #define MLD_CONFIG_SERIAL_FIPS202_ONLY */ + +/** + * MLD_CONFIG_CONTEXT_PARAMETER + * + * Set this to add a caller-supplied context parameter to the public API + * functions, which is then forwarded unchanged to the custom callbacks + * (allocation, and signing hooks below). + * + * When this option is set, every public API function gains a trailing + * parameter + * + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE context + * + * as its last argument; its type is configured via + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE (see below). mldsa-native treats this + * value as opaque: it never dereferences it and only passes it on to the + * configurable hook macros. It is meant to carry per-caller state -- e.g. a + * pointer to a memory pool for the allocation hooks, or the resume state for + * the signing hooks -- into those hooks. + * + * When this option is unset (the default), no extra parameter is added and + * the hook macros never receive a context argument. + * + * The hooks that receive the context are the allocation hooks (see + * MLD_CONFIG_CUSTOM_ALLOC_FREE) and the signing hooks (see + * MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH); each is documented with + * its own option below. + */ +/* #define MLD_CONFIG_CONTEXT_PARAMETER */ + +/** + * MLD_CONFIG_CONTEXT_PARAMETER_TYPE + * + * Set this to define the type of the context parameter added by + * MLD_CONFIG_CONTEXT_PARAMETER. It can be any C type usable as a function + * parameter, e.g. `void *` or a pointer to a caller-defined struct such as + * `struct my_ctx *`. + * + * This option must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is + * defined; defining one without the other is a compile-time error. + */ +/* #define MLD_CONFIG_CONTEXT_PARAMETER_TYPE void* */ + +/** + * Signing hooks: MLD_CONFIG_SIGN_HOOK_RESUME / _ATTEMPT / _FINISH + * + * Three optional, independent hooks into the ML-DSA signing rejection-sampling + * loop. Each is enabled by defining the matching option, in which case the + * integration must provide the corresponding function. If a hook needs + * per-operation state, enable MLD_CONFIG_CONTEXT_PARAMETER; the context is then + * appended as the last argument. + * + * @warning This feature is experimental. Its scope, configuration and + * function signatures may change at any time, including after v1. + * + * Enabling any of the hooks requires MLD_CONFIG_NO_RANDOMIZED_API (restricting + * the public API to deterministic operations). This is because the restartable + * signing as enabled by the signing hooks only produces the uninterrupted + * signature when the randomness is fixed across calls. A logging-only use + * (attempt always returns 0; resume/finish merely observe) would be safe with + * the randomized API too, but for now the requirement is imposed uniformly on + * all three hooks. + * + * Note: Randomized signing is a shim wrapper around deterministic signing, and + * all helper functions you need to build it are exposed publicly. Thus, if you + * need a restartable, randomized signing operation, you can build your own by + * replicating the logic and adding the RNG seed to the restart context. In this + * case, please also consider letting the mldsa-native maintainers know of your + * need for randomized, restartable signing, so the feature can be appropriately + * prioritized. + * + * - MLD_CONFIG_SIGN_HOOK_ATTEMPT: int mld_sign_hook_attempt(attempt[, ctxt]) + * 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 (needs MLD_CONFIG_SIGN_HOOK_RESUME to resume; otherwise just aborts). + * Always returning 0 makes it a logging/benchmarking hook. + * + * - MLD_CONFIG_SIGN_HOOK_RESUME: uint16_t mld_sign_hook_resume([ctxt]) + * Returns the attempt to resume from (0 for a fresh operation), i.e. the one + * recorded when a previous call paused. + * + * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) + * Called on success with the succeeding attempt. Observe-only. + * + * When an option is unset, the hook is a no-op (resume to 0, attempt proceeds), + * i.e. ordinary one-shot signing. + * + * Independent of MLD_CONFIG_MAX_SIGNING_ATTEMPTS, which is a static upper bound + * on the number of signing attempts. + * + * See test/src/test_sign_hook.c for a worked example using all three. + */ +/* Enable all three signing hooks. The hooks take no context argument + * (MLD_CONFIG_CONTEXT_PARAMETER is unset); they are implemented on a + * global in examples/restartable_sign/main.c. */ +#define MLD_CONFIG_SIGN_HOOK_RESUME +#define MLD_CONFIG_SIGN_HOOK_ATTEMPT +#define MLD_CONFIG_SIGN_HOOK_FINISH +#if !defined(__ASSEMBLER__) +#include +uint16_t mld_sign_hook_resume(void); +int mld_sign_hook_attempt(uint16_t attempt); +void mld_sign_hook_finish(uint16_t attempt); +#endif /* !__ASSEMBLER__ */ + + +/** + * MLD_CONFIG_REDUCE_RAM + * + * Set this to reduce RAM usage. This trades memory for performance. + * + * For expected memory usage, see the MLD_TOTAL_ALLOC_* constants defined in + * mldsa_native.h. + * + * This option is useful for embedded systems with tight RAM constraints but + * relaxed performance requirements. + * + */ +/* #define MLD_CONFIG_REDUCE_RAM */ + +/************************* Config internals ********************************/ + +#endif /* MLD_BUILD_INTERNAL */ + +/* Default namespace + * + * Don't change this. If you need a different namespace, re-define + * MLD_CONFIG_NAMESPACE_PREFIX above instead, and remove the following. + * + * The default MLDSA namespace is + * + * PQCP_MLDSA_NATIVE_MLDSA_ + * + * e.g., PQCP_MLDSA_NATIVE_MLDSA44_ + */ + +#if defined(MLD_CONFIG_MULTILEVEL_BUILD) +/* In a multi-level build the parameter set is appended by the namespacing + * machinery, so the default prefix must not embed it. */ +#define MLD_DEFAULT_NAMESPACE_PREFIX PQCP_MLDSA_NATIVE_MLDSA +#elif MLD_CONFIG_PARAMETER_SET == 44 +#define MLD_DEFAULT_NAMESPACE_PREFIX PQCP_MLDSA_NATIVE_MLDSA44 +#elif MLD_CONFIG_PARAMETER_SET == 65 +#define MLD_DEFAULT_NAMESPACE_PREFIX PQCP_MLDSA_NATIVE_MLDSA65 +#elif MLD_CONFIG_PARAMETER_SET == 87 +#define MLD_DEFAULT_NAMESPACE_PREFIX PQCP_MLDSA_NATIVE_MLDSA87 +#endif + +#endif /* !MLD_CONFIG_H */ diff --git a/examples/restartable_sign/mldsa_native/src b/examples/restartable_sign/mldsa_native/src new file mode 120000 index 000000000..3fd9af3d7 --- /dev/null +++ b/examples/restartable_sign/mldsa_native/src @@ -0,0 +1 @@ +../../../mldsa/src/ \ No newline at end of file diff --git a/scripts/tests b/scripts/tests index 62fb7e73b..b0418312d 100755 --- a/scripts/tests +++ b/scripts/tests @@ -218,6 +218,7 @@ class TEST_TYPES(Enum): WYCHEPROOF = 23 ABICHECK = 24 SIGN_HOOK = 25 + RESTARTABLE_SIGN = 26 def is_benchmark(self): return self in [TEST_TYPES.BENCH, TEST_TYPES.BENCH_COMPONENTS] @@ -240,6 +241,7 @@ class TEST_TYPES(Enum): TEST_TYPES.MULTILEVEL_BUILD_NATIVE, TEST_TYPES.BASIC_DETERMINISTIC, TEST_TYPES.BASIC_LOWRAM, + TEST_TYPES.RESTARTABLE_SIGN, ] @staticmethod @@ -281,6 +283,8 @@ class TEST_TYPES(Enum): return "Example (mldsa-native as code package without randombytes() implementation)" if self == TEST_TYPES.BASIC_LOWRAM: return "Example (mldsa-native with reduced RAM usage)" + if self == TEST_TYPES.RESTARTABLE_SIGN: + return "Example (restartable signing via signing hooks)" if self == TEST_TYPES.MONOLITHIC_BUILD: return "Example (monobuild)" if self == TEST_TYPES.MONOLITHIC_BUILD_NATIVE: @@ -319,6 +323,8 @@ class TEST_TYPES(Enum): return "examples/basic_deterministic" if self == TEST_TYPES.BASIC_LOWRAM: return "examples/basic_lowram" + if self == TEST_TYPES.RESTARTABLE_SIGN: + return "examples/restartable_sign" if self == TEST_TYPES.MONOLITHIC_BUILD: return "examples/monolithic_build" if self == TEST_TYPES.MONOLITHIC_BUILD_NATIVE: @@ -360,6 +366,8 @@ class TEST_TYPES(Enum): return "" if self == TEST_TYPES.BASIC_LOWRAM: return "" + if self == TEST_TYPES.RESTARTABLE_SIGN: + return "" if self == TEST_TYPES.MONOLITHIC_BUILD: return "" if self == TEST_TYPES.MONOLITHIC_BUILD_NATIVE: @@ -1313,6 +1321,7 @@ def cli(): "basic", "basic_deterministic", "basic_lowram", + "restartable_sign", "monolithic_build", "monolithic_build_native", "monolithic_build_multilevel", @@ -1429,6 +1438,7 @@ def cli(): "basic", "basic_deterministic", "basic_lowram", + "restartable_sign", "monolithic_build", "monolithic_build_native", "monolithic_build_multilevel", diff --git a/test/configs/configs.yml b/test/configs/configs.yml index 352bfe465..49e1deefd 100644 --- a/test/configs/configs.yml +++ b/test/configs/configs.yml @@ -417,6 +417,34 @@ configs: MLD_CONFIG_FILE: comment: "/* No need to set this -- we _are_ already in a custom config */" + - path: examples/restartable_sign/mldsa_native/mldsa_native_config.h + description: "Restartable signing driven by the signing hooks, using global resume state" + defines: + MLD_CONFIG_NAMESPACE_PREFIX: mldsa + # Resuming is only sound with the randomized API disabled: the caller + # drives signing via the deterministic internal API with fixed `rnd`. + MLD_CONFIG_NO_RANDOMIZED_API: true + # This example keeps the resume state in a global variable, so the hooks + # need no context argument and MLD_CONFIG_CONTEXT_PARAMETER stays unset. + # For per-caller state instead, enable MLD_CONFIG_CONTEXT_PARAMETER and + # give the hooks a trailing context argument, as in test_sign_hook. + MLD_CONFIG_SIGN_HOOK_RESUME: + content: | + /* Enable all three signing hooks. The hooks take no context argument + * (MLD_CONFIG_CONTEXT_PARAMETER is unset); they are implemented on a + * global in examples/restartable_sign/main.c. */ + #define MLD_CONFIG_SIGN_HOOK_RESUME + #define MLD_CONFIG_SIGN_HOOK_ATTEMPT + #define MLD_CONFIG_SIGN_HOOK_FINISH + #if !defined(__ASSEMBLER__) + #include + uint16_t mld_sign_hook_resume(void); + int mld_sign_hook_attempt(uint16_t attempt); + void mld_sign_hook_finish(uint16_t attempt); + #endif /* !__ASSEMBLER__ */ + MLD_CONFIG_FILE: + comment: "/* No need to set this -- we _are_ already in a custom config */" + - path: test/configs/custom_heap_alloc_config.h description: "Test configuration with heap-based allocation" defines: