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