|
| 1 | +/* |
| 2 | + * Copyright (c) The mldsa-native project authors |
| 3 | + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT |
| 4 | + */ |
| 5 | + |
| 6 | +#include <stdint.h> |
| 7 | +#include <stdio.h> |
| 8 | +#include <stdlib.h> |
| 9 | +#include <string.h> |
| 10 | + |
| 11 | +#include <mldsa_native.h> |
| 12 | +#include "expected_test_vectors.h" |
| 13 | + |
| 14 | +/* Convenience abbreviations for the secret-key and signature sizes, derived |
| 15 | + * from the configured parameter set. */ |
| 16 | +#define MLDSA_SK_BYTES MLDSA_SECRETKEYBYTES(MLD_CONFIG_PARAMETER_SET) |
| 17 | +#define MLDSA_SIG_BYTES MLDSA_BYTES(MLD_CONFIG_PARAMETER_SET) |
| 18 | + |
| 19 | +/* For testing, we use fixed test-vector randomness (test_vector_rnd) so that we |
| 20 | + * produce the same keys and signatures as the basic example -- and, crucially, |
| 21 | + * the same signature regardless of how the signing work is split across |
| 22 | + * restarts. A real integration must use cryptographic randomness for key |
| 23 | + * generation. |
| 24 | + */ |
| 25 | + |
| 26 | +#define CHECK(x) \ |
| 27 | + do \ |
| 28 | + { \ |
| 29 | + int rc; \ |
| 30 | + rc = (x); \ |
| 31 | + if (!rc) \ |
| 32 | + { \ |
| 33 | + fprintf(stderr, "ERROR (%s,%d)\n", __FILE__, __LINE__); \ |
| 34 | + return 1; \ |
| 35 | + } \ |
| 36 | + } while (0) |
| 37 | + |
| 38 | +/* |
| 39 | + * Signing hooks. |
| 40 | + * |
| 41 | + * This example keeps all restart state in a single global, so the hooks take |
| 42 | + * no context argument (the build leaves MLD_CONFIG_CONTEXT_PARAMETER unset). |
| 43 | + * This is the simplest possible integration and fine for a single-threaded |
| 44 | + * caller that signs one message at a time. |
| 45 | + * |
| 46 | + * For anything more -- concurrent or interleaved signing operations, or state |
| 47 | + * owned by a caller object -- enable MLD_CONFIG_CONTEXT_PARAMETER instead. Each |
| 48 | + * public API function then takes a trailing context argument that is forwarded |
| 49 | + * to the hooks, so the state below moves out of the global and into that |
| 50 | + * context. See test/src/test_sign_hook.c for the context-based variant. |
| 51 | + */ |
| 52 | + |
| 53 | +/* The three hooks this integration must provide. mldsa-native calls them from |
| 54 | + * its signing loop; the matching declarations in the configuration file are |
| 55 | + * gated behind MLD_BUILD_INTERNAL, so we repeat them here to define the hooks |
| 56 | + * with only the public API in scope. */ |
| 57 | +uint16_t mld_sign_hook_resume(void); |
| 58 | +int mld_sign_hook_attempt(uint16_t attempt); |
| 59 | +void mld_sign_hook_finish(uint16_t attempt); |
| 60 | + |
| 61 | +/* How many rejection-sampling attempts each signing call runs before pausing; |
| 62 | + * set by the driver before each operation. 0 means never pause (one-shot). */ |
| 63 | +static unsigned attempts_per_call = 0; |
| 64 | + |
| 65 | +/* Attempt from which the next signing call resumes. Advanced by the attempt |
| 66 | + * hook when it pauses, reset to 0 by the finish hook on success. */ |
| 67 | +static uint16_t resume_attempt = 0; |
| 68 | + |
| 69 | +/* Resume from wherever the previous call paused (0 on a fresh operation). */ |
| 70 | +uint16_t mld_sign_hook_resume(void) { return resume_attempt; } |
| 71 | + |
| 72 | +/* Called before each attempt. Return non-zero to pause; signing then returns |
| 73 | + * MLD_ERR_SIGNING_PAUSED and the next call resumes from `attempt`. */ |
| 74 | +int mld_sign_hook_attempt(uint16_t attempt) |
| 75 | +{ |
| 76 | + if (attempts_per_call != 0 && attempt >= resume_attempt + attempts_per_call) |
| 77 | + { |
| 78 | + resume_attempt = attempt; |
| 79 | + return 1; /* pause before this attempt */ |
| 80 | + } |
| 81 | + return 0; /* proceed */ |
| 82 | +} |
| 83 | + |
| 84 | +/* Called once signing succeeds; reset the resume point for the next operation. |
| 85 | + */ |
| 86 | +void mld_sign_hook_finish(uint16_t attempt) |
| 87 | +{ |
| 88 | + (void)attempt; |
| 89 | + resume_attempt = 0; |
| 90 | +} |
| 91 | + |
| 92 | +#if !defined(MLD_CONFIG_NO_SIGN_API) |
| 93 | +/* |
| 94 | + * Drive one signing operation to completion, pausing every `apc` attempts |
| 95 | + * (0 = one-shot). Repeatedly calls the deterministic signing API with the same |
| 96 | + * (message, sk, rnd); each MLD_ERR_SIGNING_PAUSED just means "call again to |
| 97 | + * continue". Returns the number of restarts (extra calls beyond the first), or |
| 98 | + * -1 on a genuine error. |
| 99 | + */ |
| 100 | +static int sign_restartable(uint8_t sig[MLDSA_SIG_BYTES], size_t *siglen, |
| 101 | + const uint8_t *pre, size_t prelen, |
| 102 | + const uint8_t *rnd, const uint8_t *sk, unsigned apc) |
| 103 | +{ |
| 104 | + int restarts = 0; |
| 105 | + |
| 106 | + /* Start a fresh operation and select the per-call attempt budget. */ |
| 107 | + resume_attempt = 0; |
| 108 | + attempts_per_call = apc; |
| 109 | + |
| 110 | + for (;;) |
| 111 | + { |
| 112 | + int rc = mldsa_signature_internal( |
| 113 | + sig, siglen, (const uint8_t *)TEST_VECTOR_MSG, TEST_VECTOR_MSG_LEN, pre, |
| 114 | + prelen, rnd, sk, 0 /* externalmu */); |
| 115 | + if (rc == 0) |
| 116 | + { |
| 117 | + return restarts; /* done */ |
| 118 | + } |
| 119 | + if (rc == MLD_ERR_SIGNING_PAUSED) |
| 120 | + { |
| 121 | + restarts++; |
| 122 | + continue; /* resume from resume_attempt */ |
| 123 | + } |
| 124 | + fprintf(stderr, "ERROR: signing failed with rc=%d\n", rc); |
| 125 | + return -1; |
| 126 | + } |
| 127 | +} |
| 128 | +#endif /* !MLD_CONFIG_NO_SIGN_API */ |
| 129 | + |
| 130 | +#if !defined(MLD_CONFIG_NO_SIGN_API) |
| 131 | +static int example_sign(const uint8_t sk[MLDSA_SK_BYTES]) |
| 132 | +{ |
| 133 | + uint8_t sig[MLDSA_SIG_BYTES]; |
| 134 | + uint8_t ref_sig[MLDSA_SIG_BYTES]; |
| 135 | + size_t siglen, ref_siglen; |
| 136 | + uint8_t pre[TEST_VECTOR_CTX_LEN + 2]; /* prefix string (0, ctxlen, ctx) */ |
| 137 | + int restarts; |
| 138 | + |
| 139 | + /* Prepare pre = (0, ctxlen, ctx) for pure ML-DSA domain separation. */ |
| 140 | + pre[0] = 0; |
| 141 | + pre[1] = TEST_VECTOR_CTX_LEN; |
| 142 | + memcpy(pre + 2, TEST_VECTOR_CTX, TEST_VECTOR_CTX_LEN); |
| 143 | + |
| 144 | + /* Single-step: pause after every attempt, so the operation is spread over as |
| 145 | + * many calls as it takes attempts. */ |
| 146 | + printf("Signing message (single-step, pausing every attempt)... "); |
| 147 | + restarts = |
| 148 | + sign_restartable(sig, &siglen, pre, sizeof(pre), test_vector_rnd, sk, 1); |
| 149 | + CHECK(restarts >= 0); |
| 150 | + printf("DONE after %d restart(s)\n", restarts); |
| 151 | + |
| 152 | + /* One-shot: never pause, i.e. ordinary uninterrupted signing. */ |
| 153 | + printf("Signing message (one-shot, no pausing)... "); |
| 154 | + restarts = sign_restartable(ref_sig, &ref_siglen, pre, sizeof(pre), |
| 155 | + test_vector_rnd, sk, 0); |
| 156 | + CHECK(restarts == 0); |
| 157 | + printf("DONE\n"); |
| 158 | + |
| 159 | + /* The key property: splitting the work across restarts neither repeats nor |
| 160 | + * skips a rejection-sampling attempt, so both runs produce the very same |
| 161 | + * signature -- and it is the reference test vector. */ |
| 162 | + CHECK(siglen == ref_siglen && memcmp(sig, ref_sig, siglen) == 0); |
| 163 | + CHECK(siglen == sizeof(test_vector_sig)); |
| 164 | + CHECK(memcmp(sig, test_vector_sig, siglen) == 0); |
| 165 | + printf("Single-step and one-shot signatures match the reference vector.\n"); |
| 166 | + return 0; |
| 167 | +} |
| 168 | +#else /* !MLD_CONFIG_NO_SIGN_API */ |
| 169 | +static int example_sign(const uint8_t sk[MLDSA_SK_BYTES]) |
| 170 | +{ |
| 171 | + (void)sk; |
| 172 | + printf("Signing message... SKIPPED (sign API disabled)\n"); |
| 173 | + return 0; |
| 174 | +} |
| 175 | +#endif /* MLD_CONFIG_NO_SIGN_API */ |
| 176 | + |
| 177 | +int main(void) |
| 178 | +{ |
| 179 | + printf("ML-DSA-%d Restartable Signing Example\n", MLD_CONFIG_PARAMETER_SET); |
| 180 | + printf("====================================\n\n"); |
| 181 | + |
| 182 | + printf("Message: %s\n", TEST_VECTOR_MSG); |
| 183 | + printf("Context: %s\n\n", TEST_VECTOR_CTX); |
| 184 | + |
| 185 | + /* Sign with the reference secret key; the example is solely about splitting |
| 186 | + * the signing loop across restarts and reproducing the one-shot signature. */ |
| 187 | + if (example_sign(test_vector_sk) != 0) |
| 188 | + { |
| 189 | + return 1; |
| 190 | + } |
| 191 | + |
| 192 | + printf("\nAll tests passed! Restartable signing works.\n"); |
| 193 | + return 0; |
| 194 | +} |
0 commit comments