From 2bd50ada4fd306515e7e349b2d91907dc4a4daa5 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 0b08e5f95..56e5ddf2b 100644 --- a/mldsa/src/sign.c +++ b/mldsa/src/sign.c @@ -490,11 +490,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 d09c8505023f978ff05eeaa99622308b07ab7c5c 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 56e5ddf2b..37c1ba45c 100644 --- a/mldsa/src/sign.c +++ b/mldsa/src/sign.c @@ -139,9 +139,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 fc5d40aaf771e82205391761a510d5e46cea5e4b 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 37c1ba45c..9faf94b42 100644 --- a/mldsa/src/sign.c +++ b/mldsa/src/sign.c @@ -488,11 +488,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 @@ -586,9 +583,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) && \ @@ -600,27 +599,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 } /** @@ -992,7 +990,7 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen, 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 43998ec450d48c8ef28b291440b11cacc75e5cbc Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 12:43: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 the resume hook requires MLD_CONFIG_NO_RANDOMIZED_API and is incompatible with MLD_CONFIG_KEYGEN_PCT (whose one-shot internal signature cannot be resumed). The attempt and finish hooks also serve standalone uses with any API, e.g. logging or benchmarking the per-signature attempt distribution. 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 | 15 ++++++++ mldsa/mldsa_native_config.h | 58 ++++++++++++++++++++++++++++++ mldsa/src/common.h | 12 +++++-- mldsa/src/context.h | 72 +++++++++++++++++++++++++++++++++++++ mldsa/src/sign.c | 30 +++++++++++++--- mldsa/src/sign.h | 17 +++++++-- 6 files changed, 193 insertions(+), 11 deletions(-) diff --git a/mldsa/mldsa_native.h b/mldsa/mldsa_native.h index eb64a0ed5..a56a1ca8e 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 *****************************/ @@ -274,6 +279,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 @@ -317,6 +324,8 @@ int MLD_API_NAMESPACE(signature_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 @@ -356,6 +365,8 @@ int MLD_API_NAMESPACE(signature)( * @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 @@ -536,6 +547,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 @@ -626,6 +639,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 27570221a..3972cca75 100644 --- a/mldsa/mldsa_native_config.h +++ b/mldsa/mldsa_native_config.h @@ -702,6 +702,64 @@ */ /* #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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu) and is incompatible with MLD_CONFIG_KEYGEN_PCT. + * + * - 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_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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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..2bee1b370 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,71 @@ #error MLD_CONFIG_CONTEXT_PARAMETER_TYPE must be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is defined #endif +/* The attempt hook cannot pause the key-generation PCT: the PCT runs a single + * internal signature that mldsa-native has no way to re-enter across calls, so + * a pause (MLD_ERR_SIGNING_PAUSED) could not be resumed. A consumer needing + * both must disable MLD_CONFIG_KEYGEN_PCT and run their own PCT, which is + * straightforward to build from the public signing/verification API. */ +#if defined(MLD_CONFIG_SIGN_HOOK_ATTEMPT) && defined(MLD_CONFIG_KEYGEN_PCT) +#error MLD_CONFIG_SIGN_HOOK_ATTEMPT is incompatible with MLD_CONFIG_KEYGEN_PCT as a paused PCT cannot be resumed; disable MLD_CONFIG_KEYGEN_PCT and run your own PCT +#endif + +/* The resume hook only produces the right signature if the randomness is fixed + * across calls, so it requires the deterministic internal API. */ +#if defined(MLD_CONFIG_SIGN_HOOK_RESUME) && \ + !defined(MLD_CONFIG_NO_RANDOMIZED_API) +#error MLD_CONFIG_SIGN_HOOK_RESUME requires MLD_CONFIG_NO_RANDOMIZED_API +#endif + +/* 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 9faf94b42..37442d145 100644 --- a/mldsa/src/sign.c +++ b/mldsa/src/sign.c @@ -131,7 +131,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) @@ -586,8 +586,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) && \ @@ -908,8 +907,8 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen, { 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); @@ -924,6 +923,15 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen, 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; @@ -993,11 +1001,24 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen, /* 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); if (ret == 0) { *siglen = MLDSA_CRYPTO_BYTES; + /* Signing succeeded: record the attempt that succeeded. No-op in the + * default build. */ + mld_sign_finish(attempt, context); goto cleanup; } else if (ret != MLD_ERR_FAIL) @@ -1658,6 +1679,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 4d0027c66..7564fbe9e 100644 --- a/mldsa/src/sign.h +++ b/mldsa/src/sign.h @@ -208,6 +208,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 @@ -233,7 +235,8 @@ __contract__( assigns(object_whole(siglen)) 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) ensures(return_value == 0 ==> *siglen == MLDSA_CRYPTO_BYTES) ensures(return_value != 0 ==> *siglen == 0) ); @@ -265,6 +268,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 @@ -314,6 +319,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 @@ -499,6 +506,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 @@ -521,7 +530,7 @@ __contract__( assigns(memory_slice(sig, MLDSA_CRYPTO_BYTES)) assigns(object_whole(siglen)) ensures((return_value == 0 && *siglen == MLDSA_CRYPTO_BYTES) || - ((return_value == MLD_ERR_FAIL || return_value == MLD_ERR_OUT_OF_MEMORY || return_value == MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED) && *siglen == 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) && *siglen == 0)) ); #endif /* !MLD_CONFIG_NO_SIGN_API */ @@ -603,6 +612,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 @@ -625,7 +636,7 @@ __contract__( assigns(memory_slice(sig, MLDSA_CRYPTO_BYTES)) assigns(object_whole(siglen)) ensures((return_value == 0 && *siglen == MLDSA_CRYPTO_BYTES) || - ((return_value == MLD_ERR_FAIL || return_value == MLD_ERR_OUT_OF_MEMORY || return_value == MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED) && *siglen == 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) && *siglen == 0)) ); #endif /* !MLD_CONFIG_NO_SIGN_API */ From 9333d001b7c9ce2336238d62a3b3a6cece0c383c Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 12:19:35 +0100 Subject: [PATCH 5/9] doc: Expand MLD_CONFIG_CONTEXT_PARAMETER documentation Now that the context parameter feeds both the allocation hooks and the signing hooks, expand its documentation in mldsa_native_config.h: describe the trailing parameter added to every public API function, that mldsa-native treats the value as opaque and only forwards it to the hook macros, the unset-default behaviour, and the requirement that MLD_CONFIG_CONTEXT_PARAMETER_TYPE be defined if and only if MLD_CONFIG_CONTEXT_PARAMETER is. Documentation only. Signed-off-by: Hanno Becker --- README.md | 4 ++++ mldsa/mldsa_native_config.h | 44 +++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 9 deletions(-) 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. diff --git a/mldsa/mldsa_native_config.h b/mldsa/mldsa_native_config.h index 3972cca75..c7c5baefd 100644 --- a/mldsa/mldsa_native_config.h +++ b/mldsa/mldsa_native_config.h @@ -684,21 +684,42 @@ /** * 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* */ @@ -711,19 +732,24 @@ * 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. + * * - 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. Re-invoking signing with the same * message, sk and rnd then reproduces the signature of an uninterrupted run. * Correct only if the randomness is fixed across calls, so this requires * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / - * _extmu) and is incompatible with MLD_CONFIG_KEYGEN_PCT. + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. * * - 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. + * Always returning 0 makes it a logging/benchmarking hook. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. * * - MLD_CONFIG_SIGN_HOOK_FINISH: void mld_sign_hook_finish(attempt[, ctxt]) * Called on success with the succeeding attempt. Observe-only. From f6708437c18268396c78421da595691e0ba565dc 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 1c90989df14c09eac8abfd6d76c981d665bcbf3a Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 11:17:25 +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. 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 (incompatible with SIGN_HOOK_RESUME). The generated configs follow in a separate commit. Signed-off-by: Hanno Becker --- .github/actions/config-variations/action.yml | 17 + .github/actions/functest/action.yml | 8 +- .github/actions/multi-functest/action.yml | 10 + .github/workflows/ci.yml | 1 + Makefile | 34 +- scripts/tests | 47 ++ .../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/src/test_sign_hook.c | 514 ++++++++++++++++++ 12 files changed, 708 insertions(+), 15 deletions(-) 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..baf45afbb 100644 --- a/.github/actions/config-variations/action.yml +++ b/.github/actions/config-variations/action.yml @@ -33,6 +33,7 @@ runs: func: true kat: true acvp: true + sign_hook: false # SIGN_HOOK_RESUME is incompatible with KEYGEN_PCT opt: ${{ inputs.opt }} examples: true extra_args: "--exclude-example basic_deterministic" @@ -62,6 +63,7 @@ runs: kat: true acvp: true alloc: true + sign_hook: false # SIGN_HOOK_RESUME is incompatible with KEYGEN_PCT opt: ${{ inputs.opt }} examples: true extra_args: "--exclude-example basic_deterministic" @@ -107,6 +109,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 +125,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 +141,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 +157,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 +173,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 +189,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 +205,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 +221,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 +237,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 +253,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 +269,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 +285,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 +301,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 +317,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 +333,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 5ab00ab76..9af6e42ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -435,6 +435,7 @@ 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 diff --git a/Makefile b/Makefile index cdfc6db7c..797e14227 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,22 @@ 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 + +run_sign_hook_44: sign_hook_44 + $(W) $(MLDSA44_DIR)/bin/test_sign_hook44 +run_sign_hook_65: sign_hook_65 + $(W) $(MLDSA65_DIR)/bin/test_sign_hook65 +run_sign_hook_87: sign_hook_87 + $(W) $(MLDSA87_DIR)/bin/test_sign_hook87 +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 dbb063e0f..363a261e4 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) @@ -863,6 +881,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 @@ -881,6 +900,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: @@ -912,6 +933,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: @@ -1328,6 +1351,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", @@ -1590,6 +1628,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", @@ -1639,6 +1684,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/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 3a814a818..fec8966f8 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/src/test_sign_hook.c b/test/src/test_sign_hook.c new file mode 100644 index 000000000..f1ada0059 --- /dev/null +++ b/test/src/test_sign_hook.c @@ -0,0 +1,514 @@ +/* + * Copyright (c) The mldsa-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ +#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 "../../mldsa/src/common.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: + * 2000 samples make the default +-10% tolerance below a ~5-sigma band. 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 hundredths (i.e. + * the true mean times 100, 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. */ +#if MLD_CONFIG_PARAMETER_SET == 44 +#define MLD_SIGN_HOOK_EXPECTED_RATIO_X100 425 +#elif MLD_CONFIG_PARAMETER_SET == 65 +#define MLD_SIGN_HOOK_EXPECTED_RATIO_X100 510 +#elif MLD_CONFIG_PARAMETER_SET == 87 +#define MLD_SIGN_HOOK_EXPECTED_RATIO_X100 385 +#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. The measured ratio is the mean of DIST_ITERATIONS i.i.d. + * geometric draws, so its relative standard error is sqrt((1-p)/N) with + * p = 1/mean; the tolerance is set a few standard errors wide so a healthy + * implementation passes essentially always while a grossly wrong distribution + * is still caught. + * + * At the default N = 2000 the relative standard error is ~2% (widest level), + * so the +-10% (100 per-mille) tolerance is a ~5-sigma band: spurious failure + * probability ~1e-6 per level, while a mean off by >=15% is caught with + * probability >99%. + * + * The dedicated high-sample CI job (see the README / CI) raises N to 100000 -- + * where the standard error drops to ~0.28% and the +-10% band would be ~35 + * sigma -- and tightens the tolerance accordingly (e.g. 15 per-mille = +-1.5%, + * still a ~5-sigma band) to catch far subtler bias. */ +#define MLD_SIGN_HOOK_MIN_CHECK_N 2000 +/* Relative tolerance in per mille. Overridable from the build: the dedicated + * high-sample CI job tightens 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) + +/* + * 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`. */ +static void test_sign_ctx_begin_op(struct test_sign_hook_ctx *ctx, + int attempts_per_call, int record_stats) +{ + 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 signature is + * written to sig/siglen, and -- if `expected` is non-NULL -- checked to equal + * expected/expected_len. + * + * `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], size_t *siglen, + const uint8_t *expected, size_t expected_len, + 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, siglen, 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 && + (*siglen != expected_len || memcmp(sig, expected, *siglen) != 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]; + size_t siglen; + 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, &siglen, + test_vector_sig, sizeof(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, &siglen, + test_vector_sig, sizeof(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]; + size_t ref_siglen, siglen; + 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]. */ + rand_apc = 1 + (rand_byte % 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, &ref_siglen, + NULL, 0, (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, &siglen, ref_sig, + ref_siglen, (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, &siglen, ref_sig, + ref_siglen, (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 with the fixed test-vector message and key, each with fresh + * randomness, recording statistics for every one. This gathers an + * attempts-per-signature distribution; run with a large iteration count for an + * accurate picture. + */ +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 rnd[MLDSA_RNDBYTES]; + uint8_t sig[MLDSA_SIG_BYTES]; + size_t siglen; + + /* Fixed message and key (test_vector_sk); fresh signing randomness. */ + CHECK(randombytes(rnd, sizeof(rnd)) == 0); + + CHECK(sign_and_compare(ctx, -1, 1 /* record */, sig, &siglen, NULL, 0, + (const uint8_t *)TEST_VECTOR_MSG, + TEST_VECTOR_MSG_LEN, pre, prelen, rnd, + test_vector_sk) == 0); + } + + { + /* Measured attempts/signature ratio, in hundredths (integer), against the + * expected geometric mean. */ + uint64_t a = test_sign_ctx_attempts(ctx); + uint64_t s = test_sign_ctx_signatures(ctx); + uint64_t ratio_x100 = (s != 0) ? (100u * a + s / 2) / s : 0; + printf(" Measured attempts/signature: %llu.%02llu (expected %d.%02d)\n", + (unsigned long long)(ratio_x100 / 100), + (unsigned long long)(ratio_x100 % 100), + MLD_SIGN_HOOK_EXPECTED_RATIO_X100 / 100, + MLD_SIGN_HOOK_EXPECTED_RATIO_X100 % 100); + + /* With enough samples, check that ratio against the expected mean. All + * integer: compare 100*attempts against EXPECTED_RATIO_X100*signatures, + * allowing a +-TOL_PERMILLE per-mille relative band. */ + if (iterations >= MLD_SIGN_HOOK_MIN_CHECK_N) + { + uint64_t lhs = 100u * a; + uint64_t mid = (uint64_t)MLD_SIGN_HOOK_EXPECTED_RATIO_X100 * 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) + { + fprintf(stderr, + "ERROR: attempts/signature ratio out of range " + "(expected %d.%02d +- %d.%d%%)\n", + MLD_SIGN_HOOK_EXPECTED_RATIO_X100 / 100, + MLD_SIGN_HOOK_EXPECTED_RATIO_X100 % 100, + MLD_SIGN_HOOK_RATIO_TOL_PERMILLE / 10, + MLD_SIGN_HOOK_RATIO_TOL_PERMILLE % 10); + return 1; + } + } + } + + printf("Distribution test PASSED (%d signature(s)).\n", iterations); + return 0; +} + +/* 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"); + } +} + +#if defined(main) +int main(void); +#endif +int main(void) +{ + 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); + + r |= test_known_answer(&ctx); + r |= test_restartable(&ctx, MLD_SIGN_HOOK_ITERATIONS); + r |= test_distribution(&ctx, MLD_SIGN_HOOK_DIST_ITERATIONS); + + 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 e43af93ee97d1227e233f1cb8e9d23b1f7c17b2c 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 | 2 + .../mldsa_native/mldsa_native_config.h | 98 +- .../mldsa_native/mldsa_native_config.h | 98 +- .../mldsa_native/mldsa_native_config.h | 98 +- .../mldsa_native/mldsa_native_config.h | 98 +- .../mldsa_native/mldsa_native_config.h | 98 +- .../mldsa_native/mldsa_native_config.h | 98 +- .../mldsa_native/mldsa_native_config.h | 98 +- .../mldsa_native/mldsa_native_config.h | 98 +- .../mldsa_native/mldsa_native_config.h | 98 +- .../mldsa_native/mldsa_native_config.h | 98 +- .../mldsa_native/mldsa_native_config.h | 98 +- mldsa/mldsa_native.c | 7 + mldsa/mldsa_native_asm.S | 7 + mldsa/src/poly_kl.c | 1 - proofs/cbmc/mldsa_native_config_cbmc.h | 98 +- scripts/autogen | 8 +- test/configs/break_pct_config.h | 98 +- test/configs/custom_heap_alloc_config.h | 98 +- test/configs/custom_memcpy_config.h | 98 +- test/configs/custom_memset_config.h | 98 +- .../custom_native_capability_config_0.h | 98 +- .../custom_native_capability_config_1.h | 98 +- ...stom_native_capability_config_CPUID_AVX2.h | 98 +- ...native_capability_config_ID_AA64PFR1_EL1.h | 98 +- test/configs/custom_randombytes_config.h | 98 +- test/configs/custom_stdlib_config.h | 98 +- test/configs/custom_zeroize_config.h | 98 +- test/configs/low_signing_bound_config.h | 98 +- test/configs/no_asm_config.h | 98 +- test/configs/serial_fips202_config.h | 98 +- test/configs/test_alloc_config.h | 98 +- test/configs/test_sign_hook_config.h | 845 ++++++++++++++++++ 33 files changed, 3324 insertions(+), 192 deletions(-) create mode 100644 test/configs/test_sign_hook_config.h diff --git a/BIBLIOGRAPHY.md b/BIBLIOGRAPHY.md index 399624239..55f7fe1df 100644 --- a/BIBLIOGRAPHY.md +++ b/BIBLIOGRAPHY.md @@ -103,6 +103,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` @@ -166,6 +167,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) ### `HYBRID` diff --git a/examples/basic_deterministic/mldsa_native/mldsa_native_config.h b/examples/basic_deterministic/mldsa_native/mldsa_native_config.h index 955438a78..07c29cb84 100644 --- a/examples/basic_deterministic/mldsa_native/mldsa_native_config.h +++ b/examples/basic_deterministic/mldsa_native/mldsa_native_config.h @@ -700,24 +700,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 acfdff840..f9a9eb628 100644 --- a/examples/basic_lowram/mldsa_native/mldsa_native_config.h +++ b/examples/basic_lowram/mldsa_native/mldsa_native_config.h @@ -699,24 +699,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 c09b35ae3..54fca86df 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,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 20b51248e..86591c081 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,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 3c964671d..5973b3ea9 100644 --- a/examples/custom_backend/mldsa_native/mldsa_native_config.h +++ b/examples/custom_backend/mldsa_native/mldsa_native_config.h @@ -696,24 +696,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 aec2d23be..71e36b6a8 100644 --- a/examples/monolithic_build/mldsa_native/mldsa_native_config.h +++ b/examples/monolithic_build/mldsa_native/mldsa_native_config.h @@ -699,24 +699,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 aa310d3cc..6995a7caa 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,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 429fef3f1..5bf42cee4 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,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 ce8d5c004..4e0f50aba 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,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 c309a91d4..afb536295 100644 --- a/examples/multilevel_build/mldsa_native/mldsa_native_config.h +++ b/examples/multilevel_build/mldsa_native/mldsa_native_config.h @@ -699,24 +699,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 2a08c77cf..80c9e7830 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,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 c747db4cb..345a9cb82 100644 --- a/proofs/cbmc/mldsa_native_config_cbmc.h +++ b/proofs/cbmc/mldsa_native_config_cbmc.h @@ -698,24 +698,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 82a170f42..cc5081ed8 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 2282dc4da..3137d4a96 100644 --- a/test/configs/break_pct_config.h +++ b/test/configs/break_pct_config.h @@ -704,24 +704,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 5ff48c1dc..c6df07d70 100644 --- a/test/configs/custom_heap_alloc_config.h +++ b/test/configs/custom_heap_alloc_config.h @@ -717,24 +717,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 7b1bd0b95..224a43e6e 100644 --- a/test/configs/custom_memcpy_config.h +++ b/test/configs/custom_memcpy_config.h @@ -707,24 +707,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 e331838e0..0cac390a4 100644 --- a/test/configs/custom_memset_config.h +++ b/test/configs/custom_memset_config.h @@ -706,24 +706,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 7c0ed5be5..11ef3315a 100644 --- a/test/configs/custom_native_capability_config_0.h +++ b/test/configs/custom_native_capability_config_0.h @@ -706,24 +706,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 23bd304fa..3b54dd62a 100644 --- a/test/configs/custom_native_capability_config_1.h +++ b/test/configs/custom_native_capability_config_1.h @@ -705,24 +705,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 8cea3d0ac..8c546986c 100644 --- a/test/configs/custom_native_capability_config_CPUID_AVX2.h +++ b/test/configs/custom_native_capability_config_CPUID_AVX2.h @@ -737,24 +737,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 6af9ecc2f..304774e88 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,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 04822b8fc..fbc87c955 100644 --- a/test/configs/custom_randombytes_config.h +++ b/test/configs/custom_randombytes_config.h @@ -699,24 +699,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 d3904ccb1..02f72e4de 100644 --- a/test/configs/custom_stdlib_config.h +++ b/test/configs/custom_stdlib_config.h @@ -715,24 +715,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 622742844..ad703b52e 100644 --- a/test/configs/custom_zeroize_config.h +++ b/test/configs/custom_zeroize_config.h @@ -700,24 +700,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 e3167be3b..d18d0d053 100644 --- a/test/configs/low_signing_bound_config.h +++ b/test/configs/low_signing_bound_config.h @@ -702,24 +702,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 012c1a388..5b56bf555 100644 --- a/test/configs/no_asm_config.h +++ b/test/configs/no_asm_config.h @@ -701,24 +701,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 afad1d06f..fc8dbda73 100644 --- a/test/configs/serial_fips202_config.h +++ b/test/configs/serial_fips202_config.h @@ -699,24 +699,108 @@ /** * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 3214fd754..b738b1feb 100644 --- a/test/configs/test_alloc_config.h +++ b/test/configs/test_alloc_config.h @@ -707,24 +707,108 @@ 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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..5543104c4 --- /dev/null +++ b/test/configs/test_sign_hook_config.h @@ -0,0 +1,845 @@ +/* + * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 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 44387f57bef94e3930263e1392301721be5b70da Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Thu, 2 Jul 2026 11:15:15 +0100 Subject: [PATCH 9/9] Examples: Add example for restartable signing via signing hooks Signed-off-by: Hanno Becker --- .github/actions/config-variations/action.yml | 8 +- .github/workflows/base.yml | 3 + BIBLIOGRAPHY.md | 2 + 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 | 260 ++++++ .../mldsa_native/mldsa_native.h | 1 + .../mldsa_native/mldsa_native_config.h | 836 ++++++++++++++++++ examples/restartable_sign/mldsa_native/src | 1 + scripts/tests | 10 + test/configs/configs.yml | 28 + 15 files changed, 1309 insertions(+), 2 deletions(-) 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/actions/config-variations/action.yml b/.github/actions/config-variations/action.yml index baf45afbb..9491324a0 100644 --- a/.github/actions/config-variations/action.yml +++ b/.github/actions/config-variations/action.yml @@ -36,7 +36,9 @@ runs: sign_hook: false # SIGN_HOOK_RESUME is 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 @@ -66,7 +68,9 @@ runs: sign_hook: false # SIGN_HOOK_RESUME is 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 diff --git a/.github/workflows/base.yml b/.github/workflows/base.yml index 9f8ce9375..132a8a051 100644 --- a/.github/workflows/base.yml +++ b/.github/workflows/base.yml @@ -237,6 +237,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/BIBLIOGRAPHY.md b/BIBLIOGRAPHY.md index 55f7fe1df..a9ca7f5fb 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) @@ -134,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) 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..3ca712295 --- /dev/null +++ b/examples/restartable_sign/main.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) The mldsa-native project authors + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT + */ + +#include +#include +#include +#include + +/* Import public mldsa-native API. + * + * This build sets MLD_CONFIG_NO_RANDOMIZED_API and the three signing hooks + * MLD_CONFIG_SIGN_HOOK_RESUME / MLD_CONFIG_SIGN_HOOK_ATTEMPT / + * MLD_CONFIG_SIGN_HOOK_FINISH (see mldsa_native/mldsa_native_config.h). The + * hooks make signing restartable: the + * attempt hook can pause the rejection-sampling loop, and the resume hook lets + * a later call continue from where it left off, reproducing the exact same + * signature as an uninterrupted run. + */ +#include +#include "expected_test_vectors.h" + +/* Convenience abbreviations for the key and signature sizes, derived from the + * configured parameter set. */ +#define MLDSA_PK_BYTES MLDSA_PUBLICKEYBYTES(MLD_CONFIG_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 the same randomness that would be generated by our + * deterministic test rng such that we can produce the same keys and signatures + * as in the basic example -- and, crucially, the same signature regardless of + * how the signing work is split across restarts. + */ +/* # !!! WARNING !!! + * This is for TESTING ONLY. + * For key generation, you MUST USE cryptographic randomness outside of + * testing. + * For signing, you SHOULD provide cryptographic randomness. If you require + * deterministic signing, set sign_rnd to all zeros. + */ + +#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; + +/* Absolute 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], size_t *siglen, + 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, siglen, (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_KEYPAIR_API) +static int example_keygen(uint8_t pk[MLDSA_PK_BYTES], + uint8_t sk[MLDSA_SK_BYTES]) +{ + printf("Generating keypair... "); + CHECK(mldsa_keypair_internal(pk, sk, test_vector_rnd) == 0); + CHECK(memcmp(pk, test_vector_pk, MLDSA_PK_BYTES) == 0); + CHECK(memcmp(sk, test_vector_sk, MLDSA_SK_BYTES) == 0); + printf("DONE\n"); + return 0; +} +#else /* !MLD_CONFIG_NO_KEYPAIR_API */ +static int example_keygen(uint8_t pk[MLDSA_PK_BYTES], + uint8_t sk[MLDSA_SK_BYTES]) +{ + /* No keygen API: fall back to the reference key pair. */ + memcpy(pk, test_vector_pk, MLDSA_PK_BYTES); + memcpy(sk, test_vector_sk, MLDSA_SK_BYTES); + printf("Generating keypair... SKIPPED (keygen API disabled)\n"); + return 0; +} +#endif /* MLD_CONFIG_NO_KEYPAIR_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]; + size_t siglen, ref_siglen; + 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, &siglen, 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, &ref_siglen, 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(siglen == ref_siglen && memcmp(sig, ref_sig, siglen) == 0); + CHECK(siglen == sizeof(test_vector_sig)); + CHECK(memcmp(sig, test_vector_sig, siglen) == 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 */ + +#if !defined(MLD_CONFIG_NO_VERIFY_API) +static int example_verify(const uint8_t pk[MLDSA_PK_BYTES]) +{ + printf("Verifying signature... "); + CHECK(mldsa_verify(test_vector_sig, sizeof(test_vector_sig), + (const uint8_t *)TEST_VECTOR_MSG, TEST_VECTOR_MSG_LEN, + (const uint8_t *)TEST_VECTOR_CTX, TEST_VECTOR_CTX_LEN, + pk) == 0); + printf("DONE\n"); + return 0; +} +#else /* !MLD_CONFIG_NO_VERIFY_API */ +static int example_verify(const uint8_t pk[MLDSA_PK_BYTES]) +{ + (void)pk; + printf("Verifying signature... SKIPPED (verify API disabled)\n"); + return 0; +} +#endif /* MLD_CONFIG_NO_VERIFY_API */ + +int main(void) +{ + int r = 0; + uint8_t pk[MLDSA_PK_BYTES]; + uint8_t sk[MLDSA_SK_BYTES]; + + 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); + + r |= example_keygen(pk, sk); + r |= example_sign(sk); + r |= example_verify(pk); + + if (r) + { + 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..08131211e --- /dev/null +++ b/examples/restartable_sign/mldsa_native/mldsa_native_config.h @@ -0,0 +1,836 @@ +/* + * 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. + * + * - 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. Re-invoking signing with the same + * message, sk and rnd then reproduces the signature of an uninterrupted run. + * Correct only if the randomness is fixed across calls, so this requires + * MLD_CONFIG_NO_RANDOMIZED_API (deterministic mld_sign_signature_internal / + * _extmu), which in turn rules out MLD_CONFIG_KEYGEN_PCT. + * + * - 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. Incompatible with + * MLD_CONFIG_KEYGEN_PCT: the PCT signs a single internal signature that + * mldsa-native cannot re-enter, so a paused PCT could not be resumed. + * + * - 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 (the hard attempt bound, >= + * 814 for FIPS 204, returning MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED): do not lower + * that to pause/restart -- use the attempt hook (MLD_ERR_SIGNING_PAUSED) + * instead. + * + * 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 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 363a261e4..4acb8b93a 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: @@ -1310,6 +1318,7 @@ def cli(): "basic", "basic_deterministic", "basic_lowram", + "restartable_sign", "monolithic_build", "monolithic_build_native", "monolithic_build_multilevel", @@ -1426,6 +1435,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: