From d69625bece023c8aeea76dfe6fb6a72ff1c8191b Mon Sep 17 00:00:00 2001 From: Marko Bencun Date: Sun, 11 Sep 2022 12:55:13 +0200 Subject: [PATCH] allow creating and verifying Schnorr sign-to-contract commitments Adapted from https://github.com/bitcoin-core/secp256k1/pull/589/. The data is hashed using a tagged hash with the "s2c/schnorr/data" tag, which is consistent with the data hashing done in the ECDSA version of sign-to-contract (in ElementsProject/secp256k1-zkp), where the "s2c/ecdsa/data" tag is used. Similarly, the tweak hash tag is "s2c/schnorr/point". Co-authored-by: Jonas Nick --- include/secp256k1_schnorrsig.h | 44 +++++-- src/modules/schnorrsig/main_impl.h | 85 +++++++++++- src/modules/schnorrsig/tests_impl.h | 193 ++++++++++++++++++++++------ 3 files changed, 268 insertions(+), 54 deletions(-) diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h index d7c356fe87..937fc7a44a 100644 --- a/include/secp256k1_schnorrsig.h +++ b/include/secp256k1_schnorrsig.h @@ -125,26 +125,36 @@ SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_fun * setting it to SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT. * * Members: - * magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization - * and has no other function than making sure the object is - * initialized. - * noncefp: pointer to a nonce generation function. If NULL, - * secp256k1_nonce_function_bip340 is used - * ndata: pointer to arbitrary data used by the nonce generation function - * (can be NULL). If it is non-NULL and - * secp256k1_nonce_function_bip340 is used, then ndata must be a - * pointer to 32-byte auxiliary randomness as per BIP-340. + * magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization + * and has no other function than making sure the object is + * initialized. + * noncefp: pointer to a nonce generation function. If NULL, + * secp256k1_nonce_function_bip340 is used + * ndata: pointer to arbitrary data used by the nonce generation function + * (can be NULL). If it is non-NULL and + * secp256k1_nonce_function_bip340 is used, then ndata must be a + * pointer to 32-byte auxiliary randomness as per BIP-340. + * s2c_opening: pointer to an secp256k1_schnorrsig_s2c_opening structure which can be + * NULL but is required to be not NULL if this signature creates + * a sign-to-contract commitment (i.e. the `s2c_data32` argument + * is not NULL). + * s2c_data32: pointer to a 32-byte data to create an optional + * sign-to-contract commitment to if not NULL (can be NULL). */ typedef struct { unsigned char magic[4]; secp256k1_nonce_function_hardened noncefp; void* ndata; + secp256k1_schnorrsig_s2c_opening* s2c_opening; + const unsigned char* s2c_data32; } secp256k1_schnorrsig_extraparams; #define SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC { 0xda, 0x6f, 0xb3, 0x8c } #define SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT {\ SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC,\ NULL,\ + NULL,\ + NULL,\ NULL\ } @@ -231,6 +241,22 @@ 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(5); +/** Verify a sign-to-contract commitment. + * + * Returns: 1: the signature contains a commitment to data32 + * 0: incorrect opening + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig64: the signature containing the sign-to-contract commitment (cannot be NULL) + * data32: the 32-byte data that was committed to (cannot be NULL) + * opening: pointer to the opening created during signing (cannot be NULL) + */ +SECP256K1_API int secp256k1_schnorrsig_verify_s2c_commit( + const secp256k1_context* ctx, + const unsigned char *sig64, + const unsigned char *data32, + const secp256k1_schnorrsig_s2c_opening *opening +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + #ifdef __cplusplus } #endif diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index 68359a5475..62652cbe8c 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -13,6 +13,9 @@ static uint64_t s2c_opening_magic = 0x5d0520b8b7f2b168ULL; +static const unsigned char s2c_data_tag[16] = "s2c/schnorr/data"; +static const unsigned char s2c_point_tag[17] = "s2c/schnorr/point"; + static void secp256k1_schnorrsig_s2c_opening_init(secp256k1_schnorrsig_s2c_opening *opening) { opening->magic = s2c_opening_magic; opening->nonce_is_negated = 0; @@ -183,16 +186,18 @@ static void secp256k1_schnorrsig_challenge(secp256k1_scalar* e, const unsigned c secp256k1_scalar_set_b32(e, buf, NULL); } -static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) { +static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata, secp256k1_schnorrsig_s2c_opening *s2c_opening, const unsigned char *s2c_data32) { secp256k1_scalar sk; secp256k1_scalar e; secp256k1_scalar k; secp256k1_gej rj; secp256k1_ge pk; secp256k1_ge r; + secp256k1_sha256 sha; unsigned char buf[32] = { 0 }; unsigned char pk_buf[32]; unsigned char seckey[32]; + unsigned char noncedata[32]; int ret = 1; VERIFY_CHECK(ctx != NULL); @@ -200,6 +205,12 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi ARG_CHECK(sig64 != NULL); ARG_CHECK(msg != NULL || msglen == 0); ARG_CHECK(keypair != NULL); + /* sign-to-contract commitments only work with the default nonce function, + * because we need to ensure that s2c_data is actually hashed into the nonce and + * not just ignored because otherwise this could result in nonce reuse. */ + ARG_CHECK(s2c_data32 == NULL || (noncefp == NULL || noncefp == secp256k1_nonce_function_bip340)); + /* s2c_opening and s2c_data32 should be either both non-NULL or both NULL. */ + ARG_CHECK((s2c_opening != NULL) == (s2c_data32 != NULL)); if (noncefp == NULL) { noncefp = secp256k1_nonce_function_bip340; @@ -215,6 +226,25 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi secp256k1_scalar_get_b32(seckey, &sk); secp256k1_fe_get_b32(pk_buf, &pk.x); + + if (s2c_data32 != NULL) { + /* Provide s2c_data32 and ndata (if not NULL) to the the nonce function + * as additional data to derive the nonce from. If both pointers are + * not NULL, they need to be hashed to get the nonce data 32 bytes. + * Even if only s2c_data32 is not NULL, it's hashed because it should + * be possible to derive nonces even if only a SHA256 commitment to the + * data is known. This is for example important in the anti nonce + * sidechannel protocol. + */ + secp256k1_sha256_initialize_tagged(&sha, s2c_data_tag, sizeof(s2c_data_tag)); + secp256k1_sha256_write(&sha, s2c_data32, 32); + if (ndata != NULL) { + secp256k1_sha256_write(&sha, ndata, 32); + } + secp256k1_sha256_finalize(&sha, noncedata); + ndata = &noncedata; + } + ret &= !!noncefp(buf, msg, msglen, seckey, pk_buf, bip340_algo, sizeof(bip340_algo), ndata); secp256k1_scalar_set_b32(&k, buf, NULL); ret &= !secp256k1_scalar_is_zero(&k); @@ -223,12 +253,27 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); secp256k1_ge_set_gej(&r, &rj); + if (s2c_opening != NULL) { + secp256k1_schnorrsig_s2c_opening_init(s2c_opening); + if (s2c_data32 != NULL) { + secp256k1_sha256_initialize_tagged(&sha, s2c_point_tag, sizeof(s2c_point_tag)); + /* Create sign-to-contract commitment */ + secp256k1_pubkey_save(&s2c_opening->original_pubnonce, &r); + secp256k1_ec_commit_seckey(&k, &r, &sha, s2c_data32, 32); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); + secp256k1_ge_set_gej(&r, &rj); + } + } + /* We declassify r to allow using it as a branch point. This is fine * because r is not a secret. */ secp256k1_declassify(ctx, &r, sizeof(r)); secp256k1_fe_normalize_var(&r.y); if (secp256k1_fe_is_odd(&r.y)) { secp256k1_scalar_negate(&k, &k); + if (s2c_opening != NULL) { + s2c_opening->nonce_is_negated = 1; + } } secp256k1_fe_normalize_var(&r.x); secp256k1_fe_get_b32(&sig64[0], &r.x); @@ -248,7 +293,7 @@ static int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsi int secp256k1_schnorrsig_sign32(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) { /* We cast away const from the passed aux_rand32 argument since we know the default nonce function does not modify it. */ - return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg32, 32, keypair, secp256k1_nonce_function_bip340, (unsigned char*)aux_rand32); + return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg32, 32, keypair, secp256k1_nonce_function_bip340, (unsigned char*)aux_rand32, NULL, NULL); } int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, const unsigned char *aux_rand32) { @@ -258,6 +303,8 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64 int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_schnorrsig_extraparams *extraparams) { secp256k1_nonce_function_hardened noncefp = NULL; void *ndata = NULL; + secp256k1_schnorrsig_s2c_opening *s2c_opening = NULL; + const unsigned char *s2c_data32 = NULL; VERIFY_CHECK(ctx != NULL); if (extraparams != NULL) { @@ -266,8 +313,10 @@ int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char sizeof(extraparams->magic)) == 0); noncefp = extraparams->noncefp; ndata = extraparams->ndata; + s2c_opening = extraparams->s2c_opening; + s2c_data32 = extraparams->s2c_data32; } - return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msglen, keypair, noncefp, ndata); + return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msglen, keypair, noncefp, ndata, s2c_opening, s2c_data32); } int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey) { @@ -318,4 +367,34 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha secp256k1_fe_equal_var(&rx, &r.x); } +int secp256k1_schnorrsig_verify_s2c_commit(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *data32, const secp256k1_schnorrsig_s2c_opening *opening) { + secp256k1_fe rx; + secp256k1_ge original_r; + secp256k1_ge r; + secp256k1_sha256 sha; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(data32 != NULL); + ARG_CHECK(opening != NULL); + ARG_CHECK(secp256k1_schnorrsig_s2c_commit_is_init(opening)); + + if (!secp256k1_fe_set_b32(&rx, &sig64[0])) { + return 0; + } + if (!secp256k1_ge_set_xo_var(&r, &rx, 0)) { + return 0; + } + if (opening->nonce_is_negated) { + secp256k1_ge_neg(&r, &r); + } + + if (!secp256k1_pubkey_load(ctx, &original_r, &opening->original_pubnonce)) { + return 0; + } + + secp256k1_sha256_initialize_tagged(&sha, s2c_point_tag, sizeof(s2c_point_tag)); + return secp256k1_ec_commit_verify(&r, &original_r, &sha, data32, 32); +} + #endif diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index adad62c941..248cd1e05a 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -114,18 +114,62 @@ void run_nonce_function_bip340_tests(void) { CHECK(secp256k1_memcmp_var(nonce_z, nonce, 32) == 0); } +/* Nonce function that returns constant 0 */ +static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { + (void) msg; + (void) msglen; + (void) key32; + (void) xonly_pk32; + (void) algo; + (void) algolen; + (void) data; + (void) nonce32; + return 0; +} + +/* Nonce function that sets nonce to 0 */ +static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { + (void) msg; + (void) msglen; + (void) key32; + (void) xonly_pk32; + (void) algo; + (void) algolen; + (void) data; + + memset(nonce32, 0, 32); + return 1; +} + +/* Nonce function that sets nonce to 0xFF...0xFF */ +static int nonce_function_overflowing(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { + (void) msg; + (void) msglen; + (void) key32; + (void) xonly_pk32; + (void) algo; + (void) algolen; + (void) data; + + memset(nonce32, 0xFF, 32); + return 1; +} + void test_schnorrsig_api(void) { unsigned char sk1[32]; unsigned char sk2[32]; unsigned char sk3[32]; unsigned char msg[32]; + unsigned char s2c_data32[32]; + secp256k1_schnorrsig_s2c_opening s2c_opening; secp256k1_keypair keypairs[3]; secp256k1_keypair invalid_keypair = {{ 0 }}; secp256k1_xonly_pubkey pk[3]; secp256k1_xonly_pubkey zero_pk; unsigned char sig[64]; secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; - secp256k1_schnorrsig_extraparams invalid_extraparams = {{ 0 }, NULL, NULL}; + secp256k1_schnorrsig_extraparams extraparams_s2c; + secp256k1_schnorrsig_extraparams invalid_extraparams = {{ 0 }, NULL, NULL, NULL, NULL}; /** setup **/ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); @@ -150,6 +194,7 @@ void test_schnorrsig_api(void) { secp256k1_testrand256(sk2); secp256k1_testrand256(sk3); secp256k1_testrand256(msg); + secp256k1_testrand256(s2c_data32); CHECK(secp256k1_keypair_create(ctx, &keypairs[0], sk1) == 1); CHECK(secp256k1_keypair_create(ctx, &keypairs[1], sk2) == 1); CHECK(secp256k1_keypair_create(ctx, &keypairs[2], sk3) == 1); @@ -200,6 +245,22 @@ void test_schnorrsig_api(void) { CHECK(ecount == 5); CHECK(secp256k1_schnorrsig_sign_custom(sttc, sig, msg, sizeof(msg), &keypairs[0], &extraparams) == 0); CHECK(ecount == 6); + extraparams_s2c = extraparams; + extraparams_s2c.s2c_opening = &s2c_opening; + CHECK(secp256k1_schnorrsig_sign_custom(sign, sig, msg, sizeof(msg), &keypairs[0], &extraparams_s2c) == 0); + CHECK(ecount == 7); + extraparams_s2c = extraparams; + extraparams_s2c.s2c_opening = &s2c_opening; + extraparams_s2c.s2c_data32 = s2c_data32; + CHECK(secp256k1_schnorrsig_sign_custom(sign, sig, msg, sizeof(msg), &keypairs[0], &extraparams_s2c) == 1); + CHECK(ecount == 7); + /* s2c commitments with a different nonce function than bipschnorr are not allowed */ + extraparams_s2c = extraparams; + extraparams_s2c.s2c_opening = &s2c_opening; + extraparams_s2c.s2c_data32 = s2c_data32; + extraparams_s2c.noncefp = nonce_function_0; + CHECK(secp256k1_schnorrsig_sign_custom(sign, sig, msg, sizeof(msg), &keypairs[0], &extraparams_s2c) == 0); + CHECK(ecount == 8); ecount = 0; CHECK(secp256k1_schnorrsig_sign32(sign, sig, msg, &keypairs[0], NULL) == 1); @@ -220,6 +281,22 @@ void test_schnorrsig_api(void) { CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, sizeof(msg), &zero_pk) == 0); CHECK(ecount == 4); + /* Create sign-to-contract commitment to data32 for testing verify_s2c_commit */ + ecount = 0; + extraparams_s2c = extraparams; + extraparams_s2c.s2c_opening = &s2c_opening; + extraparams_s2c.s2c_data32 = s2c_data32; + CHECK(secp256k1_schnorrsig_sign_custom(sign, sig, msg, sizeof(msg), &keypairs[0], &extraparams_s2c) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, sig, s2c_data32, &s2c_opening) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, NULL, s2c_data32, &s2c_opening) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, sig, NULL, &s2c_opening) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, sig, s2c_data32, NULL) == 0); + CHECK(ecount == 3); + secp256k1_context_destroy(none); secp256k1_context_destroy(sign); secp256k1_context_destroy(vrfy); @@ -684,47 +761,6 @@ void test_schnorrsig_bip_vectors(void) { } } -/* Nonce function that returns constant 0 */ -static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { - (void) msg; - (void) msglen; - (void) key32; - (void) xonly_pk32; - (void) algo; - (void) algolen; - (void) data; - (void) nonce32; - return 0; -} - -/* Nonce function that sets nonce to 0 */ -static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { - (void) msg; - (void) msglen; - (void) key32; - (void) xonly_pk32; - (void) algo; - (void) algolen; - (void) data; - - memset(nonce32, 0, 32); - return 1; -} - -/* Nonce function that sets nonce to 0xFF...0xFF */ -static int nonce_function_overflowing(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) { - (void) msg; - (void) msglen; - (void) key32; - (void) xonly_pk32; - (void) algo; - (void) algolen; - (void) data; - - memset(nonce32, 0xFF, 32); - return 1; -} - void test_schnorrsig_sign(void) { unsigned char sk[32]; secp256k1_xonly_pubkey pk; @@ -888,6 +924,76 @@ void test_schnorrsig_taproot(void) { CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1); } +void test_schnorrsig_s2c_commit_verify(void) { + unsigned char data32[32]; + unsigned char sig[64]; + secp256k1_schnorrsig_s2c_opening s2c_opening; + unsigned char msg[32]; + unsigned char sk[32]; + secp256k1_xonly_pubkey pk; + secp256k1_keypair keypair; + unsigned char noncedata[32]; + secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; + extraparams.ndata = noncedata; + extraparams.s2c_opening = &s2c_opening; + extraparams.s2c_data32 = data32; + + secp256k1_testrand256(data32); + secp256k1_testrand256(msg); + secp256k1_testrand256(sk); + CHECK(secp256k1_keypair_create(ctx, &keypair, sk)); + CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); + secp256k1_testrand256(noncedata); + + /* Create and verify correct commitment */ + CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &pk)); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, sig, data32, &s2c_opening) == 1); + { + /* verify_s2c_commit fails if nonce_is_negated is wrong */ + secp256k1_schnorrsig_s2c_opening s2c_opening_tmp; + s2c_opening_tmp = s2c_opening; + s2c_opening_tmp.nonce_is_negated = !s2c_opening.nonce_is_negated; + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, sig, data32, &s2c_opening_tmp) == 0); + } + { + /* verify_s2c_commit fails if given data does not match committed data */ + unsigned char data32_tmp[32]; + memcpy(data32_tmp, data32, sizeof(data32_tmp)); + data32_tmp[31] ^= 1; + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, sig, data32_tmp, &s2c_opening) == 0); + } + { + /* verify_s2c_commit fails if signature does not commit to data */ + unsigned char sig_tmp[64]; + memcpy(sig_tmp, sig, 64); + secp256k1_testrand256(sig_tmp); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, sig_tmp, data32, &s2c_opening) == 0); + } + { + /* A commitment to different data creates a different original_pubnonce + * (i.e. data is hashed into the nonce) */ + secp256k1_schnorrsig_s2c_opening s2c_opening_tmp; + unsigned char sig_tmp[64]; + unsigned char data32_tmp[32]; + unsigned char serialized_nonce[33]; + unsigned char serialized_nonce_tmp[33]; + size_t outputlen = 33; + secp256k1_schnorrsig_extraparams extraparams_tmp = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; + extraparams_tmp.s2c_opening = &s2c_opening_tmp; + extraparams_tmp.s2c_data32 = data32_tmp; + secp256k1_testrand256(data32_tmp); + CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig_tmp, msg, sizeof(msg), &keypair, &extraparams_tmp) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, sig_tmp, msg, sizeof(msg), &pk)); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, sig_tmp, data32_tmp, &s2c_opening_tmp) == 1); + secp256k1_ec_pubkey_serialize(ctx, serialized_nonce, &outputlen, &s2c_opening.original_pubnonce, SECP256K1_EC_COMPRESSED); + CHECK(outputlen == 33); + secp256k1_ec_pubkey_serialize(ctx, serialized_nonce_tmp, &outputlen, &s2c_opening_tmp.original_pubnonce, SECP256K1_EC_COMPRESSED); + CHECK(outputlen == 33); + CHECK(memcmp(serialized_nonce, serialized_nonce_tmp, outputlen) != 0); + } +} + void test_s2c_opening(void) { int i = 0; unsigned char output[33]; @@ -959,6 +1065,9 @@ void run_schnorrsig_tests(void) { for (i = 0; i < count; i++) { test_schnorrsig_sign(); test_schnorrsig_sign_verify(); + /* Run multiple times to increase probability that the nonce is negated in + * a test. */ + test_schnorrsig_s2c_commit_verify(); } test_schnorrsig_taproot(); test_s2c_opening();