@@ -227,4 +227,184 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha
227
227
&& secp256k1_gej_eq_x_var (& rx , & rj );
228
228
}
229
229
230
+ /* Data that is used by the batch verification ecmult callback */
231
+ typedef struct {
232
+ const secp256k1_context * ctx ;
233
+ /* Seed for the random number generator */
234
+ unsigned char chacha_seed [32 ];
235
+ /* Caches randomizers generated by the PRNG which returns two randomizers per call. Caching
236
+ * avoids having to call the PRNG twice as often. The very first randomizer will be set to 1 and
237
+ * the PRNG is called at every odd indexed schnorrsig to fill the cache. */
238
+ secp256k1_scalar randomizer_cache [2 ];
239
+ /* Signature, message, public key tuples to verify */
240
+ const unsigned char * const * sig ;
241
+ const unsigned char * const * msg32 ;
242
+ const secp256k1_xonly_pubkey * const * pk ;
243
+ size_t n_sigs ;
244
+ } secp256k1_schnorrsig_verify_ecmult_context ;
245
+
246
+ /* Callback function which is called by ecmult_multi in order to convert the ecmult_context
247
+ * consisting of signature, message and public key tuples into scalars and points. */
248
+ static int secp256k1_schnorrsig_verify_batch_ecmult_callback (secp256k1_scalar * sc , secp256k1_ge * pt , size_t idx , void * data ) {
249
+ secp256k1_schnorrsig_verify_ecmult_context * ecmult_context = (secp256k1_schnorrsig_verify_ecmult_context * ) data ;
250
+
251
+ if (idx % 4 == 2 ) {
252
+ /* Every idx corresponds to a (scalar,point)-tuple. So this callback is called with 4
253
+ * consecutive tuples before we need to call the RNG for new randomizers:
254
+ * (-randomizer_cache[0], R1)
255
+ * (-randomizer_cache[0]*e1, P1)
256
+ * (-randomizer_cache[1], R2)
257
+ * (-randomizer_cache[1]*e2, P2) */
258
+ secp256k1_scalar_chacha20 (& ecmult_context -> randomizer_cache [0 ], & ecmult_context -> randomizer_cache [1 ], ecmult_context -> chacha_seed , idx / 4 );
259
+ }
260
+
261
+ /* R */
262
+ if (idx % 2 == 0 ) {
263
+ secp256k1_fe rx ;
264
+ * sc = ecmult_context -> randomizer_cache [(idx / 2 ) % 2 ];
265
+ if (!secp256k1_fe_set_b32 (& rx , & ecmult_context -> sig [idx / 2 ][0 ])) {
266
+ return 0 ;
267
+ }
268
+ if (!secp256k1_ge_set_xquad (pt , & rx )) {
269
+ return 0 ;
270
+ }
271
+ /* eP */
272
+ } else {
273
+ unsigned char buf [32 ];
274
+ secp256k1_sha256 sha ;
275
+
276
+ /* xonly_pubkey_load is guaranteed not to fail because
277
+ * verify_batch_init_randomizer calls secp256k1_ec_pubkey_serialize
278
+ * which only works if loading the pubkey into a group element
279
+ * succeeds.*/
280
+ VERIFY_CHECK (secp256k1_xonly_pubkey_load (ecmult_context -> ctx , pt , ecmult_context -> pk [idx / 2 ]));
281
+
282
+ secp256k1_schnorrsig_sha256_tagged (& sha );
283
+ secp256k1_sha256_write (& sha , & ecmult_context -> sig [idx / 2 ][0 ], 32 );
284
+ secp256k1_fe_get_b32 (buf , & pt -> x );
285
+ secp256k1_sha256_write (& sha , buf , sizeof (buf ));
286
+ secp256k1_sha256_write (& sha , ecmult_context -> msg32 [idx / 2 ], 32 );
287
+ secp256k1_sha256_finalize (& sha , buf );
288
+
289
+ secp256k1_scalar_set_b32 (sc , buf , NULL );
290
+ secp256k1_scalar_mul (sc , sc , & ecmult_context -> randomizer_cache [(idx / 2 ) % 2 ]);
291
+ }
292
+ return 1 ;
293
+ }
294
+
295
+ /** Helper function for batch verification. Hashes signature verification data into the
296
+ * randomization seed and initializes ecmult_context.
297
+ *
298
+ * Returns 1 if the randomizer was successfully initialized.
299
+ *
300
+ * Args: ctx: a secp256k1 context object
301
+ * Out: ecmult_context: context for batch_ecmult_callback
302
+ * In/Out sha: an initialized sha256 object which hashes the schnorrsig input in order to get a
303
+ * seed for the randomizer PRNG
304
+ * In: sig: array of signatures, or NULL if there are no signatures
305
+ * msg32: array of messages, or NULL if there are no signatures
306
+ * pk: array of public keys, or NULL if there are no signatures
307
+ * n_sigs: number of signatures in above arrays (must be 0 if they are NULL)
308
+ */
309
+ static int secp256k1_schnorrsig_verify_batch_init_randomizer (const secp256k1_context * ctx , secp256k1_schnorrsig_verify_ecmult_context * ecmult_context , secp256k1_sha256 * sha , const unsigned char * const * sig , const unsigned char * const * msg32 , const secp256k1_xonly_pubkey * const * pk , size_t n_sigs ) {
310
+ size_t i ;
311
+
312
+ if (n_sigs > 0 ) {
313
+ ARG_CHECK (sig != NULL );
314
+ ARG_CHECK (msg32 != NULL );
315
+ ARG_CHECK (pk != NULL );
316
+ }
317
+
318
+ for (i = 0 ; i < n_sigs ; i ++ ) {
319
+ unsigned char buf [33 ];
320
+ size_t buflen = sizeof (buf );
321
+ secp256k1_sha256_write (sha , sig [i ], 64 );
322
+ secp256k1_sha256_write (sha , msg32 [i ], 32 );
323
+ /* We use compressed serialization here. If we would use
324
+ * xonly_pubkey serialization and a user would wrongly memcpy
325
+ * normal secp256k1_pubkeys into xonly_pubkeys then the randomizer
326
+ * would be the same for two different pubkeys. */
327
+ if (!secp256k1_ec_pubkey_serialize (ctx , buf , & buflen , (const secp256k1_pubkey * ) pk [i ], SECP256K1_EC_COMPRESSED )) {
328
+ return 0 ;
329
+ }
330
+ secp256k1_sha256_write (sha , buf , buflen );
331
+ }
332
+ ecmult_context -> ctx = ctx ;
333
+ ecmult_context -> sig = sig ;
334
+ ecmult_context -> msg32 = msg32 ;
335
+ ecmult_context -> pk = pk ;
336
+ ecmult_context -> n_sigs = n_sigs ;
337
+
338
+ return 1 ;
339
+ }
340
+
341
+ /** Helper function for batch verification. Sums the s part of all signatures multiplied by their
342
+ * randomizer.
343
+ *
344
+ * Returns 1 if s is successfully summed.
345
+ *
346
+ * In/Out: s: the s part of the input sigs is added to this s argument
347
+ * In: chacha_seed: PRNG seed for computing randomizers
348
+ * sig: array of signatures, or NULL if there are no signatures
349
+ * n_sigs: number of signatures in above array (must be 0 if they are NULL)
350
+ */
351
+ static int secp256k1_schnorrsig_verify_batch_sum_s (secp256k1_scalar * s , unsigned char * chacha_seed , const unsigned char * const * sig , size_t n_sigs ) {
352
+ secp256k1_scalar randomizer_cache [2 ];
353
+ size_t i ;
354
+
355
+ secp256k1_scalar_set_int (& randomizer_cache [0 ], 1 );
356
+ for (i = 0 ; i < n_sigs ; i ++ ) {
357
+ int overflow ;
358
+ secp256k1_scalar term ;
359
+ if (i % 2 == 1 ) {
360
+ secp256k1_scalar_chacha20 (& randomizer_cache [0 ], & randomizer_cache [1 ], chacha_seed , i / 2 );
361
+ }
362
+
363
+ secp256k1_scalar_set_b32 (& term , & sig [i ][32 ], & overflow );
364
+ if (overflow ) {
365
+ return 0 ;
366
+ }
367
+ secp256k1_scalar_mul (& term , & term , & randomizer_cache [i % 2 ]);
368
+ secp256k1_scalar_add (s , s , & term );
369
+ }
370
+ return 1 ;
371
+ }
372
+
373
+ /* schnorrsig batch verification.
374
+ * Seeds a random number generator with the inputs and derives a random number ai for every
375
+ * signature i. Fails if y-coordinate of any R is not a quadratic residue or if
376
+ * 0 != -(s1 + a2*s2 + ... + au*su)G + R1 + a2*R2 + ... + au*Ru + e1*P1 + (a2*e2)P2 + ... + (au*eu)Pu. */
377
+ int secp256k1_schnorrsig_verify_batch (const secp256k1_context * ctx , secp256k1_scratch * scratch , const unsigned char * const * sig , const unsigned char * const * msg32 , const secp256k1_xonly_pubkey * const * pk , size_t n_sigs ) {
378
+ secp256k1_schnorrsig_verify_ecmult_context ecmult_context ;
379
+ secp256k1_sha256 sha ;
380
+ secp256k1_scalar s ;
381
+ secp256k1_gej rj ;
382
+
383
+ VERIFY_CHECK (ctx != NULL );
384
+ ARG_CHECK (secp256k1_ecmult_context_is_built (& ctx -> ecmult_ctx ));
385
+ ARG_CHECK (scratch != NULL );
386
+ /* Check that n_sigs is less than half of the maximum size_t value. This is necessary because
387
+ * the number of points given to ecmult_multi is 2*n_sigs. */
388
+ ARG_CHECK (n_sigs <= SIZE_MAX / 2 );
389
+ /* Check that n_sigs is less than 2^31 to ensure the same behavior of this function on 32-bit
390
+ * and 64-bit platforms. */
391
+ ARG_CHECK (n_sigs < ((uint32_t )1 << 31 ));
392
+
393
+ secp256k1_sha256_initialize (& sha );
394
+ if (!secp256k1_schnorrsig_verify_batch_init_randomizer (ctx , & ecmult_context , & sha , sig , msg32 , pk , n_sigs )) {
395
+ return 0 ;
396
+ }
397
+ secp256k1_sha256_finalize (& sha , ecmult_context .chacha_seed );
398
+ secp256k1_scalar_set_int (& ecmult_context .randomizer_cache [0 ], 1 );
399
+
400
+ secp256k1_scalar_clear (& s );
401
+ if (!secp256k1_schnorrsig_verify_batch_sum_s (& s , ecmult_context .chacha_seed , sig , n_sigs )) {
402
+ return 0 ;
403
+ }
404
+ secp256k1_scalar_negate (& s , & s );
405
+
406
+ return secp256k1_ecmult_multi_var (& ctx -> error_callback , & ctx -> ecmult_ctx , scratch , & rj , & s , secp256k1_schnorrsig_verify_batch_ecmult_callback , (void * ) & ecmult_context , 2 * n_sigs )
407
+ && secp256k1_gej_is_infinity (& rj );
408
+ }
409
+
230
410
#endif
0 commit comments