diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h index 2019260e5b..27d5d635f7 100644 --- a/include/secp256k1_schnorrsig.h +++ b/include/secp256k1_schnorrsig.h @@ -65,10 +65,12 @@ SECP256K1_API int secp256k1_schnorrsig_parse( * nonce (can be NULL) * In: msg32: the 32-byte message hash being signed (cannot be NULL) * seckey: pointer to a 32-byte secret key (cannot be NULL) - * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bipschnorr is used + * noncefp: pointer to a nonce generation function. If NULL, + * secp256k1_nonce_function_bipschnorr is used * ndata: pointer to arbitrary data used by the nonce generation function. If non-NULL must - * be a pointer to a s2c_context object when using the default nonce function - * secp256k1_nonce_function_bipschnorr (can be NULL) + * be a pointer to an s2c_context object when using the default nonce function + * secp256k1_nonce_function_bipschnorr. s2c_context must be initialized with + * secp256k1_s2c_commit_context_create. (can be NULL) */ SECP256K1_API int secp256k1_schnorrsig_sign( const secp256k1_context* ctx, @@ -117,4 +119,23 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch const secp256k1_pubkey *const *pk, size_t n_sigs ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** 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: sig: the signature containing the sign-to-contract commitment (cannot be NULL) + * data32: the 32-byte data that was committed to (cannot be NULL) + * original_nonce: pointer to the original_nonce created when signing (cannot be NULL) + * negated_nonce: integer indicating if signing algorithm negated the nonce + */ +SECP256K1_API int secp256k1_schnorrsig_verify_s2c_commit( + const secp256k1_context* ctx, + const secp256k1_schnorrsig *sig, + const unsigned char *data32, + const secp256k1_pubkey *original_nonce, + int negated_nonce +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + #endif diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index 6f2a73daa9..853292eb7f 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -29,6 +29,29 @@ int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsi return 1; } +int secp256k1_schnorrsig_verify_s2c_commit(const secp256k1_context* ctx, const secp256k1_schnorrsig *sig, const unsigned char *data32, const secp256k1_pubkey *original_nonce, int negated_nonce) { + secp256k1_fe rx; + secp256k1_ge R; + secp256k1_pubkey pubnonce; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(data32 != NULL); + ARG_CHECK(original_nonce != NULL); + + if (!secp256k1_fe_set_b32(&rx, &sig->data[0])) { + return 0; + } + if (!secp256k1_ge_set_xquad(&R, &rx)) { + return 0; + } + if(negated_nonce) { + secp256k1_ge_neg(&R, &R); + } + secp256k1_pubkey_save(&pubnonce, &R); + return secp256k1_ec_commit_verify(ctx, &pubnonce, original_nonce, data32, 32); +} + int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig *sig, int *nonce_is_negated, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, void *ndata) { secp256k1_scalar x; secp256k1_scalar e; diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 1534983b0a..759d3c162f 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -25,13 +25,17 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) { unsigned char sk2[32]; unsigned char sk3[32]; unsigned char msg[32]; + unsigned char data32[32]; unsigned char sig64[64]; secp256k1_pubkey pk[3]; secp256k1_schnorrsig sig; + secp256k1_s2c_commit_context s2c_ctx; + secp256k1_pubkey s2c_original_nonce; const secp256k1_schnorrsig *sigptr = &sig; const unsigned char *msgptr = msg; const secp256k1_pubkey *pkptr = &pk[0]; int nonce_is_negated; + unsigned char ones[32]; /** setup **/ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); @@ -39,6 +43,7 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) { secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); int ecount; + memset(ones, 0xff, 32); secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); @@ -88,6 +93,29 @@ void test_schnorrsig_api(secp256k1_scratch_space *scratch) { CHECK(secp256k1_schnorrsig_parse(none, &sig, NULL) == 0); CHECK(ecount == 4); + /* Create sign-to-contract commitment to data32 for testing verify_s2c_commit */ + secp256k1_rand256(data32); + CHECK(secp256k1_s2c_commit_context_create(ctx, &s2c_ctx, data32) == 1); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, msg, sk1, NULL, &s2c_ctx) == 1); + CHECK(secp256k1_s2c_commit_get_original_nonce(ctx, &s2c_original_nonce, &s2c_ctx) == 1); + ecount = 0; + CHECK(secp256k1_schnorrsig_verify_s2c_commit(none, &sig, data32, &s2c_original_nonce, nonce_is_negated) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, data32, &s2c_original_nonce, nonce_is_negated) == 1); + CHECK(ecount == 1); + { + /* Overflowing x-coordinate in signature */ + secp256k1_schnorrsig sig_tmp = sig; + memcpy(&sig_tmp.data[0], ones, 32); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig_tmp, data32, &s2c_original_nonce, nonce_is_negated) == 0); + } + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, NULL, data32, &s2c_original_nonce, nonce_is_negated) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, NULL, &s2c_original_nonce, nonce_is_negated) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(vrfy, &sig, data32, NULL, nonce_is_negated) == 0); + CHECK(ecount == 4); + ecount = 0; CHECK(secp256k1_schnorrsig_verify(none, &sig, msg, &pk[0]) == 0); CHECK(ecount == 1); @@ -713,7 +741,49 @@ void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) { } #undef N_SIGS +void test_schnorrsig_s2c_commit_verify(void) { + secp256k1_s2c_commit_context s2c_ctx; + unsigned char data32[32]; + secp256k1_schnorrsig sig; + int nonce_is_negated; + unsigned char msg[32]; + unsigned char sk[32]; + secp256k1_pubkey pk; + secp256k1_pubkey s2c_original_nonce; + + secp256k1_rand256(data32); + secp256k1_rand256(msg); + secp256k1_rand256(sk); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1); + + /* Create and verify correct commitment */ + CHECK(secp256k1_s2c_commit_context_create(ctx, &s2c_ctx, data32) == 1); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, &nonce_is_negated, msg, sk, NULL, &s2c_ctx) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig, msg, &pk)); + + CHECK(secp256k1_s2c_commit_get_original_nonce(ctx, &s2c_original_nonce, &s2c_ctx) == 1); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig, data32, &s2c_original_nonce, nonce_is_negated) == 1); + /* verify_s2c_commit fails if nonce_is_negated is wrong */ + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig, data32, &s2c_original_nonce, !nonce_is_negated) == 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_original_nonce, nonce_is_negated) == 0); + } + { + /* verify_s2c_commit fails if signature does not commit to data */ + secp256k1_schnorrsig sig_tmp; + sig_tmp = sig; + secp256k1_rand256(&sig_tmp.data[0]); + CHECK(secp256k1_schnorrsig_verify_s2c_commit(ctx, &sig_tmp, data32, &s2c_original_nonce, nonce_is_negated) == 0); + } +} + void run_schnorrsig_tests(void) { + int i; secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); test_schnorrsig_serialize(); @@ -721,7 +791,11 @@ void run_schnorrsig_tests(void) { test_schnorrsig_bip_vectors(scratch); test_schnorrsig_sign(); test_schnorrsig_sign_verify(scratch); - + for (i = 0; i < count; i++) { + /* Run multiple times to increase probability that the nonce is negated in + * a test. */ + test_schnorrsig_s2c_commit_verify(); + } secp256k1_scratch_space_destroy(scratch); }