diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h index 466f132fa9..23d5ec01ee 100644 --- a/include/secp256k1_schnorrsig.h +++ b/include/secp256k1_schnorrsig.h @@ -90,6 +90,27 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( const secp256k1_xonly_pubkey *pubkey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +/** Verifies a set of Schnorr signatures. + * + * Returns 1 if all succeeded, 0 otherwise. In particular, returns 1 if n_sigs is 0. + * + * Args: ctx: a secp256k1 context object, initialized for verification. + * scratch: scratch space used for the multiexponentiation + * In: sig: array of pointers to signatures, or NULL if there are no signatures + * msg32: array of pointers to messages, or NULL if there are no signatures + * pk: array of pointers to x-only public keys, or NULL if there are no signatures + * n_sigs: number of signatures in above arrays. Must be below the + * minimum of 2^31 and SIZE_MAX/2. Must be 0 if above arrays are NULL. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + const unsigned char *const *sig, + const unsigned char *const *msg32, + const secp256k1_xonly_pubkey *const *pk, + size_t n_sigs +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + #ifdef __cplusplus } #endif diff --git a/src/bench_schnorrsig.c b/src/bench_schnorrsig.c index 86ae7bbf19..2ccdeb6844 100644 --- a/src/bench_schnorrsig.c +++ b/src/bench_schnorrsig.c @@ -48,6 +48,26 @@ void bench_schnorrsig_verify(void* arg, int iters) { } } +void bench_schnorrsig_verify_n(void* arg, int iters) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + int i, j; + const secp256k1_xonly_pubkey **pk = (const secp256k1_xonly_pubkey **)malloc(data->n * sizeof(*pk)); + + CHECK(pk != NULL); + for (j = 0; j < iters/data->n; j++) { + for (i = 0; i < data->n; i++) { + secp256k1_xonly_pubkey *pk_nonconst = (secp256k1_xonly_pubkey *)malloc(sizeof(*pk_nonconst)); + CHECK(secp256k1_xonly_pubkey_parse(data->ctx, pk_nonconst, data->pk[i]) == 1); + pk[i] = pk_nonconst; + } + CHECK(secp256k1_schnorrsig_verify_batch(data->ctx, data->scratch, data->sigs, data->msgs, pk, data->n)); + for (i = 0; i < data->n; i++) { + free((void *)pk[i]); + } + } + free(pk); +} + int main(void) { int i; bench_schnorrsig_data data; @@ -87,6 +107,15 @@ int main(void) { run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, iters); run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, iters); + for (i = 1; i <= iters; i *= 2) { + char name[64]; + int divisible_iters; + sprintf(name, "schnorrsig_batch_verify_%d", (int) i); + + data.n = i; + divisible_iters = iters - (iters % data.n); + run_benchmark(name, bench_schnorrsig_verify_n, NULL, NULL, (void *) &data, 3, divisible_iters); + } for (i = 0; i < iters; i++) { free((void *)data.keypairs[i]); diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index 44574e8291..81258153e0 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -231,4 +231,184 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha && secp256k1_gej_eq_x_var(&rx, &rj); } +/* Data that is used by the batch verification ecmult callback */ +typedef struct { + const secp256k1_context *ctx; + /* Seed for the random number generator */ + unsigned char chacha_seed[32]; + /* Caches randomizers generated by the PRNG which returns two randomizers per call. Caching + * avoids having to call the PRNG twice as often. The very first randomizer will be set to 1 and + * the PRNG is called at every odd indexed schnorrsig to fill the cache. */ + secp256k1_scalar randomizer_cache[2]; + /* Signature, message, public key tuples to verify */ + const unsigned char *const *sig; + const unsigned char *const *msg32; + const secp256k1_xonly_pubkey *const *pk; + size_t n_sigs; +} secp256k1_schnorrsig_verify_ecmult_context; + +/* Callback function which is called by ecmult_multi in order to convert the ecmult_context + * consisting of signature, message and public key tuples into scalars and points. */ +static int secp256k1_schnorrsig_verify_batch_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_schnorrsig_verify_ecmult_context *ecmult_context = (secp256k1_schnorrsig_verify_ecmult_context *) data; + + if (idx % 4 == 2) { + /* Every idx corresponds to a (scalar,point)-tuple. So this callback is called with 4 + * consecutive tuples before we need to call the RNG for new randomizers: + * (-randomizer_cache[0], R1) + * (-randomizer_cache[0]*e1, P1) + * (-randomizer_cache[1], R2) + * (-randomizer_cache[1]*e2, P2) */ + secp256k1_scalar_chacha20(&ecmult_context->randomizer_cache[0], &ecmult_context->randomizer_cache[1], ecmult_context->chacha_seed, idx / 4); + } + + /* R */ + if (idx % 2 == 0) { + secp256k1_fe rx; + *sc = ecmult_context->randomizer_cache[(idx / 2) % 2]; + if (!secp256k1_fe_set_b32(&rx, &ecmult_context->sig[idx / 2][0])) { + return 0; + } + if (!secp256k1_ge_set_xquad(pt, &rx)) { + return 0; + } + /* eP */ + } else { + unsigned char buf[32]; + secp256k1_sha256 sha; + + /* xonly_pubkey_load is guaranteed not to fail because + * verify_batch_init_randomizer calls secp256k1_ec_pubkey_serialize + * which only works if loading the pubkey into a group element + * succeeds.*/ + VERIFY_CHECK(secp256k1_xonly_pubkey_load(ecmult_context->ctx, pt, ecmult_context->pk[idx / 2])); + + secp256k1_schnorrsig_sha256_tagged(&sha); + secp256k1_sha256_write(&sha, &ecmult_context->sig[idx / 2][0], 32); + secp256k1_fe_get_b32(buf, &pt->x); + secp256k1_sha256_write(&sha, buf, sizeof(buf)); + secp256k1_sha256_write(&sha, ecmult_context->msg32[idx / 2], 32); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(sc, buf, NULL); + secp256k1_scalar_mul(sc, sc, &ecmult_context->randomizer_cache[(idx / 2) % 2]); + } + return 1; +} + +/** Helper function for batch verification. Hashes signature verification data into the + * randomization seed and initializes ecmult_context. + * + * Returns 1 if the randomizer was successfully initialized. + * + * Args: ctx: a secp256k1 context object + * Out: ecmult_context: context for batch_ecmult_callback + * In/Out sha: an initialized sha256 object which hashes the schnorrsig input in order to get a + * seed for the randomizer PRNG + * In: sig: array of signatures, or NULL if there are no signatures + * msg32: array of messages, or NULL if there are no signatures + * pk: array of public keys, or NULL if there are no signatures + * n_sigs: number of signatures in above arrays (must be 0 if they are NULL) + */ +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) { + size_t i; + + if (n_sigs > 0) { + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pk != NULL); + } + + for (i = 0; i < n_sigs; i++) { + unsigned char buf[33]; + size_t buflen = sizeof(buf); + secp256k1_sha256_write(sha, sig[i], 64); + secp256k1_sha256_write(sha, msg32[i], 32); + /* We use compressed serialization here. If we would use + * xonly_pubkey serialization and a user would wrongly memcpy + * normal secp256k1_pubkeys into xonly_pubkeys then the randomizer + * would be the same for two different pubkeys. */ + if (!secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, (const secp256k1_pubkey *) pk[i], SECP256K1_EC_COMPRESSED)) { + return 0; + } + secp256k1_sha256_write(sha, buf, buflen); + } + ecmult_context->ctx = ctx; + ecmult_context->sig = sig; + ecmult_context->msg32 = msg32; + ecmult_context->pk = pk; + ecmult_context->n_sigs = n_sigs; + + return 1; +} + +/** Helper function for batch verification. Sums the s part of all signatures multiplied by their + * randomizer. + * + * Returns 1 if s is successfully summed. + * + * In/Out: s: the s part of the input sigs is added to this s argument + * In: chacha_seed: PRNG seed for computing randomizers + * sig: array of signatures, or NULL if there are no signatures + * n_sigs: number of signatures in above array (must be 0 if they are NULL) + */ +static int secp256k1_schnorrsig_verify_batch_sum_s(secp256k1_scalar *s, unsigned char *chacha_seed, const unsigned char *const *sig, size_t n_sigs) { + secp256k1_scalar randomizer_cache[2]; + size_t i; + + secp256k1_scalar_set_int(&randomizer_cache[0], 1); + for (i = 0; i < n_sigs; i++) { + int overflow; + secp256k1_scalar term; + if (i % 2 == 1) { + secp256k1_scalar_chacha20(&randomizer_cache[0], &randomizer_cache[1], chacha_seed, i / 2); + } + + secp256k1_scalar_set_b32(&term, &sig[i][32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_mul(&term, &term, &randomizer_cache[i % 2]); + secp256k1_scalar_add(s, s, &term); + } + return 1; +} + +/* schnorrsig batch verification. + * Seeds a random number generator with the inputs and derives a random number ai for every + * signature i. Fails if y-coordinate of any R is not a quadratic residue or if + * 0 != -(s1 + a2*s2 + ... + au*su)G + R1 + a2*R2 + ... + au*Ru + e1*P1 + (a2*e2)P2 + ... + (au*eu)Pu. */ +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) { + secp256k1_schnorrsig_verify_ecmult_context ecmult_context; + secp256k1_sha256 sha; + secp256k1_scalar s; + secp256k1_gej rj; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(scratch != NULL); + /* Check that n_sigs is less than half of the maximum size_t value. This is necessary because + * the number of points given to ecmult_multi is 2*n_sigs. */ + ARG_CHECK(n_sigs <= SIZE_MAX / 2); + /* Check that n_sigs is less than 2^31 to ensure the same behavior of this function on 32-bit + * and 64-bit platforms. */ + ARG_CHECK(n_sigs < ((uint32_t)1 << 31)); + + secp256k1_sha256_initialize(&sha); + if (!secp256k1_schnorrsig_verify_batch_init_randomizer(ctx, &ecmult_context, &sha, sig, msg32, pk, n_sigs)) { + return 0; + } + secp256k1_sha256_finalize(&sha, ecmult_context.chacha_seed); + secp256k1_scalar_set_int(&ecmult_context.randomizer_cache[0], 1); + + secp256k1_scalar_clear(&s); + if (!secp256k1_schnorrsig_verify_batch_sum_s(&s, ecmult_context.chacha_seed, sig, n_sigs)) { + return 0; + } + secp256k1_scalar_negate(&s, &s); + + 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) + && secp256k1_gej_is_infinity(&rj); +} + #endif diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 6c47a4e251..04e457de18 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -90,7 +90,7 @@ void run_nonce_function_bip340_tests(void) { CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL, 1) == 0); } -void test_schnorrsig_api(void) { +void test_schnorrsig_api(secp256k1_scratch_space *scratch) { unsigned char sk1[32]; unsigned char sk2[32]; unsigned char sk3[32]; @@ -99,6 +99,10 @@ void test_schnorrsig_api(void) { secp256k1_xonly_pubkey pk[3]; secp256k1_xonly_pubkey zero_pk; unsigned char sig[64]; + const unsigned char *sigptr = sig; + const unsigned char *msgptr = msg; + const secp256k1_xonly_pubkey *pkptr = &pk[0]; + const secp256k1_xonly_pubkey *zeroptr = &zero_pk; /** setup **/ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); @@ -159,6 +163,30 @@ void test_schnorrsig_api(void) { CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, &zero_pk) == 0); CHECK(ecount == 6); + ecount = 0; + CHECK(secp256k1_schnorrsig_verify_batch(none, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify_batch(sign, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, 1) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, NULL, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, NULL, NULL, 0) == 1); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, NULL, &pkptr, 1) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, NULL, 1) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (size_t)1 << (sizeof(size_t)*8-1)) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (uint32_t)1 << 31) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &zeroptr, 1) == 0); + CHECK(ecount == 9); + secp256k1_context_destroy(none); secp256k1_context_destroy(sign); secp256k1_context_destroy(vrfy); @@ -195,17 +223,26 @@ void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const un } /* Helper function for schnorrsig_bip_vectors - * Checks that both verify and verify_batch (TODO) return the same value as expected. */ -void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig, int expected) { + * Checks that both verify and verify_batch return the same value as expected. */ +void test_schnorrsig_bip_vectors_check_verify(secp256k1_scratch_space *scratch, const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig, int expected) { + const unsigned char *msg_arr[1]; + const unsigned char *sig_arr[1]; + const secp256k1_xonly_pubkey *pk_arr[1]; secp256k1_xonly_pubkey pk; CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk, pk_serialized)); + + sig_arr[0] = sig; + msg_arr[0] = msg32; + pk_arr[0] = &pk; + CHECK(expected == secp256k1_schnorrsig_verify(ctx, sig, msg32, &pk)); + CHECK(expected == secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); } /* Test vectors according to BIP-340 ("Schnorr Signatures for secp256k1"). See * https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv. */ -void test_schnorrsig_bip_vectors(void) { +void test_schnorrsig_bip_vectors(secp256k1_scratch_space *scratch) { { /* Test vector 0 */ const unsigned char sk[32] = { @@ -243,7 +280,7 @@ void test_schnorrsig_bip_vectors(void) { 0xE1, 0x8A, 0xCD, 0x90, 0xA9, 0x0C, 0x94, 0x7E }; test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 1); } { /* Test vector 1 */ @@ -282,7 +319,7 @@ void test_schnorrsig_bip_vectors(void) { 0x3A, 0xF8, 0x67, 0x74, 0x91, 0x7C, 0xF9, 0x28 }; test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 1); } { /* Test vector 2 */ @@ -321,7 +358,7 @@ void test_schnorrsig_bip_vectors(void) { 0x47, 0x24, 0x4C, 0xC2, 0x89, 0xC7, 0x4D, 0x1D }; test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 1); } { /* Test vector 3 */ @@ -360,7 +397,7 @@ void test_schnorrsig_bip_vectors(void) { 0xD6, 0x9D, 0x1A, 0xF7, 0xA5, 0xAE, 0x83, 0x69 }; test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 1); } { /* Test vector 4 */ @@ -386,7 +423,7 @@ void test_schnorrsig_bip_vectors(void) { 0x65, 0x7E, 0x0E, 0x0D, 0x1A, 0x6F, 0xFE, 0x28, 0x3A, 0x33, 0x43, 0x8D, 0xE4, 0x73, 0x84, 0x19 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 1); } { /* Test vector 5 */ @@ -424,7 +461,7 @@ void test_schnorrsig_bip_vectors(void) { 0xCD, 0xCF, 0x97, 0x06, 0x0B, 0x2C, 0x73, 0xCD, 0xE6, 0x0E, 0x87, 0xAB, 0xCA, 0x1A, 0xA5, 0xD9 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); } { /* Test vector 7 */ @@ -450,7 +487,7 @@ void test_schnorrsig_bip_vectors(void) { 0xCF, 0xFC, 0x7D, 0x57, 0x0B, 0x9A, 0x71, 0x92, 0xEB, 0xF1, 0x89, 0x8E, 0x13, 0x44, 0xE3, 0xBF }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); } { /* Test vector 8 */ @@ -476,7 +513,7 @@ void test_schnorrsig_bip_vectors(void) { 0x83, 0xD0, 0xDC, 0x47, 0xCD, 0x59, 0x4B, 0x21, 0xA8, 0x58, 0xF3, 0x1A, 0x19, 0xAA, 0xB7, 0x1D }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); } { /* Test vector 9 */ @@ -502,7 +539,7 @@ void test_schnorrsig_bip_vectors(void) { 0x49, 0xDE, 0x66, 0x9C, 0xD2, 0x47, 0x73, 0xBC, 0xED, 0x77, 0xDD, 0xA3, 0x6D, 0x07, 0x3E, 0xC8 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); } { /* Test vector 10 */ @@ -528,7 +565,7 @@ void test_schnorrsig_bip_vectors(void) { 0xBA, 0xB1, 0x48, 0xE9, 0xA7, 0xE3, 0x6E, 0x6B, 0x22, 0x8F, 0x95, 0xDF, 0xA0, 0x8B, 0x43, 0xEC }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); } { /* Test vector 11 */ @@ -554,7 +591,7 @@ void test_schnorrsig_bip_vectors(void) { 0x36, 0xDE, 0x00, 0x9E, 0xE1, 0xEF, 0x55, 0x1A, 0x17, 0x79, 0x6B, 0x72, 0xB6, 0x8B, 0x8A, 0x24 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); } { /* Test vector 12 */ @@ -580,7 +617,7 @@ void test_schnorrsig_bip_vectors(void) { 0x36, 0xDE, 0x00, 0x9E, 0xE1, 0xEF, 0x55, 0x1A, 0x17, 0x79, 0x6B, 0x72, 0xB6, 0x8B, 0x8A, 0x24 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); } { /* Test vector 13 */ @@ -606,7 +643,7 @@ void test_schnorrsig_bip_vectors(void) { 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 }; - test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk, msg, sig, 0); } { /* Test vector 14 */ @@ -683,48 +720,64 @@ void test_schnorrsig_sign(void) { } #define N_SIGS 200 -/* Creates N_SIGS valid signatures and verifies them with verify and - * verify_batch (TODO). Then flips some bits and checks that verification now - * fails. */ -void test_schnorrsig_sign_verify(void) { +/* Creates N_SIGS valid signatures and verifies them with verify and verify_batch. Then flips some + * bits and checks that verification now fails. */ +void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) { const unsigned char sk[32] = "shhhhhhhh! this key is a secret."; unsigned char msg[N_SIGS][32]; unsigned char sig[N_SIGS][64]; size_t i; + const unsigned char *sig_arr[N_SIGS]; + const unsigned char *msg_arr[N_SIGS]; + const secp256k1_xonly_pubkey *pk_arr[N_SIGS]; secp256k1_keypair keypair; secp256k1_xonly_pubkey pk; CHECK(secp256k1_keypair_create(ctx, &keypair, sk)); CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, NULL, NULL, NULL, 0)); + for (i = 0; i < N_SIGS; i++) { secp256k1_rand256(msg[i]); CHECK(secp256k1_schnorrsig_sign(ctx, sig[i], msg[i], &keypair, NULL, NULL)); CHECK(secp256k1_schnorrsig_verify(ctx, sig[i], msg[i], &pk)); + sig_arr[i] = sig[i]; + msg_arr[i] = msg[i]; + pk_arr[i] = &pk; } + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 2)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, N_SIGS)); + { /* Flip a few bits in the signature and in the message and check that - * verify and verify_batch (TODO) fail */ + * verify and verify_batch fail */ size_t sig_idx = secp256k1_rand_int(4); size_t byte_idx = secp256k1_rand_int(32); unsigned char xorbyte = secp256k1_rand_int(254)+1; sig[sig_idx][byte_idx] ^= xorbyte; CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); sig[sig_idx][byte_idx] ^= xorbyte; byte_idx = secp256k1_rand_int(32); sig[sig_idx][32+byte_idx] ^= xorbyte; CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); sig[sig_idx][32+byte_idx] ^= xorbyte; byte_idx = secp256k1_rand_int(32); msg[sig_idx][byte_idx] ^= xorbyte; CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); msg[sig_idx][byte_idx] ^= xorbyte; /* Check that above bitflips have been reversed correctly */ CHECK(secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); } } #undef N_SIGS @@ -766,14 +819,18 @@ void test_schnorrsig_taproot(void) { } void run_schnorrsig_tests(void) { + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); + run_nonce_function_bip340_tests(); - test_schnorrsig_api(); + test_schnorrsig_api(scratch); test_schnorrsig_sha256_tagged(); - test_schnorrsig_bip_vectors(); + test_schnorrsig_bip_vectors(scratch); test_schnorrsig_sign(); - test_schnorrsig_sign_verify(); + test_schnorrsig_sign_verify(scratch); test_schnorrsig_taproot(); + + secp256k1_scratch_space_destroy(ctx, scratch); } #endif