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/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/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/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.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_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/mldsa_native_config.h b/mldsa/mldsa_native_config.h index 3bf487db3..9280d07f9 100644 --- a/mldsa/mldsa_native_config.h +++ b/mldsa/mldsa_native_config.h @@ -696,24 +696,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/src/common.h b/mldsa/src/common.h index 95a88b382..37532f755 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) @@ -270,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 901fc6398..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 @@ -60,9 +65,85 @@ (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 #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/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/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/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..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) @@ -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 */ @@ -490,14 +488,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. - * 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 @@ -591,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) && \ @@ -605,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 } /** @@ -915,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); @@ -931,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; @@ -997,14 +999,27 @@ 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); + + /* 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) @@ -1665,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 */ 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/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/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/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", } 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/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/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/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 */ 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 */