Skip to content

Commit 40f8f7a

Browse files
committed
Set limit on n_sigs in batch verification and simplify randomizer_cache (h/t Tim Ruffing)
1 parent 9e3bb77 commit 40f8f7a

File tree

4 files changed

+24
-20
lines changed

4 files changed

+24
-20
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ bench_ecmult
44
bench_schnorrsig
55
bench_sign
66
bench_verify
7-
bench_schnorr_verify
87
bench_recover
98
bench_internal
109
tests

include/secp256k1_schnorrsig.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ typedef struct {
1919
unsigned char data[64];
2020
} secp256k1_schnorrsig;
2121

22-
/** Serialize a Schnorr signature
22+
/** Serialize a Schnorr signature.
2323
*
2424
* Returns: 1
2525
* Args: ctx: a secp256k1 context object
@@ -91,7 +91,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
9191
const secp256k1_pubkey *pubkey
9292
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
9393

94-
/** Verifies a set of Schnorr signatures
94+
/** Verifies a set of Schnorr signatures.
9595
*
9696
* Returns 1 if all succeeded, 0 otherwise. In particular, returns 1 if n_sigs is 0.
9797
*
@@ -100,7 +100,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
100100
* In: sig: array of signatures, or NULL if there are no signatures
101101
* msg32: array of messages, or NULL if there are no signatures
102102
* pk: array of public keys, or NULL if there are no signatures
103-
* n_sigs: number of signatures in above arrays (must be 0 if they are NULL)
103+
* n_sigs: number of signatures in above arrays. Must be smaller than
104+
* 2^31 and smaller than 2^(sizeof(size_t)*8-1) i.e. half the
105+
* maximum size_t value. Must be 0 if above arrays are NULL.
104106
*/
105107
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch(
106108
const secp256k1_context* ctx,

src/modules/schnorrsig/main_impl.h

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ typedef struct {
163163
/* Seed for the random number generator */
164164
unsigned char chacha_seed[32];
165165
/* Caches randomizers generated by the PRNG which returns two randomizers per call. Caching
166-
* avoids having to call the PRNG twice as often. */
166+
* avoids having to call the PRNG twice as often. The very first randomizer will be set to 1 and
167+
* the PRNG is called at every odd indexed schnorrsig to fill the cache. */
167168
secp256k1_scalar randomizer_cache[2];
168169
/* Signature, message, public key tuples to verify */
169170
const secp256k1_schnorrsig *const *sig;
@@ -172,29 +173,19 @@ typedef struct {
172173
size_t n_sigs;
173174
} secp256k1_schnorrsig_verify_ecmult_context;
174175

175-
/* Helper function for batch verification.
176-
* One call to the chacha20 returns two random scalars which are returned by this function as r[0]
177-
* and r[1] except for the first randomizer which is set to 1. */
178-
static void secp256k1_schnorrsig_batch_randomizer(secp256k1_scalar *r, const unsigned char *seed, uint64_t idx) {
179-
secp256k1_scalar_chacha20(&r[0], &r[1], seed, idx);
180-
if (idx == 0) {
181-
secp256k1_scalar_set_int(&r[0], 1);
182-
}
183-
}
184-
185176
/* Callback function which is called by ecmult_multi in order to convert the ecmult_context
186177
* consisting of signature, message and public key tuples into scalars and points. */
187178
static int secp256k1_schnorrsig_verify_batch_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
188179
secp256k1_schnorrsig_verify_ecmult_context *ecmult_context = (secp256k1_schnorrsig_verify_ecmult_context *) data;
189180

190-
if (idx % 4 == 0) {
191-
/* Every idx corresponds to a (scalar,point)-tuple. So this callback is called on 4
181+
if (idx % 4 == 2) {
182+
/* Every idx corresponds to a (scalar,point)-tuple. So this callback is called with 4
192183
* consecutive tuples before we need to call the RNG for new randomizers:
193184
* (-randomizer_cache[0], R1)
194185
* (-randomizer_cache[0]*e1, P1)
195186
* (-randomizer_cache[1], R2)
196187
* (-randomizer_cache[1]*e2, P2) */
197-
secp256k1_schnorrsig_batch_randomizer(ecmult_context->randomizer_cache, ecmult_context->chacha_seed, idx / 4);
188+
secp256k1_scalar_chacha20(&ecmult_context->randomizer_cache[0], &ecmult_context->randomizer_cache[1], ecmult_context->chacha_seed, idx / 4);
198189
}
199190

200191
/* R */
@@ -284,11 +275,12 @@ int secp256k1_schnorrsig_verify_batch_sum_s(secp256k1_scalar *s, unsigned char *
284275
secp256k1_scalar randomizer_cache[2];
285276
size_t i;
286277

278+
secp256k1_scalar_set_int(&randomizer_cache[0], 1);
287279
for (i = 0; i < n_sigs; i++) {
288280
int overflow;
289281
secp256k1_scalar term;
290-
if (i % 2 == 0) {
291-
secp256k1_schnorrsig_batch_randomizer(randomizer_cache, chacha_seed, i / 2);
282+
if (i % 2 == 1) {
283+
secp256k1_scalar_chacha20(&randomizer_cache[0], &randomizer_cache[1], chacha_seed, i / 2);
292284
}
293285

294286
secp256k1_scalar_set_b32(&term, &sig[i]->data[32], &overflow);
@@ -314,12 +306,19 @@ int secp256k1_schnorrsig_verify_batch(const secp256k1_context *ctx, secp256k1_sc
314306
VERIFY_CHECK(ctx != NULL);
315307
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
316308
ARG_CHECK(scratch != NULL);
309+
/* Check that n_sigs is less than half of the maximum size_t value. This is necessary because
310+
* the number of points given to ecmult_multi is 2*n_sigs. */
311+
ARG_CHECK(n_sigs < (size_t)1 << (sizeof(size_t)*8-1));
312+
/* Check that n_sigs is less 2^31 to ensure the same behavior of this function on 32-bit and
313+
* 64-bit platforms. */
314+
ARG_CHECK(n_sigs < (size_t)(1 << 31));
317315

318316
secp256k1_sha256_initialize(&sha);
319317
if (!secp256k1_schnorrsig_verify_batch_init_randomizer(ctx, &ecmult_context, &sha, sig, msg32, pk, n_sigs)) {
320318
return 0;
321319
}
322320
secp256k1_sha256_finalize(&sha, ecmult_context.chacha_seed);
321+
secp256k1_scalar_set_int(&ecmult_context.randomizer_cache[0], 1);
323322

324323
secp256k1_scalar_clear(&s);
325324
if (!secp256k1_schnorrsig_verify_batch_sum_s(&s, ecmult_context.chacha_seed, sig, n_sigs)) {

src/modules/schnorrsig/tests_impl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) {
114114
CHECK(ecount == 4);
115115
CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, NULL, 1) == 0);
116116
CHECK(ecount == 5);
117+
CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (size_t)1 << (sizeof(size_t)*8-1)) == 0);
118+
CHECK(ecount == 6);
119+
CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, 1 << 31) == 0);
120+
CHECK(ecount == 7);
117121

118122
secp256k1_context_destroy(none);
119123
secp256k1_context_destroy(sign);

0 commit comments

Comments
 (0)