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