From 98308e04e6aee529d9752261a1ffb0836af7072a Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 10:28:17 +0100 Subject: [PATCH 1/8] 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 c0098a5776dd83e8ef2caeb15c69703883bdec57 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 11:16:38 +0100 Subject: [PATCH 2/8] 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 95a88b382..92116eb6d 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 c5b105b20527f7fead3bed4d4e9ff19975729444 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 12:42:40 +0100 Subject: [PATCH 3/8] 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 9b967c717fca24cef61ae56e93153d7d9445230b Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 12:43:18 +0100 Subject: [PATCH 4/8] 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 | 27 ++++++++++++-- mldsa/src/sign.h | 17 +++++++-- 6 files changed, 192 insertions(+), 9 deletions(-) diff --git a/mldsa/mldsa_native.h b/mldsa/mldsa_native.h index b40a876bc..bbb98b88f 100644 --- a/mldsa/mldsa_native.h +++ b/mldsa/mldsa_native.h @@ -137,6 +137,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) /****************************** Function API **********************************/ @@ -331,6 +336,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 @@ -374,6 +381,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 @@ -413,6 +422,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 @@ -593,6 +604,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 @@ -683,6 +696,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 3bf487db3..0d2026af9 100644 --- a/mldsa/mldsa_native_config.h +++ b/mldsa/mldsa_native_config.h @@ -714,6 +714,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 92116eb6d..37532f755 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..a69454a3d 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 resume hook cannot drive the key-generation PCT: the PCT runs a single + * internal signature that mldsa-native has no way to re-enter across calls, so + * it cannot 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_RESUME) && defined(MLD_CONFIG_KEYGEN_PCT) +#error MLD_CONFIG_SIGN_HOOK_RESUME is incompatible with MLD_CONFIG_KEYGEN_PCT as the 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..da435f3de 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) @@ -908,8 +908,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 +924,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 +1002,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_RESUME. */ + 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 +1680,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 d7e65fbac..dea934bc9 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 b251646327b40623a0618904627155bcdddc7b2f Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 12:19:35 +0100 Subject: [PATCH 5/8] 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 --- mldsa/mldsa_native_config.h | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/mldsa/mldsa_native_config.h b/mldsa/mldsa_native_config.h index 0d2026af9..9280d07f9 100644 --- a/mldsa/mldsa_native_config.h +++ b/mldsa/mldsa_native_config.h @@ -696,21 +696,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* */ From 925852e681421751d9e778e4530dd20c1f148772 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 11:17:25 +0100 Subject: [PATCH 6/8] 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 81bd34aca4d9594e90860dd919ac9c4cebd37cf6 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 11:17:25 +0100 Subject: [PATCH 7/8] 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 ++ test/configs/configs.yml | 36 ++ test/mk/components.mk | 27 +- test/mk/rules.mk | 30 ++ test/src/test_sign_hook.c | 484 +++++++++++++++++++ 10 files changed, 682 insertions(+), 12 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 9f26dbb3f..e0157d714 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,11 +123,11 @@ 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 }} 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 68f5429c8..a945448fa 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/configs/configs.yml b/test/configs/configs.yml index 905019c54..ce0d95867 100644 --- a/test/configs/configs.yml +++ b/test/configs/configs.yml @@ -471,6 +471,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 a93056b8f..920a088e9 100644 --- a/test/mk/components.mk +++ b/test/mk/components.mk @@ -20,8 +20,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 @@ -52,6 +53,14 @@ $(MLDSA65_ALLOC_OBJS): CFLAGS += -DMLD_CONFIG_PARAMETER_SET=65 -DMLD_CONFIG_FILE MLDSA87_ALLOC_OBJS = $(call MAKE_OBJS,$(MLDSA87_DIR)/alloc,$(SOURCES) $(FIPS202_SRCS)) $(MLDSA87_ALLOC_OBJS): CFLAGS += -DMLD_CONFIG_PARAMETER_SET=87 -DMLD_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" +# Sign-hook test object files - same sources but with the sign-hook config +MLDSA44_SIGN_HOOK_OBJS = $(call MAKE_OBJS,$(MLDSA44_DIR)/sign_hook,$(SOURCES) $(FIPS202_SRCS)) +$(MLDSA44_SIGN_HOOK_OBJS): CFLAGS += -DMLD_CONFIG_PARAMETER_SET=44 -DMLD_CONFIG_FILE=\"../test/configs/test_sign_hook_config.h\" +MLDSA65_SIGN_HOOK_OBJS = $(call MAKE_OBJS,$(MLDSA65_DIR)/sign_hook,$(SOURCES) $(FIPS202_SRCS)) +$(MLDSA65_SIGN_HOOK_OBJS): CFLAGS += -DMLD_CONFIG_PARAMETER_SET=65 -DMLD_CONFIG_FILE=\"../test/configs/test_sign_hook_config.h\" +MLDSA87_SIGN_HOOK_OBJS = $(call MAKE_OBJS,$(MLDSA87_DIR)/sign_hook,$(SOURCES) $(FIPS202_SRCS)) +$(MLDSA87_SIGN_HOOK_OBJS): CFLAGS += -DMLD_CONFIG_PARAMETER_SET=87 -DMLD_CONFIG_FILE=\"../test/configs/test_sign_hook_config.h\" + CFLAGS += -Imldsa $(BUILD_DIR)/libmldsa44.a: $(MLDSA44_OBJS) @@ -68,6 +77,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) $(MLDSA44_DIR)/bin/bench_mldsa44: CFLAGS += -Itest/hal @@ -85,6 +99,10 @@ $(MLDSA44_DIR)/test/src/test_alloc.c.o: CFLAGS += -DMLD_CONFIG_FILE=\"../test/co $(MLDSA65_DIR)/test/src/test_alloc.c.o: CFLAGS += -DMLD_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" $(MLDSA87_DIR)/test/src/test_alloc.c.o: CFLAGS += -DMLD_CONFIG_FILE=\"../test/configs/test_alloc_config.h\" +$(MLDSA44_DIR)/test/src/test_sign_hook.c.o: CFLAGS += -DMLD_CONFIG_FILE=\"../test/configs/test_sign_hook_config.h\" +$(MLDSA65_DIR)/test/src/test_sign_hook.c.o: CFLAGS += -DMLD_CONFIG_FILE=\"../test/configs/test_sign_hook_config.h\" +$(MLDSA87_DIR)/test/src/test_sign_hook.c.o: CFLAGS += -DMLD_CONFIG_FILE=\"../test/configs/test_sign_hook_config.h\" + $(MLDSA44_DIR)/test/src/test_unit.c.o: CFLAGS += $(UNIT_CFLAGS) $(MLDSA65_DIR)/test/src/test_unit.c.o: CFLAGS += $(UNIT_CFLAGS) $(MLDSA87_DIR)/test/src/test_unit.c.o: CFLAGS += $(UNIT_CFLAGS) @@ -129,6 +147,12 @@ $(BUILD_DIR)/$(1)/bin/test_alloc$(subst mldsa,,$(1)): LDLIBS += -L$(BUILD_DIR) - $(BUILD_DIR)/$(1)/bin/test_alloc$(subst mldsa,,$(1)): $(BUILD_DIR)/$(1)/test/src/test_alloc.c.o $(BUILD_DIR)/lib$(1)_alloc.a $(call MAKE_OBJS, $(BUILD_DIR)/$(1), $(wildcard test/notrandombytes/*.c)) endef +# Special rule for test_sign_hook - link against sign_hook libraries with the sign-hook config +define ADD_SOURCE_SIGN_HOOK +$(BUILD_DIR)/$(1)/bin/test_sign_hook$(subst mldsa,,$(1)): LDLIBS += -L$(BUILD_DIR) -l$(1)_sign_hook +$(BUILD_DIR)/$(1)/bin/test_sign_hook$(subst mldsa,,$(1)): $(BUILD_DIR)/$(1)/test/src/test_sign_hook.c.o $(BUILD_DIR)/lib$(1)_sign_hook.a $(call MAKE_OBJS, $(BUILD_DIR)/$(1), $(wildcard test/notrandombytes/*.c)) +endef + $(foreach scheme,mldsa44 mldsa65 mldsa87, \ $(foreach test,$(ACVP_TESTS), \ $(eval $(call ADD_SOURCE,$(scheme),$(test),acvp)) \ @@ -145,6 +169,7 @@ $(foreach scheme,mldsa44 mldsa65 mldsa87, \ $(eval $(call ADD_SOURCE,$(scheme),test_rng_fail,src)) \ $(eval $(call ADD_SOURCE_UNIT,$(scheme))) \ $(eval $(call ADD_SOURCE_ALLOC,$(scheme))) \ + $(eval $(call ADD_SOURCE_SIGN_HOOK,$(scheme))) \ ) # All tests get EXTRA_SOURCES diff --git a/test/mk/rules.mk b/test/mk/rules.mk index ba8d8e8e1..3fe6b0301 100644 --- a/test/mk/rules.mk +++ b/test/mk/rules.mk @@ -114,6 +114,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..0929c847e --- /dev/null +++ b/test/src/test_sign_hook.c @@ -0,0 +1,484 @@ +/* + * 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. */ +#ifndef MLD_SIGN_HOOK_DIST_ITERATIONS +#define MLD_SIGN_HOOK_DIST_ITERATIONS NTESTS +#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_API_PARAMETER_SET == 44 +#define MLD_SIGN_HOOK_EXPECTED_RATIO_X100 425 +#elif MLD_CONFIG_API_PARAMETER_SET == 65 +#define MLD_SIGN_HOOK_EXPECTED_RATIO_X100 510 +#elif MLD_CONFIG_API_PARAMETER_SET == 87 +#define MLD_SIGN_HOOK_EXPECTED_RATIO_X100 385 +#endif + +/* 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 5% relative tolerance comfortably contains the deviation seen + * with probability 99.99% at this sample count (~3.5% for the widest level), so + * a healthy implementation passes essentially always. */ +#define MLD_SIGN_HOOK_MIN_CHECK_N 10000 +#define MLD_SIGN_HOOK_RATIO_TOL_PCT 5 /* relative tolerance, percent */ + +/* 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[CRYPTO_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[CRYPTO_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[CRYPTO_BYTES]; + uint8_t sig[CRYPTO_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[CRYPTO_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_PCT% 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_PCT * mid / 100u; + 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%%)\n", + MLD_SIGN_HOOK_EXPECTED_RATIO_X100 / 100, + MLD_SIGN_HOOK_EXPECTED_RATIO_X100 % 100, + MLD_SIGN_HOOK_RATIO_TOL_PCT); + 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"); + } +} + +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 */ + +int main(void) +{ + printf("Signing API disabled; nothing to test.\n"); + return 0; +} + +#endif /* MLD_CONFIG_NO_SIGN_API */ From 9686b6610a639c0a201efe05364b57bddf745582 Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Wed, 1 Jul 2026 11:17:34 +0100 Subject: [PATCH 8/8] 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 | 93 +- .../mldsa_native/mldsa_native_config.h | 93 +- .../mldsa_native/mldsa_native_config.h | 93 +- .../mldsa_native/mldsa_native_config.h | 93 +- .../mldsa_native/mldsa_native_config.h | 93 +- .../mldsa_native/mldsa_native_config.h | 93 +- .../mldsa_native/mldsa_native_config.h | 93 +- .../mldsa_native/mldsa_native_config.h | 93 +- .../mldsa_native/mldsa_native_config.h | 93 +- .../mldsa_native/mldsa_native_config.h | 93 +- .../mldsa_native/mldsa_native_config.h | 93 +- mldsa/mldsa_native.c | 7 + mldsa/mldsa_native_asm.S | 7 + mldsa/src/poly_kl.c | 1 - proofs/cbmc/mldsa_native_config_cbmc.h | 93 +- scripts/autogen | 8 +- test/configs/break_pct_config.h | 93 +- test/configs/custom_heap_alloc_config.h | 93 +- test/configs/custom_memcpy_config.h | 93 +- test/configs/custom_memset_config.h | 93 +- .../custom_native_capability_config_0.h | 93 +- .../custom_native_capability_config_1.h | 93 +- ...stom_native_capability_config_CPUID_AVX2.h | 93 +- ...native_capability_config_ID_AA64PFR1_EL1.h | 93 +- test/configs/custom_randombytes_config.h | 93 +- test/configs/custom_stdlib_config.h | 93 +- test/configs/custom_zeroize_config.h | 93 +- test/configs/low_signing_bound_config.h | 93 +- test/configs/no_asm_config.h | 93 +- test/configs/serial_fips202_config.h | 93 +- test/configs/test_alloc_config.h | 93 +- test/configs/test_sign_hook_config.h | 852 ++++++++++++++++++ 33 files changed, 3196 insertions(+), 192 deletions(-) create mode 100644 test/configs/test_sign_hook_config.h diff --git a/BIBLIOGRAPHY.md b/BIBLIOGRAPHY.md index 96430656d..4aa4f7ae5 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 33f8bda55..67db8d905 100644 --- a/examples/basic_deterministic/mldsa_native/mldsa_native_config.h +++ b/examples/basic_deterministic/mldsa_native/mldsa_native_config.h @@ -712,24 +712,103 @@ /** * 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. + * + * - 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/examples/basic_lowram/mldsa_native/mldsa_native_config.h b/examples/basic_lowram/mldsa_native/mldsa_native_config.h index bee3f7bd6..4bb52b82f 100644 --- a/examples/basic_lowram/mldsa_native/mldsa_native_config.h +++ b/examples/basic_lowram/mldsa_native/mldsa_native_config.h @@ -711,24 +711,103 @@ /** * 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. + * + * - 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/examples/bring_your_own_fips202/mldsa_native/mldsa_native_config.h b/examples/bring_your_own_fips202/mldsa_native/mldsa_native_config.h index 774c48913..0d0e95ce2 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 @@ -712,24 +712,103 @@ /** * 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. + * + * - 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/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 3885fb8e9..26a29e7b2 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 @@ -713,24 +713,103 @@ /** * 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. + * + * - 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/examples/custom_backend/mldsa_native/mldsa_native_config.h b/examples/custom_backend/mldsa_native/mldsa_native_config.h index c7b168b41..a51005808 100644 --- a/examples/custom_backend/mldsa_native/mldsa_native_config.h +++ b/examples/custom_backend/mldsa_native/mldsa_native_config.h @@ -708,24 +708,103 @@ /** * 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. + * + * - 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/examples/monolithic_build/mldsa_native/mldsa_native_config.h b/examples/monolithic_build/mldsa_native/mldsa_native_config.h index 79bce13a2..20c6f2245 100644 --- a/examples/monolithic_build/mldsa_native/mldsa_native_config.h +++ b/examples/monolithic_build/mldsa_native/mldsa_native_config.h @@ -711,24 +711,103 @@ /** * 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. + * + * - 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/examples/monolithic_build_multilevel/mldsa_native/mldsa_native_config.h b/examples/monolithic_build_multilevel/mldsa_native/mldsa_native_config.h index 2d3b4dd74..d84ccdc62 100644 --- a/examples/monolithic_build_multilevel/mldsa_native/mldsa_native_config.h +++ b/examples/monolithic_build_multilevel/mldsa_native/mldsa_native_config.h @@ -713,24 +713,103 @@ /** * 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. + * + * - 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/examples/monolithic_build_multilevel_native/mldsa_native/mldsa_native_config.h b/examples/monolithic_build_multilevel_native/mldsa_native/mldsa_native_config.h index f46204d25..4b914c484 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 @@ -719,24 +719,103 @@ 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. + * + * - 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/examples/monolithic_build_native/mldsa_native/mldsa_native_config.h b/examples/monolithic_build_native/mldsa_native/mldsa_native_config.h index 934e35365..958ff7647 100644 --- a/examples/monolithic_build_native/mldsa_native/mldsa_native_config.h +++ b/examples/monolithic_build_native/mldsa_native/mldsa_native_config.h @@ -711,24 +711,103 @@ /** * 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. + * + * - 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/examples/multilevel_build/mldsa_native/mldsa_native_config.h b/examples/multilevel_build/mldsa_native/mldsa_native_config.h index c31b2d77a..faac3bdb5 100644 --- a/examples/multilevel_build/mldsa_native/mldsa_native_config.h +++ b/examples/multilevel_build/mldsa_native/mldsa_native_config.h @@ -712,24 +712,103 @@ /** * 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. + * + * - 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/examples/multilevel_build_native/mldsa_native/mldsa_native_config.h b/examples/multilevel_build_native/mldsa_native/mldsa_native_config.h index 609bbc4cc..83e7729c3 100644 --- a/examples/multilevel_build_native/mldsa_native/mldsa_native_config.h +++ b/examples/multilevel_build_native/mldsa_native/mldsa_native_config.h @@ -710,24 +710,103 @@ /** * 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. + * + * - 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/mldsa_native.c b/mldsa/mldsa_native.c index 49a4cd1ed..4590d6fe8 100644 --- a/mldsa/mldsa_native.c +++ b/mldsa/mldsa_native.c @@ -172,6 +172,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_ @@ -229,6 +230,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 @@ -310,6 +312,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 @@ -452,6 +455,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 30a0b571d..c6b5b0a47 100644 --- a/mldsa/mldsa_native_asm.S +++ b/mldsa/mldsa_native_asm.S @@ -185,6 +185,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_ @@ -242,6 +243,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 @@ -323,6 +325,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 @@ -465,6 +468,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 6f463f041..65e351623 100644 --- a/proofs/cbmc/mldsa_native_config_cbmc.h +++ b/proofs/cbmc/mldsa_native_config_cbmc.h @@ -710,24 +710,103 @@ /** * 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. + * + * - 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/scripts/autogen b/scripts/autogen index b1e1c3143..c517fd502 100755 --- a/scripts/autogen +++ b/scripts/autogen @@ -3223,8 +3223,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 67d1ffd88..2b84ed711 100644 --- a/test/configs/break_pct_config.h +++ b/test/configs/break_pct_config.h @@ -716,24 +716,103 @@ 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. + * + * - 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/test/configs/custom_heap_alloc_config.h b/test/configs/custom_heap_alloc_config.h index e3c8cf30d..b76466a0c 100644 --- a/test/configs/custom_heap_alloc_config.h +++ b/test/configs/custom_heap_alloc_config.h @@ -729,24 +729,103 @@ 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. + * + * - 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/test/configs/custom_memcpy_config.h b/test/configs/custom_memcpy_config.h index 97bc1a86c..d4bd6bcda 100644 --- a/test/configs/custom_memcpy_config.h +++ b/test/configs/custom_memcpy_config.h @@ -719,24 +719,103 @@ 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. + * + * - 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/test/configs/custom_memset_config.h b/test/configs/custom_memset_config.h index 7de4fba94..71f1bd01d 100644 --- a/test/configs/custom_memset_config.h +++ b/test/configs/custom_memset_config.h @@ -718,24 +718,103 @@ 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. + * + * - 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/test/configs/custom_native_capability_config_0.h b/test/configs/custom_native_capability_config_0.h index e1950b289..9d74a01b3 100644 --- a/test/configs/custom_native_capability_config_0.h +++ b/test/configs/custom_native_capability_config_0.h @@ -718,24 +718,103 @@ 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. + * + * - 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/test/configs/custom_native_capability_config_1.h b/test/configs/custom_native_capability_config_1.h index 4e4ba3199..e14ad52db 100644 --- a/test/configs/custom_native_capability_config_1.h +++ b/test/configs/custom_native_capability_config_1.h @@ -717,24 +717,103 @@ 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. + * + * - 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/test/configs/custom_native_capability_config_CPUID_AVX2.h b/test/configs/custom_native_capability_config_CPUID_AVX2.h index 0e1a35413..b95d2f90c 100644 --- a/test/configs/custom_native_capability_config_CPUID_AVX2.h +++ b/test/configs/custom_native_capability_config_CPUID_AVX2.h @@ -749,24 +749,103 @@ 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. + * + * - 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/test/configs/custom_native_capability_config_ID_AA64PFR1_EL1.h b/test/configs/custom_native_capability_config_ID_AA64PFR1_EL1.h index 5157df436..590a97ecc 100644 --- a/test/configs/custom_native_capability_config_ID_AA64PFR1_EL1.h +++ b/test/configs/custom_native_capability_config_ID_AA64PFR1_EL1.h @@ -736,24 +736,103 @@ 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. + * + * - 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/test/configs/custom_randombytes_config.h b/test/configs/custom_randombytes_config.h index 3ae7f047c..85c01cefc 100644 --- a/test/configs/custom_randombytes_config.h +++ b/test/configs/custom_randombytes_config.h @@ -711,24 +711,103 @@ 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. + * + * - 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/test/configs/custom_stdlib_config.h b/test/configs/custom_stdlib_config.h index 900ef2489..20697197a 100644 --- a/test/configs/custom_stdlib_config.h +++ b/test/configs/custom_stdlib_config.h @@ -727,24 +727,103 @@ 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. + * + * - 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/test/configs/custom_zeroize_config.h b/test/configs/custom_zeroize_config.h index 8c0ef4b3e..7595da391 100644 --- a/test/configs/custom_zeroize_config.h +++ b/test/configs/custom_zeroize_config.h @@ -712,24 +712,103 @@ 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. + * + * - 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/test/configs/low_signing_bound_config.h b/test/configs/low_signing_bound_config.h index f3f44d583..6e2e8800a 100644 --- a/test/configs/low_signing_bound_config.h +++ b/test/configs/low_signing_bound_config.h @@ -714,24 +714,103 @@ /** * 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. + * + * - 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/test/configs/no_asm_config.h b/test/configs/no_asm_config.h index e9fdd0b66..a4d0de5e1 100644 --- a/test/configs/no_asm_config.h +++ b/test/configs/no_asm_config.h @@ -713,24 +713,103 @@ 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. + * + * - 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/test/configs/serial_fips202_config.h b/test/configs/serial_fips202_config.h index 55521b2f0..8d1cb4931 100644 --- a/test/configs/serial_fips202_config.h +++ b/test/configs/serial_fips202_config.h @@ -711,24 +711,103 @@ /** * 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. + * + * - 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/test/configs/test_alloc_config.h b/test/configs/test_alloc_config.h index 06d01990a..61717a0c4 100644 --- a/test/configs/test_alloc_config.h +++ b/test/configs/test_alloc_config.h @@ -719,24 +719,103 @@ 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. + * + * - 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/test/configs/test_sign_hook_config.h b/test/configs/test_sign_hook_config.h new file mode 100644 index 000000000..04dd885a5 --- /dev/null +++ b/test/configs/test_sign_hook_config.h @@ -0,0 +1,852 @@ +/* + * 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 crypto_sign_keypair, crypto_sign_keypair_internal, + * crypto_sign_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 crypto_sign_signature, + * crypto_sign_signature_extmu, crypto_sign_signature_internal, + * crypto_sign_signature_pre_hash_internal, + * crypto_sign_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 crypto_sign_verify, + * crypto_sign_verify_extmu, crypto_sign_verify_internal, + * crypto_sign_verify_pre_hash_internal, + * crypto_sign_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 + * crypto_sign_keypair_internal, crypto_sign_signature_internal, + * and crypto_sign_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 (crypto_sign_keypair, + * crypto_sign_signature, and crypto_sign_signature_extmu). + * This allows users to build mldsa-native without providing a + * randombytes() implementation if they only need the + * internal deterministic API + * (crypto_sign_keypair_internal, crypto_sign_signature_internal). + * + * @note This option is incompatible with MLD_CONFIG_KEYGEN_PCT + * as the current PCT implementation requires + * crypto_sign_signature(). + */ +#define MLD_CONFIG_NO_RANDOMIZED_API + +/** + * MLD_CONFIG_NO_SUPERCOP + * + * By default, mldsa_native.h exposes the mldsa-native API in the + * SUPERCOP naming convention (crypto_sign_xxx). If you don't need + * this, set MLD_CONFIG_NO_SUPERCOP. + * + * @note You must set this for a multi-level build as the SUPERCOP + * naming does not disambiguate between the parameter sets. + */ +/* #define MLD_CONFIG_NO_SUPERCOP */ + +/** + * 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, crypto_sign_keypair_internal and + * crypto_sign_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 crypto_sign_signature() and crypto_sign_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. + * + * - 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. + */ +/* 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 */