From 3e08b02e2a78f2a1fc457efab665db8ab8085373 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 30 Mar 2020 14:51:38 +0000 Subject: [PATCH 01/19] Make the secp256k1_declassify argument constant This is required to declassify pointers to constant memory. Declassify should never modify its argument. --- src/secp256k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/secp256k1.c b/src/secp256k1.c index b43a9592..4a55ad6b 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -227,7 +227,7 @@ void secp256k1_scratch_space_destroy(const secp256k1_context *ctx, secp256k1_scr * of the software. This is setup for use with valgrind but could be substituted with * the appropriate instrumentation for other analysis tools. */ -static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, void *p, size_t len) { +static SECP256K1_INLINE void secp256k1_declassify(const secp256k1_context* ctx, const void *p, size_t len) { #if defined(VALGRIND) if (EXPECT(ctx->declassify,0)) VALGRIND_MAKE_MEM_DEFINED(p, len); #else From 47e6618e11813cfabe91f0909ca031f960cb7dd4 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 12 May 2020 13:58:47 +0000 Subject: [PATCH 02/19] extrakeys: Init empty experimental module This is to prepare for xonly_pubkeys and keypairs. --- Makefile.am | 4 ++++ configure.ac | 15 +++++++++++++++ include/secp256k1_extrakeys.h | 14 ++++++++++++++ src/modules/extrakeys/Makefile.am.include | 3 +++ src/modules/extrakeys/main_impl.h | 13 +++++++++++++ src/modules/extrakeys/tests_impl.h | 16 ++++++++++++++++ src/secp256k1.c | 4 ++++ src/tests.c | 8 ++++++++ 8 files changed, 77 insertions(+) create mode 100644 include/secp256k1_extrakeys.h create mode 100644 src/modules/extrakeys/Makefile.am.include create mode 100644 src/modules/extrakeys/main_impl.h create mode 100644 src/modules/extrakeys/tests_impl.h diff --git a/Makefile.am b/Makefile.am index 1c19e14d..9adfbaed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -153,3 +153,7 @@ endif if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include endif + +if ENABLE_MODULE_EXTRAKEYS +include src/modules/extrakeys/Makefile.am.include +endif diff --git a/configure.ac b/configure.ac index 743f7f7b..4d51eb5a 100644 --- a/configure.ac +++ b/configure.ac @@ -136,6 +136,11 @@ AC_ARG_ENABLE(module_recovery, [enable_module_recovery=$enableval], [enable_module_recovery=no]) +AC_ARG_ENABLE(module_extrakeys, + AS_HELP_STRING([--enable-module-extrakeys],[enable extrakeys module (experimental)]), + [enable_module_extrakeys=$enableval], + [enable_module_extrakeys=no]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [use_external_default_callbacks=$enableval], @@ -421,6 +426,10 @@ if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) fi +if test x"$enable_module_extrakeys" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module]) +fi + if test x"$use_external_asm" = x"yes"; then AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) fi @@ -434,11 +443,15 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([WARNING: experimental build]) AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) + AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys]) AC_MSG_NOTICE([******]) else if test x"$enable_module_ecdh" = x"yes"; then AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_extrakeys" = x"yes"; then + AC_MSG_ERROR([extrakeys module is experimental. Use --enable-experimental to allow.]) + fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi @@ -456,6 +469,7 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) @@ -476,6 +490,7 @@ echo " with benchmarks = $use_benchmark" echo " with coverage = $enable_coverage" echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" +echo " module extrakeys = $enable_module_extrakeys" echo echo " asm = $set_asm" echo " bignum = $set_bignum" diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h new file mode 100644 index 00000000..e453c9dd --- /dev/null +++ b/include/secp256k1_extrakeys.h @@ -0,0 +1,14 @@ +#ifndef SECP256K1_EXTRAKEYS_H +#define SECP256K1_EXTRAKEYS_H + +#include "secp256k1.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_EXTRAKEYS_H */ diff --git a/src/modules/extrakeys/Makefile.am.include b/src/modules/extrakeys/Makefile.am.include new file mode 100644 index 00000000..8515f92e --- /dev/null +++ b/src/modules/extrakeys/Makefile.am.include @@ -0,0 +1,3 @@ +include_HEADERS += include/secp256k1_extrakeys.h +noinst_HEADERS += src/modules/extrakeys/tests_impl.h +noinst_HEADERS += src/modules/extrakeys/main_impl.h diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h new file mode 100644 index 00000000..0b2b27be --- /dev/null +++ b/src/modules/extrakeys/main_impl.h @@ -0,0 +1,13 @@ +/********************************************************************** + * Copyright (c) 2020 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_EXTRAKEYS_MAIN_ +#define _SECP256K1_MODULE_EXTRAKEYS_MAIN_ + +#include "include/secp256k1.h" +#include "include/secp256k1_extrakeys.h" + +#endif diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h new file mode 100644 index 00000000..9d1d80e1 --- /dev/null +++ b/src/modules/extrakeys/tests_impl.h @@ -0,0 +1,16 @@ +/********************************************************************** + * Copyright (c) 2020 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_EXTRAKEYS_TESTS_ +#define _SECP256K1_MODULE_EXTRAKEYS_TESTS_ + +#include "secp256k1_extrakeys.h" + +void run_extrakeys_tests(void) { + /* TODO */ +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 4a55ad6b..4f5b547d 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -742,3 +742,7 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/main_impl.h" #endif + +#ifdef ENABLE_MODULE_EXTRAKEYS +# include "modules/extrakeys/main_impl.h" +#endif diff --git a/src/tests.c b/src/tests.c index 58d2ad2f..9ad9e32a 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5306,6 +5306,10 @@ void run_ecdsa_openssl(void) { # include "modules/recovery/tests_impl.h" #endif +#ifdef ENABLE_MODULE_EXTRAKEYS +# include "modules/extrakeys/tests_impl.h" +#endif + void run_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -5612,6 +5616,10 @@ int main(int argc, char **argv) { run_recovery_tests(); #endif +#ifdef ENABLE_MODULE_EXTRAKEYS + run_extrakeys_tests(); +#endif + /* util tests */ run_memczero_test(); From 4cd2ee474d178bd1b5602486104db346a7562c67 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 12 May 2020 14:40:28 +0000 Subject: [PATCH 03/19] extrakeys: Add xonly_pubkey with serialize, parse and from_pubkey --- include/secp256k1_extrakeys.h | 69 +++++++++++++++ src/modules/extrakeys/main_impl.h | 75 +++++++++++++++++ src/modules/extrakeys/tests_impl.h | 131 ++++++++++++++++++++++++++++- 3 files changed, 274 insertions(+), 1 deletion(-) diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index e453c9dd..fdbfdeec 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -7,6 +7,75 @@ extern "C" { #endif +/** Opaque data structure that holds a parsed and valid "x-only" public key. + * An x-only pubkey encodes a point whose Y coordinate is even. It is + * serialized using only its X coordinate (32 bytes). See BIP-340 for more + * information about x-only pubkeys. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_xonly_pubkey_serialize and + * secp256k1_xonly_pubkey_parse. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_xonly_pubkey; + +/** Parse a 32-byte sequence into a xonly_pubkey object. + * + * Returns: 1 if the public key was fully valid. + * 0 if the public key could not be parsed or is invalid. + * + * Args: ctx: a secp256k1 context object (cannot be NULL). + * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a + * parsed version of input. If not, it's set to an invalid value. + * (cannot be NULL). + * In: input32: pointer to a serialized xonly_pubkey (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_parse( + const secp256k1_context* ctx, + secp256k1_xonly_pubkey* pubkey, + const unsigned char *input32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize an xonly_pubkey object into a 32-byte sequence. + * + * Returns: 1 always. + * + * Args: ctx: a secp256k1 context object (cannot be NULL). + * Out: output32: a pointer to a 32-byte array to place the serialized key in + * (cannot be NULL). + * In: pubkey: a pointer to a secp256k1_xonly_pubkey containing an + * initialized public key (cannot be NULL). + */ +SECP256K1_API int secp256k1_xonly_pubkey_serialize( + const secp256k1_context* ctx, + unsigned char *output32, + const secp256k1_xonly_pubkey* pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey. + * + * Returns: 1 if the public key was successfully converted + * 0 otherwise + * + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: xonly_pubkey: pointer to an x-only public key object for placing the + * converted public key (cannot be NULL) + * pk_parity: pointer to an integer that will be set to 1 if the point + * encoded by xonly_pubkey is the negation of the pubkey and + * set to 0 otherwise. (can be NULL) + * In: pubkey: pointer to a public key that is converted (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubkey( + const secp256k1_context* ctx, + secp256k1_xonly_pubkey *xonly_pubkey, + int *pk_parity, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + #ifdef __cplusplus } #endif diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index 0b2b27be..4f6fd362 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -10,4 +10,79 @@ #include "include/secp256k1.h" #include "include/secp256k1_extrakeys.h" +static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) { + return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey *) pubkey); +} + +static SECP256K1_INLINE void secp256k1_xonly_pubkey_save(secp256k1_xonly_pubkey *pubkey, secp256k1_ge *ge) { + secp256k1_pubkey_save((secp256k1_pubkey *) pubkey, ge); +} + +int secp256k1_xonly_pubkey_parse(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, const unsigned char *input32) { + secp256k1_ge pk; + secp256k1_fe x; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(input32 != NULL); + + if (!secp256k1_fe_set_b32(&x, input32)) { + return 0; + } + if (!secp256k1_ge_set_xo_var(&pk, &x, 0)) { + return 0; + } + secp256k1_xonly_pubkey_save(pubkey, &pk); + return 1; +} + +int secp256k1_xonly_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output32, const secp256k1_xonly_pubkey *pubkey) { + secp256k1_ge pk; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output32 != NULL); + memset(output32, 0, 32); + ARG_CHECK(pubkey != NULL); + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) { + return 0; + } + secp256k1_fe_get_b32(output32, &pk.x); + return 1; +} + +/** Keeps a group element as is if it has an even Y and otherwise negates it. + * y_parity is set to 0 in the former case and to 1 in the latter case. + * Requires that the coordinates of r are normalized. */ +static int secp256k1_extrakeys_ge_even_y(secp256k1_ge *r) { + int y_parity = 0; + VERIFY_CHECK(!secp256k1_ge_is_infinity(r)); + + if (secp256k1_fe_is_odd(&r->y)) { + secp256k1_fe_negate(&r->y, &r->y, 1); + y_parity = 1; + } + return y_parity; +} + +int secp256k1_xonly_pubkey_from_pubkey(const secp256k1_context* ctx, secp256k1_xonly_pubkey *xonly_pubkey, int *pk_parity, const secp256k1_pubkey *pubkey) { + secp256k1_ge pk; + int tmp; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(xonly_pubkey != NULL); + ARG_CHECK(pubkey != NULL); + + if (!secp256k1_pubkey_load(ctx, &pk, pubkey)) { + return 0; + } + tmp = secp256k1_extrakeys_ge_even_y(&pk); + if (pk_parity != NULL) { + *pk_parity = tmp; + } + secp256k1_xonly_pubkey_save(xonly_pubkey, &pk); + return 1; +} + #endif diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index 9d1d80e1..ebe51325 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -9,8 +9,137 @@ #include "secp256k1_extrakeys.h" +static secp256k1_context* api_test_context(int flags, int *ecount) { + secp256k1_context *ctx0 = secp256k1_context_create(flags); + secp256k1_context_set_error_callback(ctx0, counting_illegal_callback_fn, ecount); + secp256k1_context_set_illegal_callback(ctx0, counting_illegal_callback_fn, ecount); + return ctx0; +} + +void test_xonly_pubkey(void) { + secp256k1_pubkey pk; + secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp; + secp256k1_ge pk1; + secp256k1_ge pk2; + secp256k1_fe y; + unsigned char sk[32]; + unsigned char xy_sk[32]; + unsigned char buf32[32]; + unsigned char ones32[32]; + unsigned char zeros64[64] = { 0 }; + int pk_parity; + int i; + + int ecount; + secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount); + secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount); + secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount); + + secp256k1_rand256(sk); + memset(ones32, 0xFF, 32); + secp256k1_rand256(xy_sk); + CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1); + + /* Test xonly_pubkey_from_pubkey */ + ecount = 0; + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(sign, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(verify, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, NULL, &pk_parity, &pk) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, NULL, &pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, NULL) == 0); + CHECK(ecount == 2); + memset(&pk, 0, sizeof(pk)); + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 0); + CHECK(ecount == 3); + + /* Choose a secret key such that the resulting pubkey and xonly_pubkey match. */ + memset(sk, 0, sizeof(sk)); + sk[0] = 1; + CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(memcmp(&pk, &xonly_pk, sizeof(pk)) == 0); + CHECK(pk_parity == 0); + + /* Choose a secret key such that pubkey and xonly_pubkey are each others + * negation. */ + sk[0] = 2; + CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(memcmp(&xonly_pk, &pk, sizeof(xonly_pk)) != 0); + CHECK(pk_parity == 1); + secp256k1_pubkey_load(ctx, &pk1, &pk); + secp256k1_pubkey_load(ctx, &pk2, (secp256k1_pubkey *) &xonly_pk); + CHECK(secp256k1_fe_equal(&pk1.x, &pk2.x) == 1); + secp256k1_fe_negate(&y, &pk2.y, 1); + CHECK(secp256k1_fe_equal(&pk1.y, &y) == 1); + + /* Test xonly_pubkey_serialize and xonly_pubkey_parse */ + ecount = 0; + CHECK(secp256k1_xonly_pubkey_serialize(none, NULL, &xonly_pk) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, NULL) == 0); + CHECK(memcmp(buf32, zeros64, 32) == 0); + CHECK(ecount == 2); + { + /* A pubkey filled with 0s will fail to serialize due to pubkey_load + * special casing. */ + secp256k1_xonly_pubkey pk_tmp; + memset(&pk_tmp, 0, sizeof(pk_tmp)); + CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, &pk_tmp) == 0); + } + /* pubkey_load called illegal callback */ + CHECK(ecount == 3); + + CHECK(secp256k1_xonly_pubkey_serialize(none, buf32, &xonly_pk) == 1); + ecount = 0; + CHECK(secp256k1_xonly_pubkey_parse(none, NULL, buf32) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, NULL) == 0); + CHECK(ecount == 2); + + /* Serialization and parse roundtrip */ + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, NULL, &pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk_tmp, buf32) == 1); + CHECK(memcmp(&xonly_pk, &xonly_pk_tmp, sizeof(xonly_pk)) == 0); + + /* Test parsing invalid field elements */ + memset(&xonly_pk, 1, sizeof(xonly_pk)); + /* Overflowing field element */ + CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, ones32) == 0); + CHECK(memcmp(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0); + memset(&xonly_pk, 1, sizeof(xonly_pk)); + /* There's no point with x-coordinate 0 on secp256k1 */ + CHECK(secp256k1_xonly_pubkey_parse(none, &xonly_pk, zeros64) == 0); + CHECK(memcmp(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0); + /* If a random 32-byte string can not be parsed with ec_pubkey_parse + * (because interpreted as X coordinate it does not correspond to a point on + * the curve) then xonly_pubkey_parse should fail as well. */ + for (i = 0; i < count; i++) { + unsigned char rand33[33]; + secp256k1_rand256(&rand33[1]); + rand33[0] = SECP256K1_TAG_PUBKEY_EVEN; + if (!secp256k1_ec_pubkey_parse(ctx, &pk, rand33, 33)) { + memset(&xonly_pk, 1, sizeof(xonly_pk)); + CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, &rand33[1]) == 0); + CHECK(memcmp(&xonly_pk, zeros64, sizeof(xonly_pk)) == 0); + } else { + CHECK(secp256k1_xonly_pubkey_parse(ctx, &xonly_pk, &rand33[1]) == 1); + } + } + CHECK(ecount == 2); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(verify); +} + void run_extrakeys_tests(void) { - /* TODO */ + /* xonly key test cases */ + test_xonly_pubkey(); } #endif From 176bfb1110147b5dca1834ea071acc846fb1cab3 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 12 May 2020 14:45:22 +0000 Subject: [PATCH 04/19] Separate helper function for ec_pubkey_tweak_add This is in preparation for allowing code reuse by xonly tweak add functions --- src/secp256k1.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/secp256k1.c b/src/secp256k1.c index 4f5b547d..297c0d7e 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -631,25 +631,26 @@ int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char * return secp256k1_ec_seckey_tweak_add(ctx, seckey, tweak); } +static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak) { + secp256k1_scalar term; + int overflow = 0; + secp256k1_scalar_set_b32(&term, tweak, &overflow); + return !overflow && secp256k1_eckey_pubkey_tweak_add(ecmult_ctx, p, &term); +} + int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) { secp256k1_ge p; - secp256k1_scalar term; int ret = 0; - int overflow = 0; VERIFY_CHECK(ctx != NULL); ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); ARG_CHECK(pubkey != NULL); ARG_CHECK(tweak != NULL); - secp256k1_scalar_set_b32(&term, tweak, &overflow); - ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); + ret = secp256k1_pubkey_load(ctx, &p, pubkey); memset(pubkey, 0, sizeof(*pubkey)); + ret = ret && secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &p, tweak); if (ret) { - if (secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term)) { - secp256k1_pubkey_save(pubkey, &p); - } else { - ret = 0; - } + secp256k1_pubkey_save(pubkey, &p); } return ret; From 910d9c284c33b77774a9316d4524f313357d441c Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 12 May 2020 14:49:12 +0000 Subject: [PATCH 05/19] extrakeys: Add xonly_pubkey_tweak_add & xonly_pubkey_tweak_add_test --- include/secp256k1_extrakeys.h | 64 +++++++++++ src/modules/extrakeys/main_impl.h | 40 +++++++ src/modules/extrakeys/tests_impl.h | 175 +++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+) diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index fdbfdeec..cda7eee0 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -76,6 +76,70 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_from_pubke const secp256k1_pubkey *pubkey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); +/** Tweak an x-only public key by adding the generator multiplied with tweak32 + * to it. + * + * Note that the resulting point can not in general be represented by an x-only + * pubkey because it may have an odd Y coordinate. Instead, the output_pubkey + * is a normal secp256k1_pubkey. + * + * Returns: 0 if the arguments are invalid or the resulting public key would be + * invalid (only when the tweak is the negation of the corresponding + * secret key). 1 otherwise. + * + * Args: ctx: pointer to a context object initialized for verification + * (cannot be NULL) + * Out: output_pubkey: pointer to a public key to store the result. Will be set + * to an invalid value if this function returns 0 (cannot + * be NULL) + * In: internal_pubkey: pointer to an x-only pubkey to apply the tweak to. + * (cannot be NULL). + * tweak32: pointer to a 32-byte tweak. If the tweak is invalid + * according to secp256k1_ec_seckey_verify, this function + * returns 0. For uniformly random 32-byte arrays the + * chance of being invalid is negligible (around 1 in + * 2^128) (cannot be NULL). + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add( + const secp256k1_context* ctx, + secp256k1_pubkey *output_pubkey, + const secp256k1_xonly_pubkey *internal_pubkey, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Checks that a tweaked pubkey is the result of calling + * secp256k1_xonly_pubkey_tweak_add with internal_pubkey and tweak32. + * + * The tweaked pubkey is represented by its 32-byte x-only serialization and + * its pk_parity, which can both be obtained by converting the result of + * tweak_add to a secp256k1_xonly_pubkey. + * + * Note that this alone does _not_ verify that the tweaked pubkey is a + * commitment. If the tweak is not chosen in a specific way, the tweaked pubkey + * can easily be the result of a different internal_pubkey and tweak. + * + * Returns: 0 if the arguments are invalid or the tweaked pubkey is not the + * result of tweaking the internal_pubkey with tweak32. 1 otherwise. + * Args: ctx: pointer to a context object initialized for verification + * (cannot be NULL) + * In: tweaked_pubkey32: pointer to a serialized xonly_pubkey (cannot be NULL) + * tweaked_pk_parity: the parity of the tweaked pubkey (whose serialization + * is passed in as tweaked_pubkey32). This must match the + * pk_parity value that is returned when calling + * secp256k1_xonly_pubkey with the tweaked pubkey, or + * this function will fail. + * internal_pubkey: pointer to an x-only public key object to apply the + * tweak to (cannot be NULL) + * tweak32: pointer to a 32-byte tweak (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_check( + const secp256k1_context* ctx, + const unsigned char *tweaked_pubkey32, + int tweaked_pk_parity, + const secp256k1_xonly_pubkey *internal_pubkey, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + #ifdef __cplusplus } #endif diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index 4f6fd362..c01ede12 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -85,4 +85,44 @@ int secp256k1_xonly_pubkey_from_pubkey(const secp256k1_context* ctx, secp256k1_x return 1; } +int secp256k1_xonly_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) { + secp256k1_ge pk; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output_pubkey != NULL); + memset(output_pubkey, 0, sizeof(*output_pubkey)); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(internal_pubkey != NULL); + ARG_CHECK(tweak32 != NULL); + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey) + || !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) { + return 0; + } + secp256k1_pubkey_save(output_pubkey, &pk); + return 1; +} + +int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const unsigned char *tweaked_pubkey32, int tweaked_pk_parity, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) { + secp256k1_ge pk; + unsigned char pk_expected32[32]; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(internal_pubkey != NULL); + ARG_CHECK(tweaked_pubkey32 != NULL); + ARG_CHECK(tweak32 != NULL); + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, internal_pubkey) + || !secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32)) { + return 0; + } + secp256k1_fe_normalize_var(&pk.x); + secp256k1_fe_normalize_var(&pk.y); + secp256k1_fe_get_b32(pk_expected32, &pk.x); + + return memcmp(&pk_expected32, tweaked_pubkey32, 32) == 0 + && secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity; +} + #endif diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index ebe51325..644ff303 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -137,9 +137,184 @@ void test_xonly_pubkey(void) { secp256k1_context_destroy(verify); } +void test_xonly_pubkey_tweak(void) { + unsigned char zeros64[64] = { 0 }; + unsigned char overflows[32]; + unsigned char sk[32]; + secp256k1_pubkey internal_pk; + secp256k1_xonly_pubkey internal_xonly_pk; + secp256k1_pubkey output_pk; + int pk_parity; + unsigned char tweak[32]; + int i; + + int ecount; + secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount); + secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount); + secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount); + + memset(overflows, 0xff, sizeof(overflows)); + secp256k1_rand256(tweak); + secp256k1_rand256(sk); + CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &internal_xonly_pk, &pk_parity, &internal_pk) == 1); + + ecount = 0; + CHECK(secp256k1_xonly_pubkey_tweak_add(none, &output_pk, &internal_xonly_pk, tweak) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(sign, &output_pk, &internal_xonly_pk, tweak) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(verify, NULL, &internal_xonly_pk, tweak) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, NULL, tweak) == 0); + CHECK(ecount == 4); + /* NULL internal_xonly_pk zeroes the output_pk */ + CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, NULL) == 0); + CHECK(ecount == 5); + /* NULL tweak zeroes the output_pk */ + CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0); + + /* Invalid tweak zeroes the output_pk */ + CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, overflows) == 0); + CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0); + + /* A zero tweak is fine */ + CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, zeros64) == 1); + + /* Fails if the resulting key was infinity */ + for (i = 0; i < count; i++) { + secp256k1_scalar scalar_tweak; + /* Because sk may be negated before adding, we need to try with tweak = + * sk as well as tweak = -sk. */ + secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL); + secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak); + secp256k1_scalar_get_b32(tweak, &scalar_tweak); + CHECK((secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, sk) == 0) + || (secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 0)); + CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0); + } + + /* Invalid pk with a valid tweak */ + memset(&internal_xonly_pk, 0, sizeof(internal_xonly_pk)); + secp256k1_rand256(tweak); + ecount = 0; + CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 0); + CHECK(ecount == 1); + CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(verify); +} + +void test_xonly_pubkey_tweak_check(void) { + unsigned char zeros64[64] = { 0 }; + unsigned char overflows[32]; + unsigned char sk[32]; + secp256k1_pubkey internal_pk; + secp256k1_xonly_pubkey internal_xonly_pk; + secp256k1_pubkey output_pk; + secp256k1_xonly_pubkey output_xonly_pk; + unsigned char output_pk32[32]; + unsigned char buf32[32]; + int pk_parity; + unsigned char tweak[32]; + + int ecount; + secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount); + secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount); + secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount); + + memset(overflows, 0xff, sizeof(overflows)); + secp256k1_rand256(tweak); + secp256k1_rand256(sk); + CHECK(secp256k1_ec_pubkey_create(ctx, &internal_pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &internal_xonly_pk, &pk_parity, &internal_pk) == 1); + + ecount = 0; + CHECK(secp256k1_xonly_pubkey_tweak_add(verify, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(verify, &output_xonly_pk, &pk_parity, &output_pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &output_xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(none, buf32, pk_parity, &internal_xonly_pk, tweak) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(sign, buf32, pk_parity, &internal_xonly_pk, tweak) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, NULL, pk_parity, &internal_xonly_pk, tweak) == 0); + CHECK(ecount == 3); + /* invalid pk_parity value */ + CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, 2, &internal_xonly_pk, tweak) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, NULL, tweak) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(verify, buf32, pk_parity, &internal_xonly_pk, NULL) == 0); + CHECK(ecount == 5); + + memset(tweak, 1, sizeof(tweak)); + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &internal_xonly_pk, NULL, &internal_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, tweak) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &output_xonly_pk, &pk_parity, &output_pk) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk32, &output_xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, tweak) == 1); + + /* Wrong pk_parity */ + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, !pk_parity, &internal_xonly_pk, tweak) == 0); + /* Wrong public key */ + CHECK(secp256k1_xonly_pubkey_serialize(ctx, buf32, &internal_xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, buf32, pk_parity, &internal_xonly_pk, tweak) == 0); + + /* Overflowing tweak not allowed */ + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk32, pk_parity, &internal_xonly_pk, overflows) == 0); + CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk, &internal_xonly_pk, overflows) == 0); + CHECK(memcmp(&output_pk, zeros64, sizeof(output_pk)) == 0); + CHECK(ecount == 5); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(verify); +} + +/* Starts with an initial pubkey and recursively creates N_PUBKEYS - 1 + * additional pubkeys by calling tweak_add. Then verifies every tweak starting + * from the last pubkey. */ +#define N_PUBKEYS 32 +void test_xonly_pubkey_tweak_recursive(void) { + unsigned char sk[32]; + secp256k1_pubkey pk[N_PUBKEYS]; + unsigned char pk_serialized[32]; + unsigned char tweak[N_PUBKEYS - 1][32]; + int i; + + secp256k1_rand256(sk); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk) == 1); + /* Add tweaks */ + for (i = 0; i < N_PUBKEYS - 1; i++) { + secp256k1_xonly_pubkey xonly_pk; + memset(tweak[i], i + 1, sizeof(tweak[i])); + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i]) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &pk[i + 1], &xonly_pk, tweak[i]) == 1); + } + + /* Verify tweaks */ + for (i = N_PUBKEYS - 1; i > 0; i--) { + secp256k1_xonly_pubkey xonly_pk; + int pk_parity; + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, &pk_parity, &pk[i]) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk_serialized, &xonly_pk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_pk, NULL, &pk[i - 1]) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk_serialized, pk_parity, &xonly_pk, tweak[i - 1]) == 1); + } +} +#undef N_PUBKEYS + void run_extrakeys_tests(void) { /* xonly key test cases */ test_xonly_pubkey(); + test_xonly_pubkey_tweak(); + test_xonly_pubkey_tweak_check(); + test_xonly_pubkey_tweak_recursive(); } #endif From f0010349b876bc6b3f0a6ec6c8bad0b12ca17b51 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 12 May 2020 14:43:48 +0000 Subject: [PATCH 06/19] Separate helper functions for pubkey_create and seckey_tweak_add This is in preparation for allowing code reuse by keypair functions --- src/secp256k1.c | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/secp256k1.c b/src/secp256k1.c index 297c0d7e..675227f1 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -549,10 +549,21 @@ int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char return ret; } -int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) { +static int secp256k1_ec_pubkey_create_helper(const secp256k1_ecmult_gen_context *ecmult_gen_ctx, secp256k1_scalar *seckey_scalar, secp256k1_ge *p, const unsigned char *seckey) { secp256k1_gej pj; + int ret; + + ret = secp256k1_scalar_set_b32_seckey(seckey_scalar, seckey); + secp256k1_scalar_cmov(seckey_scalar, &secp256k1_scalar_one, !ret); + + secp256k1_ecmult_gen(ecmult_gen_ctx, &pj, seckey_scalar); + secp256k1_ge_set_gej(p, &pj); + return ret; +} + +int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) { secp256k1_ge p; - secp256k1_scalar sec; + secp256k1_scalar seckey_scalar; int ret = 0; VERIFY_CHECK(ctx != NULL); ARG_CHECK(pubkey != NULL); @@ -560,15 +571,11 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); ARG_CHECK(seckey != NULL); - ret = secp256k1_scalar_set_b32_seckey(&sec, seckey); - secp256k1_scalar_cmov(&sec, &secp256k1_scalar_one, !ret); - - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec); - secp256k1_ge_set_gej(&p, &pj); + ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &seckey_scalar, &p, seckey); secp256k1_pubkey_save(pubkey, &p); memczero(pubkey, sizeof(*pubkey), !ret); - secp256k1_scalar_clear(&sec); + secp256k1_scalar_clear(&seckey_scalar); return ret; } @@ -606,24 +613,31 @@ int secp256k1_ec_pubkey_negate(const secp256k1_context* ctx, secp256k1_pubkey *p return ret; } -int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + +static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak) { secp256k1_scalar term; + int overflow = 0; + int ret = 0; + + secp256k1_scalar_set_b32(&term, tweak, &overflow); + ret = (!overflow) & secp256k1_eckey_privkey_tweak_add(sec, &term); + secp256k1_scalar_clear(&term); + return ret; +} + +int secp256k1_ec_seckey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { secp256k1_scalar sec; int ret = 0; - int overflow = 0; VERIFY_CHECK(ctx != NULL); ARG_CHECK(seckey != NULL); ARG_CHECK(tweak != NULL); - secp256k1_scalar_set_b32(&term, tweak, &overflow); ret = secp256k1_scalar_set_b32_seckey(&sec, seckey); - - ret &= (!overflow) & secp256k1_eckey_privkey_tweak_add(&sec, &term); + ret &= secp256k1_ec_seckey_tweak_add_helper(&sec, tweak); secp256k1_scalar_cmov(&sec, &secp256k1_scalar_zero, !ret); secp256k1_scalar_get_b32(seckey, &sec); secp256k1_scalar_clear(&sec); - secp256k1_scalar_clear(&term); return ret; } From 58254463f9a2e96d893157a341c9953c440fdf60 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 12 May 2020 14:52:34 +0000 Subject: [PATCH 07/19] extrakeys: Add keypair struct with create, pub and pub_xonly --- include/secp256k1_extrakeys.h | 62 +++++++++++++++++++ src/modules/extrakeys/main_impl.h | 89 ++++++++++++++++++++++++++++ src/modules/extrakeys/tests_impl.h | 95 ++++++++++++++++++++++++++++++ src/valgrind_ctime_test.c | 14 +++++ 4 files changed, 260 insertions(+) diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index cda7eee0..96e470db 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -23,6 +23,17 @@ typedef struct { unsigned char data[64]; } secp256k1_xonly_pubkey; +/** Opaque data structure that holds a keypair consisting of a secret and a + * public key. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 96 bytes in size, and can be safely copied/moved. + */ +typedef struct { + unsigned char data[96]; +} secp256k1_keypair; + /** Parse a 32-byte sequence into a xonly_pubkey object. * * Returns: 1 if the public key was fully valid. @@ -140,6 +151,57 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_ const unsigned char *tweak32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); +/** Compute the keypair for a secret key. + * + * Returns: 1: secret was valid, keypair is ready to use + * 0: secret was invalid, try again with a different secret + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: keypair: pointer to the created keypair (cannot be NULL) + * In: seckey: pointer to a 32-byte secret key (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create( + const secp256k1_context* ctx, + secp256k1_keypair *keypair, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Get the public key from a keypair. + * + * Returns: 0 if the arguments are invalid. 1 otherwise. + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to + * the keypair public key. If not, it's set to an invalid value. + * (cannot be NULL) + * In: keypair: pointer to a keypair (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const secp256k1_keypair *keypair +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Get the x-only public key from a keypair. + * + * This is the same as calling secp256k1_keypair_pub and then + * secp256k1_xonly_pubkey_from_pubkey. + * + * Returns: 0 if the arguments are invalid. 1 otherwise. + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set + * to the keypair public key after converting it to an + * xonly_pubkey. If not, it's set to an invalid value (cannot be + * NULL). + * pk_parity: pointer to an integer that will be set to the pk_parity + * argument of secp256k1_xonly_pubkey_from_pubkey (can be NULL). + * In: keypair: pointer to a keypair (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub( + const secp256k1_context* ctx, + secp256k1_xonly_pubkey *pubkey, + int *pk_parity, + const secp256k1_keypair *keypair +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + #ifdef __cplusplus } #endif diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index c01ede12..a0c44e98 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -125,4 +125,93 @@ int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const u && secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity; } +static void secp256k1_keypair_save(secp256k1_keypair *keypair, const secp256k1_scalar *sk, secp256k1_ge *pk) { + secp256k1_scalar_get_b32(&keypair->data[0], sk); + secp256k1_pubkey_save((secp256k1_pubkey *)&keypair->data[32], pk); +} + + +static int secp256k1_keypair_seckey_load(const secp256k1_context* ctx, secp256k1_scalar *sk, const secp256k1_keypair *keypair) { + int ret; + + ret = secp256k1_scalar_set_b32_seckey(sk, &keypair->data[0]); + /* We can declassify ret here because sk is only zero if a keypair function + * failed (which zeroes the keypair) and its return value is ignored. */ + secp256k1_declassify(ctx, &ret, sizeof(ret)); + ARG_CHECK(ret); + return ret; +} + +/* Load a keypair into pk and sk (if non-NULL). This function declassifies pk + * and ARG_CHECKs that the keypair is not invalid. It always initializes sk and + * pk with dummy values. */ +static int secp256k1_keypair_load(const secp256k1_context* ctx, secp256k1_scalar *sk, secp256k1_ge *pk, const secp256k1_keypair *keypair) { + int ret; + const secp256k1_pubkey *pubkey = (const secp256k1_pubkey *)&keypair->data[32]; + + /* Need to declassify the pubkey because pubkey_load ARG_CHECKs if it's + * invalid. */ + secp256k1_declassify(ctx, pubkey, sizeof(*pubkey)); + ret = secp256k1_pubkey_load(ctx, pk, pubkey); + if (sk != NULL) { + ret = ret && secp256k1_keypair_seckey_load(ctx, sk, keypair); + } + if (!ret) { + *pk = secp256k1_ge_const_g; + if (sk != NULL) { + *sk = secp256k1_scalar_one; + } + } + return ret; +} + +int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *seckey32) { + secp256k1_scalar sk; + secp256k1_ge pk; + int ret = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(keypair != NULL); + memset(keypair, 0, sizeof(*keypair)); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(seckey32 != NULL); + + ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32); + secp256k1_keypair_save(keypair, &sk, &pk); + memczero(keypair, sizeof(*keypair), !ret); + + secp256k1_scalar_clear(&sk); + return ret; +} + +int secp256k1_keypair_pub(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(keypair != NULL); + + memcpy(pubkey->data, &keypair->data[32], sizeof(*pubkey)); + return 1; +} + +int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, int *pk_parity, const secp256k1_keypair *keypair) { + secp256k1_ge pk; + int tmp; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(keypair != NULL); + + if (!secp256k1_keypair_load(ctx, NULL, &pk, keypair)) { + return 0; + } + tmp = secp256k1_extrakeys_ge_even_y(&pk); + if (pk_parity != NULL) { + *pk_parity = tmp; + } + secp256k1_xonly_pubkey_save(pubkey, &pk); + + return 1; +} + #endif diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index 644ff303..af289c86 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -309,12 +309,107 @@ void test_xonly_pubkey_tweak_recursive(void) { } #undef N_PUBKEYS +void test_keypair(void) { + unsigned char sk[32]; + unsigned char zeros96[96] = { 0 }; + unsigned char overflows[32]; + secp256k1_keypair keypair; + secp256k1_pubkey pk, pk_tmp; + secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp; + int pk_parity, pk_parity_tmp; + int ecount; + secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount); + secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount); + secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount); + + CHECK(sizeof(zeros96) == sizeof(keypair)); + memset(overflows, 0xFF, sizeof(overflows)); + + /* Test keypair_create */ + ecount = 0; + secp256k1_rand256(sk); + CHECK(secp256k1_keypair_create(none, &keypair, sk) == 0); + CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_keypair_create(verify, &keypair, sk) == 0); + CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1); + CHECK(secp256k1_keypair_create(sign, NULL, sk) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_keypair_create(sign, &keypair, NULL) == 0); + CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0); + CHECK(ecount == 4); + + /* Invalid secret key */ + CHECK(secp256k1_keypair_create(sign, &keypair, zeros96) == 0); + CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0); + CHECK(secp256k1_keypair_create(sign, &keypair, overflows) == 0); + CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0); + + /* Test keypair_pub */ + ecount = 0; + secp256k1_rand256(sk); + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1); + CHECK(secp256k1_keypair_pub(none, NULL, &keypair) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_keypair_pub(none, &pk, NULL) == 0); + CHECK(ecount == 2); + CHECK(memcmp(zeros96, &pk, sizeof(pk)) == 0); + + /* Using an invalid keypair is fine for keypair_pub */ + memset(&keypair, 0, sizeof(keypair)); + CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1); + CHECK(memcmp(zeros96, &pk, sizeof(pk)) == 0); + + /* keypair holds the same pubkey as pubkey_create */ + CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1); + CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1); + CHECK(secp256k1_keypair_pub(none, &pk_tmp, &keypair) == 1); + CHECK(memcmp(&pk, &pk_tmp, sizeof(pk)) == 0); + + /** Test keypair_xonly_pub **/ + ecount = 0; + secp256k1_rand256(sk); + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1); + CHECK(secp256k1_keypair_xonly_pub(none, NULL, &pk_parity, &keypair) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, NULL, &keypair) == 1); + CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, NULL) == 0); + CHECK(ecount == 2); + CHECK(memcmp(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0); + /* Using an invalid keypair will set the xonly_pk to 0 (first reset + * xonly_pk). */ + CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1); + memset(&keypair, 0, sizeof(keypair)); + CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 0); + CHECK(memcmp(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0); + CHECK(ecount == 3); + + /** keypair holds the same xonly pubkey as pubkey_create **/ + CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1); + CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1); + CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk_tmp, &pk_parity_tmp, &keypair) == 1); + CHECK(memcmp(&xonly_pk, &xonly_pk_tmp, sizeof(pk)) == 0); + CHECK(pk_parity == pk_parity_tmp); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(verify); +} + void run_extrakeys_tests(void) { /* xonly key test cases */ test_xonly_pubkey(); test_xonly_pubkey_tweak(); test_xonly_pubkey_tweak_check(); test_xonly_pubkey_tweak_recursive(); + + /* keypair tests */ + test_keypair(); } #endif diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index 090fde2b..09b578a4 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -17,6 +17,10 @@ # include "include/secp256k1_recovery.h" #endif +#if ENABLE_MODULE_EXTRAKEYS +# include "include/secp256k1_extrakeys.h" +#endif + int main(void) { secp256k1_context* ctx; secp256k1_ecdsa_signature signature; @@ -33,6 +37,9 @@ int main(void) { secp256k1_ecdsa_recoverable_signature recoverable_signature; int recid; #endif +#if ENABLE_MODULE_EXTRAKEYS + secp256k1_keypair keypair; +#endif if (!RUNNING_ON_VALGRIND) { fprintf(stderr, "This test can only usefully be run inside valgrind.\n"); @@ -115,6 +122,13 @@ int main(void) { VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); CHECK(ret); +#if ENABLE_MODULE_EXTRAKEYS + VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + ret = secp256k1_keypair_create(ctx, &keypair, key); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); +#endif + secp256k1_context_destroy(ctx); return 0; } From 6fcb5b845d2832ce019d60507033f74426290768 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Wed, 22 Jul 2020 09:09:34 +0000 Subject: [PATCH 08/19] extrakeys: Add keypair_xonly_tweak_add --- include/secp256k1_extrakeys.h | 27 +++++++ src/modules/extrakeys/main_impl.h | 31 ++++++++ src/modules/extrakeys/tests_impl.h | 109 +++++++++++++++++++++++++++++ src/valgrind_ctime_test.c | 11 ++- 4 files changed, 177 insertions(+), 1 deletion(-) diff --git a/include/secp256k1_extrakeys.h b/include/secp256k1_extrakeys.h index 96e470db..0c5dff2c 100644 --- a/include/secp256k1_extrakeys.h +++ b/include/secp256k1_extrakeys.h @@ -202,6 +202,33 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub( const secp256k1_keypair *keypair ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); +/** Tweak a keypair by adding tweak32 to the secret key and updating the public + * key accordingly. + * + * Calling this function and then secp256k1_keypair_pub results in the same + * public key as calling secp256k1_keypair_xonly_pub and then + * secp256k1_xonly_pubkey_tweak_add. + * + * Returns: 0 if the arguments are invalid or the resulting keypair would be + * invalid (only when the tweak is the negation of the keypair's + * secret key). 1 otherwise. + * + * Args: ctx: pointer to a context object initialized for verification + * (cannot be NULL) + * In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to + * an invalid value if this function returns 0 (cannot be + * NULL). + * In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according + * to secp256k1_ec_seckey_verify, this function returns 0. For + * uniformly random 32-byte arrays the chance of being invalid + * is negligible (around 1 in 2^128) (cannot be NULL). + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add( + const secp256k1_context* ctx, + secp256k1_keypair *keypair, + const unsigned char *tweak32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + #ifdef __cplusplus } #endif diff --git a/src/modules/extrakeys/main_impl.h b/src/modules/extrakeys/main_impl.h index a0c44e98..d3192153 100644 --- a/src/modules/extrakeys/main_impl.h +++ b/src/modules/extrakeys/main_impl.h @@ -214,4 +214,35 @@ int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pu return 1; } +int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *tweak32) { + secp256k1_ge pk; + secp256k1_scalar sk; + int y_parity; + int ret; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(keypair != NULL); + ARG_CHECK(tweak32 != NULL); + + ret = secp256k1_keypair_load(ctx, &sk, &pk, keypair); + memset(keypair, 0, sizeof(*keypair)); + + y_parity = secp256k1_extrakeys_ge_even_y(&pk); + if (y_parity == 1) { + secp256k1_scalar_negate(&sk, &sk); + } + + ret &= secp256k1_ec_seckey_tweak_add_helper(&sk, tweak32); + ret &= secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32); + + secp256k1_declassify(ctx, &ret, sizeof(ret)); + if (ret) { + secp256k1_keypair_save(keypair, &sk, &pk); + } + + secp256k1_scalar_clear(&sk); + return ret; +} + #endif diff --git a/src/modules/extrakeys/tests_impl.h b/src/modules/extrakeys/tests_impl.h index af289c86..fc9d40ed 100644 --- a/src/modules/extrakeys/tests_impl.h +++ b/src/modules/extrakeys/tests_impl.h @@ -401,6 +401,114 @@ void test_keypair(void) { secp256k1_context_destroy(verify); } +void test_keypair_add(void) { + unsigned char sk[32]; + secp256k1_keypair keypair; + unsigned char overflows[32]; + unsigned char zeros96[96] = { 0 }; + unsigned char tweak[32]; + int i; + int ecount = 0; + secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount); + secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount); + secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount); + + CHECK(sizeof(zeros96) == sizeof(keypair)); + secp256k1_rand256(sk); + secp256k1_rand256(tweak); + memset(overflows, 0xFF, 32); + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + + CHECK(secp256k1_keypair_xonly_tweak_add(none, &keypair, tweak) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(sign, &keypair, tweak) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(verify, NULL, tweak) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, NULL) == 0); + CHECK(ecount == 4); + /* This does not set the keypair to zeroes */ + CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) != 0); + + /* Invalid tweak zeroes the keypair */ + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, overflows) == 0); + CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0); + + /* A zero tweak is fine */ + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, zeros96) == 1); + + /* Fails if the resulting keypair was (sk=0, pk=infinity) */ + for (i = 0; i < count; i++) { + secp256k1_scalar scalar_tweak; + secp256k1_keypair keypair_tmp; + secp256k1_rand256(sk); + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + memcpy(&keypair_tmp, &keypair, sizeof(keypair)); + /* Because sk may be negated before adding, we need to try with tweak = + * sk as well as tweak = -sk. */ + secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL); + secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak); + secp256k1_scalar_get_b32(tweak, &scalar_tweak); + CHECK((secp256k1_keypair_xonly_tweak_add(ctx, &keypair, sk) == 0) + || (secp256k1_keypair_xonly_tweak_add(ctx, &keypair_tmp, tweak) == 0)); + CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0 + || memcmp(&keypair_tmp, zeros96, sizeof(keypair_tmp)) == 0); + } + + /* Invalid keypair with a valid tweak */ + memset(&keypair, 0, sizeof(keypair)); + secp256k1_rand256(tweak); + ecount = 0; + CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0); + CHECK(ecount == 1); + CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0); + /* Only seckey part of keypair invalid */ + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + memset(&keypair, 0, 32); + CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0); + CHECK(ecount == 2); + /* Only pubkey part of keypair invalid */ + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + memset(&keypair.data[32], 0, 64); + CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0); + CHECK(ecount == 3); + + /* Check that the keypair_tweak_add implementation is correct */ + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + for (i = 0; i < count; i++) { + secp256k1_xonly_pubkey internal_pk; + secp256k1_xonly_pubkey output_pk; + secp256k1_pubkey output_pk_xy; + secp256k1_pubkey output_pk_expected; + unsigned char pk32[32]; + int pk_parity; + + secp256k1_rand256(tweak); + CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1); + + /* Check that it passes xonly_pubkey_tweak_add_check */ + CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk32, &output_pk) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk32, pk_parity, &internal_pk, tweak) == 1); + + /* Check that the resulting pubkey matches xonly_pubkey_tweak_add */ + CHECK(secp256k1_keypair_pub(ctx, &output_pk_xy, &keypair) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk_expected, &internal_pk, tweak) == 1); + CHECK(memcmp(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0); + + /* Check that the secret key in the keypair is tweaked correctly */ + CHECK(secp256k1_ec_pubkey_create(ctx, &output_pk_expected, &keypair.data[0]) == 1); + CHECK(memcmp(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0); + } + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(verify); +} + void run_extrakeys_tests(void) { /* xonly key test cases */ test_xonly_pubkey(); @@ -410,6 +518,7 @@ void run_extrakeys_tests(void) { /* keypair tests */ test_keypair(); + test_keypair_add(); } #endif diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index 09b578a4..3d705633 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -57,7 +57,9 @@ int main(void) { msg[i] = i + 1; } - ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_DECLASSIFY); + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN + | SECP256K1_CONTEXT_VERIFY + | SECP256K1_CONTEXT_DECLASSIFY); /* Test keygen. */ VALGRIND_MAKE_MEM_UNDEFINED(key, 32); @@ -122,11 +124,18 @@ int main(void) { VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); CHECK(ret); + /* Test keypair_create and keypair_xonly_tweak_add. */ #if ENABLE_MODULE_EXTRAKEYS VALGRIND_MAKE_MEM_UNDEFINED(key, 32); ret = secp256k1_keypair_create(ctx, &keypair, key); VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); CHECK(ret == 1); + + /* The tweak is not treated as a secret in keypair_tweak_add */ + VALGRIND_MAKE_MEM_DEFINED(msg, 32); + ret = secp256k1_keypair_xonly_tweak_add(ctx, &keypair, msg); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); #endif secp256k1_context_destroy(ctx); From eabd9bc46a31c0da6db6d88840eadbe9006447b1 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 14 Nov 2019 13:53:46 +0000 Subject: [PATCH 09/19] Allow initializing tagged sha256 This will be used by the schnorrsig module --- src/hash_impl.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/hash_impl.h b/src/hash_impl.h index 1985a078..40977258 100644 --- a/src/hash_impl.h +++ b/src/hash_impl.h @@ -164,6 +164,19 @@ static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out memcpy(out32, (const unsigned char*)out, 32); } +/* Initializes a sha256 struct and writes the 64 byte string + * SHA256(tag)||SHA256(tag) into it. */ +static void secp256k1_sha256_initialize_tagged(secp256k1_sha256 *hash, const unsigned char *tag, size_t taglen) { + unsigned char buf[32]; + secp256k1_sha256_initialize(hash); + secp256k1_sha256_write(hash, tag, taglen); + secp256k1_sha256_finalize(hash, buf); + + secp256k1_sha256_initialize(hash); + secp256k1_sha256_write(hash, buf, 32); + secp256k1_sha256_write(hash, buf, 32); +} + static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) { size_t n; unsigned char rkey[64]; From 7a703fd97db0161bae07ef84513ddde6e0d27353 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 12 May 2020 21:19:03 +0000 Subject: [PATCH 10/19] schnorrsig: Init empty experimental module --- Makefile.am | 4 ++++ configure.ac | 20 +++++++++++++++++++- include/secp256k1_schnorrsig.h | 17 +++++++++++++++++ src/modules/schnorrsig/Makefile.am.include | 3 +++ src/modules/schnorrsig/main_impl.h | 16 ++++++++++++++++ src/modules/schnorrsig/tests_impl.h | 17 +++++++++++++++++ src/secp256k1.c | 4 ++++ src/tests.c | 8 ++++++++ 8 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 include/secp256k1_schnorrsig.h create mode 100644 src/modules/schnorrsig/Makefile.am.include create mode 100644 src/modules/schnorrsig/main_impl.h create mode 100644 src/modules/schnorrsig/tests_impl.h diff --git a/Makefile.am b/Makefile.am index 9adfbaed..990d78da 100644 --- a/Makefile.am +++ b/Makefile.am @@ -157,3 +157,7 @@ endif if ENABLE_MODULE_EXTRAKEYS include src/modules/extrakeys/Makefile.am.include endif + +if ENABLE_MODULE_SCHNORRSIG +include src/modules/schnorrsig/Makefile.am.include +endif diff --git a/configure.ac b/configure.ac index 4d51eb5a..6fe8984f 100644 --- a/configure.ac +++ b/configure.ac @@ -141,6 +141,11 @@ AC_ARG_ENABLE(module_extrakeys, [enable_module_extrakeys=$enableval], [enable_module_extrakeys=no]) +AC_ARG_ENABLE(module_schnorrsig, + AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]), + [enable_module_schnorrsig=$enableval], + [enable_module_schnorrsig=no]) + AC_ARG_ENABLE(external_default_callbacks, AS_HELP_STRING([--enable-external-default-callbacks],[enable external default callback functions [default=no]]), [use_external_default_callbacks=$enableval], @@ -426,6 +431,13 @@ if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) fi +if test x"$enable_module_schnorrsig" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) + enable_module_extrakeys=yes +fi + +# Test if extrakeys is set after the schnorrsig module to allow the schnorrsig +# module to set enable_module_extrakeys=yes if test x"$enable_module_extrakeys" = x"yes"; then AC_DEFINE(ENABLE_MODULE_EXTRAKEYS, 1, [Define this symbol to enable the extrakeys module]) fi @@ -444,6 +456,7 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys]) + AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) AC_MSG_NOTICE([******]) else if test x"$enable_module_ecdh" = x"yes"; then @@ -452,6 +465,9 @@ else if test x"$enable_module_extrakeys" = x"yes"; then AC_MSG_ERROR([extrakeys module is experimental. Use --enable-experimental to allow.]) fi + if test x"$enable_module_schnorrsig" = x"yes"; then + AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) + fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi @@ -469,8 +485,9 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) -AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) @@ -491,6 +508,7 @@ echo " with coverage = $enable_coverage" echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" echo " module extrakeys = $enable_module_extrakeys" +echo " module schnorrsig = $enable_module_schnorrsig" echo echo " asm = $set_asm" echo " bignum = $set_bignum" diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h new file mode 100644 index 00000000..c7961298 --- /dev/null +++ b/include/secp256k1_schnorrsig.h @@ -0,0 +1,17 @@ +#ifndef SECP256K1_SCHNORRSIG_H +#define SECP256K1_SCHNORRSIG_H + +#include "secp256k1.h" +#include "secp256k1_extrakeys.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /* TODO */ + +#ifdef __cplusplus +} +#endif + +#endif /* SECP256K1_SCHNORRSIG_H */ diff --git a/src/modules/schnorrsig/Makefile.am.include b/src/modules/schnorrsig/Makefile.am.include new file mode 100644 index 00000000..0efafa8a --- /dev/null +++ b/src/modules/schnorrsig/Makefile.am.include @@ -0,0 +1,3 @@ +include_HEADERS += include/secp256k1_schnorrsig.h +noinst_HEADERS += src/modules/schnorrsig/main_impl.h +noinst_HEADERS += src/modules/schnorrsig/tests_impl.h \ No newline at end of file diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h new file mode 100644 index 00000000..fa9b1258 --- /dev/null +++ b/src/modules/schnorrsig/main_impl.h @@ -0,0 +1,16 @@ +/********************************************************************** + * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_ +#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_ + +#include "include/secp256k1.h" +#include "include/secp256k1_schnorrsig.h" +#include "hash.h" + +/* TODO */ + +#endif diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h new file mode 100644 index 00000000..9c2a4b25 --- /dev/null +++ b/src/modules/schnorrsig/tests_impl.h @@ -0,0 +1,17 @@ +/********************************************************************** + * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_ + +#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_ + +#include "secp256k1_schnorrsig.h" + +void run_schnorrsig_tests(void) { + /* TODO */ +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 675227f1..1c22b86b 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -761,3 +761,7 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * #ifdef ENABLE_MODULE_EXTRAKEYS # include "modules/extrakeys/main_impl.h" #endif + +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/main_impl.h" +#endif diff --git a/src/tests.c b/src/tests.c index 9ad9e32a..4f88287a 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5310,6 +5310,10 @@ void run_ecdsa_openssl(void) { # include "modules/extrakeys/tests_impl.h" #endif +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/tests_impl.h" +#endif + void run_memczero_test(void) { unsigned char buf1[6] = {1, 2, 3, 4, 5, 6}; unsigned char buf2[sizeof(buf1)]; @@ -5620,6 +5624,10 @@ int main(int argc, char **argv) { run_extrakeys_tests(); #endif +#ifdef ENABLE_MODULE_SCHNORRSIG + run_schnorrsig_tests(); +#endif + /* util tests */ run_memczero_test(); From 7332d2db6b62fda851f9ed8adbfda187a875b84e Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 12 May 2020 21:23:22 +0000 Subject: [PATCH 11/19] schnorrsig: Add BIP-340 nonce function --- include/secp256k1_schnorrsig.h | 50 +++++++++++++++- src/modules/schnorrsig/main_impl.h | 82 +++++++++++++++++++++++++- src/modules/schnorrsig/tests_impl.h | 90 ++++++++++++++++++++++++++++- src/testrand.h | 3 + src/testrand_impl.h | 4 ++ 5 files changed, 226 insertions(+), 3 deletions(-) diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h index c7961298..67f198fd 100644 --- a/include/secp256k1_schnorrsig.h +++ b/include/secp256k1_schnorrsig.h @@ -8,7 +8,55 @@ extern "C" { #endif - /* TODO */ +/** This module implements a variant of Schnorr signatures compliant with + * Bitcoin Improvement Proposal 340 "Schnorr Signatures for secp256k1" + * (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). + */ + +/** A pointer to a function to deterministically generate a nonce. + * + * Same as secp256k1_nonce function with the exception of accepting an + * additional pubkey argument and not requiring an attempt argument. The pubkey + * argument can protect signature schemes with key-prefixed challenge hash + * inputs against reusing the nonce when signing with the wrong precomputed + * pubkey. + * + * Returns: 1 if a nonce was successfully generated. 0 will cause signing to + * return an error. + * Out: nonce32: pointer to a 32-byte array to be filled by the function. + * In: msg32: the 32-byte message hash being verified (will not be NULL) + * key32: pointer to a 32-byte secret key (will not be NULL) + * xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32 + * (will not be NULL) + * algo16: pointer to a 16-byte array describing the signature + * algorithm (will not be NULL). + * data: Arbitrary data pointer that is passed through. + * + * Except for test cases, this function should compute some cryptographic hash of + * the message, the key, the pubkey, the algorithm description, and data. + */ +typedef int (*secp256k1_nonce_function_hardened)( + unsigned char *nonce32, + const unsigned char *msg32, + const unsigned char *key32, + const unsigned char *xonly_pk32, + const unsigned char *algo16, + void *data +); + +/** An implementation of the nonce generation function as defined in Bitcoin + * Improvement Proposal 340 "Schnorr Signatures for secp256k1" + * (https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). + * + * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of + * auxiliary random data as defined in BIP-340. If the data pointer is NULL, + * schnorrsig_sign does not produce BIP-340 compliant signatures. The algo16 + * argument must be non-NULL, otherwise the function will fail and return 0. + * The hash will be tagged with algo16 after removing all terminating null + * bytes. Therefore, to create BIP-340 compliant signatures, algo16 must be set + * to "BIP0340/nonce\0\0\0" + */ +SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340; #ifdef __cplusplus } diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h index fa9b1258..3f0c2bab 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -11,6 +11,86 @@ #include "include/secp256k1_schnorrsig.h" #include "hash.h" -/* TODO */ +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIP0340/nonce")||SHA256("BIP0340/nonce"). */ +static void secp256k1_nonce_function_bip340_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x46615b35ul; + sha->s[1] = 0xf4bfbff7ul; + sha->s[2] = 0x9f8dc671ul; + sha->s[3] = 0x83627ab3ul; + sha->s[4] = 0x60217180ul; + sha->s[5] = 0x57358661ul; + sha->s[6] = 0x21a29e54ul; + sha->s[7] = 0x68b07b4cul; + + sha->bytes = 64; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIP0340/aux")||SHA256("BIP0340/aux"). */ +static void secp256k1_nonce_function_bip340_sha256_tagged_aux(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x24dd3219ul; + sha->s[1] = 0x4eba7e70ul; + sha->s[2] = 0xca0fabb9ul; + sha->s[3] = 0x0fa3166dul; + sha->s[4] = 0x3afbe4b1ul; + sha->s[5] = 0x4c44df97ul; + sha->s[6] = 0x4aac2739ul; + sha->s[7] = 0x249e850aul; + + sha->bytes = 64; +} + +/* algo16 argument for nonce_function_bip340 to derive the nonce exactly as stated in BIP-340 + * by using the correct tagged hash function. */ +static const unsigned char bip340_algo16[16] = "BIP0340/nonce\0\0\0"; + +static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) { + secp256k1_sha256 sha; + unsigned char masked_key[32]; + int i; + + if (algo16 == NULL) { + return 0; + } + + if (data != NULL) { + secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha); + secp256k1_sha256_write(&sha, data, 32); + secp256k1_sha256_finalize(&sha, masked_key); + for (i = 0; i < 32; i++) { + masked_key[i] ^= key32[i]; + } + } + + /* Tag the hash with algo16 which is important to avoid nonce reuse across + * algorithms. If this nonce function is used in BIP-340 signing as defined + * in the spec, an optimized tagging implementation is used. */ + if (memcmp(algo16, bip340_algo16, 16) == 0) { + secp256k1_nonce_function_bip340_sha256_tagged(&sha); + } else { + int algo16_len = 16; + /* Remove terminating null bytes */ + while (algo16_len > 0 && !algo16[algo16_len - 1]) { + algo16_len--; + } + secp256k1_sha256_initialize_tagged(&sha, algo16, algo16_len); + } + + /* Hash (masked-)key||pk||msg using the tagged hash as per the spec */ + if (data != NULL) { + secp256k1_sha256_write(&sha, masked_key, 32); + } else { + secp256k1_sha256_write(&sha, key32, 32); + } + secp256k1_sha256_write(&sha, xonly_pk32, 32); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, nonce32); + return 1; +} + +const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340 = nonce_function_bip340; #endif diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 9c2a4b25..6ea4d880 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -10,8 +10,96 @@ #include "secp256k1_schnorrsig.h" +/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many + * bytes) changes the hash function + */ +void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) { + unsigned char nonces[2][32]; + CHECK(nonce_function_bip340(nonces[0], args[0], args[1], args[2], args[3], args[4]) == 1); + secp256k1_rand_flip(args[n_flip], n_bytes); + CHECK(nonce_function_bip340(nonces[1], args[0], args[1], args[2], args[3], args[4]) == 1); + CHECK(memcmp(nonces[0], nonces[1], 32) != 0); +} + +/* Tests for the equality of two sha256 structs. This function only produces a + * correct result if an integer multiple of 64 many bytes have been written + * into the hash functions. */ +void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2) { + /* Is buffer fully consumed? */ + CHECK((sha1->bytes & 0x3F) == 0); + + CHECK(sha1->bytes == sha2->bytes); + CHECK(memcmp(sha1->s, sha2->s, sizeof(sha1->s)) == 0); +} + +void run_nonce_function_bip340_tests(void) { + unsigned char tag[13] = "BIP0340/nonce"; + unsigned char aux_tag[11] = "BIP0340/aux"; + unsigned char algo16[16] = "BIP0340/nonce\0\0\0"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + unsigned char nonce[32]; + unsigned char msg[32]; + unsigned char key[32]; + unsigned char pk[32]; + unsigned char aux_rand[32]; + unsigned char *args[5]; + int i; + + /* Check that hash initialized by + * secp256k1_nonce_function_bip340_sha256_tagged has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, tag, sizeof(tag)); + secp256k1_nonce_function_bip340_sha256_tagged(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + /* Check that hash initialized by + * secp256k1_nonce_function_bip340_sha256_tagged_aux has the expected + * state. */ + secp256k1_sha256_initialize_tagged(&sha, aux_tag, sizeof(aux_tag)); + secp256k1_nonce_function_bip340_sha256_tagged_aux(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); + + secp256k1_rand256(msg); + secp256k1_rand256(key); + secp256k1_rand256(pk); + secp256k1_rand256(aux_rand); + + /* Check that a bitflip in an argument results in different nonces. */ + args[0] = msg; + args[1] = key; + args[2] = pk; + args[3] = algo16; + args[4] = aux_rand; + for (i = 0; i < count; i++) { + nonce_function_bip340_bitflip(args, 0, 32); + nonce_function_bip340_bitflip(args, 1, 32); + nonce_function_bip340_bitflip(args, 2, 32); + /* Flip algo16 special case "BIP0340/nonce" */ + nonce_function_bip340_bitflip(args, 3, 16); + /* Flip algo16 again */ + nonce_function_bip340_bitflip(args, 3, 16); + nonce_function_bip340_bitflip(args, 4, 32); + } + + /* NULL algo16 is disallowed */ + CHECK(nonce_function_bip340(nonce, msg, key, pk, NULL, NULL) == 0); + /* Empty algo16 is fine */ + memset(algo16, 0x00, 16); + CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1); + /* algo16 with terminating null bytes is fine */ + algo16[1] = 65; + CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1); + /* Other algo16 is fine */ + memset(algo16, 0xFF, 16); + CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1); + + /* NULL aux_rand argument is allowed. */ + CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1); +} + void run_schnorrsig_tests(void) { - /* TODO */ + run_nonce_function_bip340_tests(); } #endif diff --git a/src/testrand.h b/src/testrand.h index f1f9be07..bcbe15a6 100644 --- a/src/testrand.h +++ b/src/testrand.h @@ -35,4 +35,7 @@ static void secp256k1_rand256_test(unsigned char *b32); /** Generate pseudorandom bytes with long sequences of zero and one bits. */ static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len); +/** Flip a single random bit in a byte array */ +static void secp256k1_rand_flip(unsigned char *b, size_t len); + #endif /* SECP256K1_TESTRAND_H */ diff --git a/src/testrand_impl.h b/src/testrand_impl.h index 30a91e52..dfb658d9 100644 --- a/src/testrand_impl.h +++ b/src/testrand_impl.h @@ -107,4 +107,8 @@ static void secp256k1_rand256_test(unsigned char *b32) { secp256k1_rand_bytes_test(b32, 32); } +static void secp256k1_rand_flip(unsigned char *b, size_t len) { + b[secp256k1_rand_int(len)] ^= (1 << secp256k1_rand_int(8)); +} + #endif /* SECP256K1_TESTRAND_IMPL_H */ From 4e43520026f5bcd182d21f0759bac159ef47bb62 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 12 May 2020 16:17:59 +0000 Subject: [PATCH 12/19] schnorrsig: Add BIP-340 compatible signing and verification --- .gitignore | 2 +- .travis.yml | 10 +- contrib/travis.sh | 1 + include/secp256k1_schnorrsig.h | 46 ++ src/modules/schnorrsig/main_impl.h | 142 ++++++ src/modules/schnorrsig/tests_impl.h | 666 +++++++++++++++++++++++++++- 6 files changed, 860 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index cb4331aa..85fe89aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ bench_inv bench_ecdh bench_ecmult +bench_schnorrsig bench_sign bench_verify -bench_schnorr_verify bench_recover bench_internal tests diff --git a/.travis.yml b/.travis.yml index 81d1ba89..e1a88c40 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,19 +17,19 @@ compiler: - gcc env: global: - - WIDEMUL=auto BIGNUM=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 + - WIDEMUL=auto BIGNUM=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ECMULTGENPRECISION=auto ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no SCHNORRSIG=no EXPERIMENTAL=no CTIMETEST=yes BENCH=yes ITERS=2 matrix: - WIDEMUL=int64 RECOVERY=yes - - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes + - WIDEMUL=int64 ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes - WIDEMUL=int64 ENDOMORPHISM=yes - WIDEMUL=int128 - - WIDEMUL=int128 RECOVERY=yes + - WIDEMUL=int128 RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes - WIDEMUL=int128 ENDOMORPHISM=yes - - WIDEMUL=int128 ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes + - WIDEMUL=int128 ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes SCHNORRSIG=yes - WIDEMUL=int128 ASM=x86_64 - WIDEMUL=int128 ENDOMORPHISM=yes ASM=x86_64 - BIGNUM=no - - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes + - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes SCHNORRSIG=yes - BIGNUM=no STATICPRECOMPUTATION=no - BUILD=distcheck CTIMETEST= BENCH= - CPPFLAGS=-DDETERMINISTIC diff --git a/contrib/travis.sh b/contrib/travis.sh index 2023d0b5..b0b55b44 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -17,6 +17,7 @@ fi --with-test-override-wide-multiply="$WIDEMUL" --with-bignum="$BIGNUM" --with-asm="$ASM" \ --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ + --enable-module-schnorrsig="$SCHNORRSIG" \ --host="$HOST" $EXTRAFLAGS if [ -n "$BUILD" ] diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h index 67f198fd..0150cd33 100644 --- a/include/secp256k1_schnorrsig.h +++ b/include/secp256k1_schnorrsig.h @@ -58,6 +58,52 @@ typedef int (*secp256k1_nonce_function_hardened)( */ SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340; +/** Create a Schnorr signature. + * + * Does _not_ strictly follow BIP-340 because it does not verify the resulting + * signature. Instead, you can manually use secp256k1_schnorrsig_verify and + * abort if it fails. + * + * Otherwise BIP-340 compliant if the noncefp argument is NULL or + * secp256k1_nonce_function_bip340 and the ndata argument is 32-byte auxiliary + * randomness. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig64: pointer to a 64-byte array to store the serialized signature (cannot be NULL) + * In: msg32: the 32-byte message being signed (cannot be NULL) + * keypair: pointer to an initialized keypair (cannot be NULL) + * 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. + */ +SECP256K1_API int secp256k1_schnorrsig_sign( + const secp256k1_context* ctx, + unsigned char *sig64, + const unsigned char *msg32, + const secp256k1_keypair *keypair, + secp256k1_nonce_function_hardened noncefp, + void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verify a Schnorr signature. + * + * Returns: 1: correct signature + * 0: incorrect signature + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig64: pointer to the 64-byte signature to verify (cannot be NULL) + * msg32: the 32-byte message being verified (cannot be NULL) + * pubkey: pointer to an x-only public key to verify with (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( + const secp256k1_context* ctx, + const unsigned char *sig64, + const unsigned char *msg32, + const secp256k1_xonly_pubkey *pubkey +) 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 3f0c2bab..a0218f88 100644 --- a/src/modules/schnorrsig/main_impl.h +++ b/src/modules/schnorrsig/main_impl.h @@ -93,4 +93,146 @@ static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *ms const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340 = nonce_function_bip340; +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("BIP0340/challenge")||SHA256("BIP0340/challenge"). */ +static void secp256k1_schnorrsig_sha256_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + sha->s[0] = 0x9cecba11ul; + sha->s[1] = 0x23925381ul; + sha->s[2] = 0x11679112ul; + sha->s[3] = 0xd1627e0ful; + sha->s[4] = 0x97c87550ul; + sha->s[5] = 0x003cc765ul; + sha->s[6] = 0x90f61164ul; + sha->s[7] = 0x33e9b66aul; + sha->bytes = 64; +} + +int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) { + 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]; + int ret = 1; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(keypair != NULL); + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_bip340; + } + + ret &= secp256k1_keypair_load(ctx, &sk, &pk, keypair); + /* Because we are signing for a x-only pubkey, the secret key is negated + * before signing if the point corresponding to the secret key does not + * have an even Y. */ + if (secp256k1_fe_is_odd(&pk.y)) { + secp256k1_scalar_negate(&sk, &sk); + } + + secp256k1_scalar_get_b32(seckey, &sk); + secp256k1_fe_get_b32(pk_buf, &pk.x); + ret &= !!noncefp(buf, msg32, seckey, pk_buf, bip340_algo16, ndata); + secp256k1_scalar_set_b32(&k, buf, NULL); + ret &= !secp256k1_scalar_is_zero(&k); + secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret); + + 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); + } + secp256k1_fe_normalize_var(&r.x); + secp256k1_fe_get_b32(&sig64[0], &r.x); + + /* tagged hash(r.x, pk.x, msg32) */ + secp256k1_schnorrsig_sha256_tagged(&sha); + secp256k1_sha256_write(&sha, &sig64[0], 32); + secp256k1_sha256_write(&sha, pk_buf, sizeof(pk_buf)); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, buf); + + /* Set scalar e to the challenge hash modulo the curve order as per + * BIP340. */ + secp256k1_scalar_set_b32(&e, buf, NULL); + secp256k1_scalar_mul(&e, &e, &sk); + secp256k1_scalar_add(&e, &e, &k); + secp256k1_scalar_get_b32(&sig64[32], &e); + + memczero(sig64, 64, !ret); + secp256k1_scalar_clear(&k); + secp256k1_scalar_clear(&sk); + memset(seckey, 0, sizeof(seckey)); + + return ret; +} + +int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey) { + secp256k1_scalar s; + secp256k1_scalar e; + secp256k1_gej rj; + secp256k1_ge pk; + secp256k1_gej pkj; + secp256k1_fe rx; + secp256k1_ge r; + secp256k1_sha256 sha; + unsigned char buf[32]; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(sig64 != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pubkey != NULL); + + if (!secp256k1_fe_set_b32(&rx, &sig64[0])) { + return 0; + } + + secp256k1_scalar_set_b32(&s, &sig64[32], &overflow); + if (overflow) { + return 0; + } + + if (!secp256k1_xonly_pubkey_load(ctx, &pk, pubkey)) { + return 0; + } + + secp256k1_schnorrsig_sha256_tagged(&sha); + secp256k1_sha256_write(&sha, &sig64[0], 32); + secp256k1_fe_get_b32(buf, &pk.x); + secp256k1_sha256_write(&sha, buf, sizeof(buf)); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(&e, buf, NULL); + + /* Compute rj = s*G + (-e)*pkj */ + secp256k1_scalar_negate(&e, &e); + secp256k1_gej_set_ge(&pkj, &pk); + secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s); + + secp256k1_ge_set_gej_var(&r, &rj); + if (secp256k1_ge_is_infinity(&r)) { + return 0; + } + + secp256k1_fe_normalize_var(&r.y); + return !secp256k1_fe_is_odd(&r.y) && + secp256k1_fe_equal_var(&rx, &r.x); +} + #endif diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 6ea4d880..7a8ea9bb 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -5,7 +5,6 @@ **********************************************************************/ #ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_ - #define _SECP256K1_MODULE_SCHNORRSIG_TESTS_ #include "secp256k1_schnorrsig.h" @@ -98,8 +97,673 @@ void run_nonce_function_bip340_tests(void) { CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1); } +void test_schnorrsig_api(void) { + unsigned char sk1[32]; + unsigned char sk2[32]; + unsigned char sk3[32]; + unsigned char msg[32]; + secp256k1_keypair keypairs[3]; + secp256k1_keypair invalid_keypair = { 0 }; + secp256k1_xonly_pubkey pk[3]; + secp256k1_xonly_pubkey zero_pk; + unsigned char sig[64]; + + /** setup **/ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + int ecount; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + secp256k1_rand256(sk1); + secp256k1_rand256(sk2); + secp256k1_rand256(sk3); + secp256k1_rand256(msg); + 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); + CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[0], NULL, &keypairs[0]) == 1); + CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[1], NULL, &keypairs[1]) == 1); + CHECK(secp256k1_keypair_xonly_pub(ctx, &pk[2], NULL, &keypairs[2]) == 1); + memset(&zero_pk, 0, sizeof(zero_pk)); + + /** main test body **/ + ecount = 0; + CHECK(secp256k1_schnorrsig_sign(none, sig, msg, &keypairs[0], NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_sign(vrfy, sig, msg, &keypairs[0], NULL, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL, NULL) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_sign(sign, NULL, msg, &keypairs[0], NULL, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_sign(sign, sig, NULL, &keypairs[0], NULL, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, NULL, NULL, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &invalid_keypair, NULL, NULL) == 0); + CHECK(ecount == 6); + + ecount = 0; + CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL, NULL) == 1); + CHECK(secp256k1_schnorrsig_verify(none, sig, msg, &pk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify(sign, sig, msg, &pk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, &pk[0]) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, &pk[0]) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify(vrfy, sig, NULL, &pk[0]) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, &zero_pk) == 0); + CHECK(ecount == 6); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + +/* Checks that hash initialized by secp256k1_schnorrsig_sha256_tagged has the + * expected state. */ +void test_schnorrsig_sha256_tagged(void) { + char tag[17] = "BIP0340/challenge"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_optimized; + + secp256k1_sha256_initialize_tagged(&sha, (unsigned char *) tag, sizeof(tag)); + secp256k1_schnorrsig_sha256_tagged(&sha_optimized); + test_sha256_eq(&sha, &sha_optimized); +} + +/* Helper function for schnorrsig_bip_vectors + * Signs the message and checks that it's the same as expected_sig. */ +void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, unsigned char *aux_rand, const unsigned char *msg, const unsigned char *expected_sig) { + unsigned char sig[64]; + secp256k1_keypair keypair; + secp256k1_xonly_pubkey pk, pk_expected; + + CHECK(secp256k1_keypair_create(ctx, &keypair, sk)); + CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, aux_rand)); + CHECK(memcmp(sig, expected_sig, 64) == 0); + + CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk_expected, pk_serialized)); + CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); + CHECK(memcmp(&pk, &pk_expected, sizeof(pk)) == 0); + CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, &pk)); +} + +/* 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) { + secp256k1_xonly_pubkey pk; + + CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk, pk_serialized)); + CHECK(expected == secp256k1_schnorrsig_verify(ctx, sig, msg32, &pk)); +} + +/* 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) { + { + /* Test vector 0 */ + const unsigned char sk[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 + }; + const unsigned char pk[32] = { + 0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, + 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, + 0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0, + 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9 + }; + unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char msg[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char sig[64] = { + 0xE9, 0x07, 0x83, 0x1F, 0x80, 0x84, 0x8D, 0x10, + 0x69, 0xA5, 0x37, 0x1B, 0x40, 0x24, 0x10, 0x36, + 0x4B, 0xDF, 0x1C, 0x5F, 0x83, 0x07, 0xB0, 0x08, + 0x4C, 0x55, 0xF1, 0xCE, 0x2D, 0xCA, 0x82, 0x15, + 0x25, 0xF6, 0x6A, 0x4A, 0x85, 0xEA, 0x8B, 0x71, + 0xE4, 0x82, 0xA7, 0x4F, 0x38, 0x2D, 0x2C, 0xE5, + 0xEB, 0xEE, 0xE8, 0xFD, 0xB2, 0x17, 0x2F, 0x47, + 0x7D, 0xF4, 0x90, 0x0D, 0x31, 0x05, 0x36, 0xC0 + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + } + { + /* Test vector 1 */ + const unsigned char sk[32] = { + 0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, + 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, + 0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56, + 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF + }; + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + unsigned char aux_rand[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x68, 0x96, 0xBD, 0x60, 0xEE, 0xAE, 0x29, 0x6D, + 0xB4, 0x8A, 0x22, 0x9F, 0xF7, 0x1D, 0xFE, 0x07, + 0x1B, 0xDE, 0x41, 0x3E, 0x6D, 0x43, 0xF9, 0x17, + 0xDC, 0x8D, 0xCF, 0x8C, 0x78, 0xDE, 0x33, 0x41, + 0x89, 0x06, 0xD1, 0x1A, 0xC9, 0x76, 0xAB, 0xCC, + 0xB2, 0x0B, 0x09, 0x12, 0x92, 0xBF, 0xF4, 0xEA, + 0x89, 0x7E, 0xFC, 0xB6, 0x39, 0xEA, 0x87, 0x1C, + 0xFA, 0x95, 0xF6, 0xDE, 0x33, 0x9E, 0x4B, 0x0A + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + } + { + /* Test vector 2 */ + const unsigned char sk[32] = { + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x14, 0xE5, 0xC9 + }; + const unsigned char pk[32] = { + 0xDD, 0x30, 0x8A, 0xFE, 0xC5, 0x77, 0x7E, 0x13, + 0x12, 0x1F, 0xA7, 0x2B, 0x9C, 0xC1, 0xB7, 0xCC, + 0x01, 0x39, 0x71, 0x53, 0x09, 0xB0, 0x86, 0xC9, + 0x60, 0xE1, 0x8F, 0xD9, 0x69, 0x77, 0x4E, 0xB8 + }; + unsigned char aux_rand[32] = { + 0xC8, 0x7A, 0xA5, 0x38, 0x24, 0xB4, 0xD7, 0xAE, + 0x2E, 0xB0, 0x35, 0xA2, 0xB5, 0xBB, 0xBC, 0xCC, + 0x08, 0x0E, 0x76, 0xCD, 0xC6, 0xD1, 0x69, 0x2C, + 0x4B, 0x0B, 0x62, 0xD7, 0x98, 0xE6, 0xD9, 0x06 + }; + const unsigned char msg[32] = { + 0x7E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, + 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, + 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, + 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C + }; + const unsigned char sig[64] = { + 0x58, 0x31, 0xAA, 0xEE, 0xD7, 0xB4, 0x4B, 0xB7, + 0x4E, 0x5E, 0xAB, 0x94, 0xBA, 0x9D, 0x42, 0x94, + 0xC4, 0x9B, 0xCF, 0x2A, 0x60, 0x72, 0x8D, 0x8B, + 0x4C, 0x20, 0x0F, 0x50, 0xDD, 0x31, 0x3C, 0x1B, + 0xAB, 0x74, 0x58, 0x79, 0xA5, 0xAD, 0x95, 0x4A, + 0x72, 0xC4, 0x5A, 0x91, 0xC3, 0xA5, 0x1D, 0x3C, + 0x7A, 0xDE, 0xA9, 0x8D, 0x82, 0xF8, 0x48, 0x1E, + 0x0E, 0x1E, 0x03, 0x67, 0x4A, 0x6F, 0x3F, 0xB7 + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + } + { + /* Test vector 3 */ + const unsigned char sk[32] = { + 0x0B, 0x43, 0x2B, 0x26, 0x77, 0x93, 0x73, 0x81, + 0xAE, 0xF0, 0x5B, 0xB0, 0x2A, 0x66, 0xEC, 0xD0, + 0x12, 0x77, 0x30, 0x62, 0xCF, 0x3F, 0xA2, 0x54, + 0x9E, 0x44, 0xF5, 0x8E, 0xD2, 0x40, 0x17, 0x10 + }; + const unsigned char pk[32] = { + 0x25, 0xD1, 0xDF, 0xF9, 0x51, 0x05, 0xF5, 0x25, + 0x3C, 0x40, 0x22, 0xF6, 0x28, 0xA9, 0x96, 0xAD, + 0x3A, 0x0D, 0x95, 0xFB, 0xF2, 0x1D, 0x46, 0x8A, + 0x1B, 0x33, 0xF8, 0xC1, 0x60, 0xD8, 0xF5, 0x17 + }; + unsigned char aux_rand[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + const unsigned char msg[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + const unsigned char sig[64] = { + 0x7E, 0xB0, 0x50, 0x97, 0x57, 0xE2, 0x46, 0xF1, + 0x94, 0x49, 0x88, 0x56, 0x51, 0x61, 0x1C, 0xB9, + 0x65, 0xEC, 0xC1, 0xA1, 0x87, 0xDD, 0x51, 0xB6, + 0x4F, 0xDA, 0x1E, 0xDC, 0x96, 0x37, 0xD5, 0xEC, + 0x97, 0x58, 0x2B, 0x9C, 0xB1, 0x3D, 0xB3, 0x93, + 0x37, 0x05, 0xB3, 0x2B, 0xA9, 0x82, 0xAF, 0x5A, + 0xF2, 0x5F, 0xD7, 0x88, 0x81, 0xEB, 0xB3, 0x27, + 0x71, 0xFC, 0x59, 0x22, 0xEF, 0xC6, 0x6E, 0xA3 + }; + test_schnorrsig_bip_vectors_check_signing(sk, pk, aux_rand, msg, sig); + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + } + { + /* Test vector 4 */ + const unsigned char pk[32] = { + 0xD6, 0x9C, 0x35, 0x09, 0xBB, 0x99, 0xE4, 0x12, + 0xE6, 0x8B, 0x0F, 0xE8, 0x54, 0x4E, 0x72, 0x83, + 0x7D, 0xFA, 0x30, 0x74, 0x6D, 0x8B, 0xE2, 0xAA, + 0x65, 0x97, 0x5F, 0x29, 0xD2, 0x2D, 0xC7, 0xB9 + }; + const unsigned char msg[32] = { + 0x4D, 0xF3, 0xC3, 0xF6, 0x8F, 0xCC, 0x83, 0xB2, + 0x7E, 0x9D, 0x42, 0xC9, 0x04, 0x31, 0xA7, 0x24, + 0x99, 0xF1, 0x78, 0x75, 0xC8, 0x1A, 0x59, 0x9B, + 0x56, 0x6C, 0x98, 0x89, 0xB9, 0x69, 0x67, 0x03 + }; + const unsigned char sig[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3B, 0x78, 0xCE, 0x56, 0x3F, + 0x89, 0xA0, 0xED, 0x94, 0x14, 0xF5, 0xAA, 0x28, + 0xAD, 0x0D, 0x96, 0xD6, 0x79, 0x5F, 0x9C, 0x63, + 0x76, 0xAF, 0xB1, 0x54, 0x8A, 0xF6, 0x03, 0xB3, + 0xEB, 0x45, 0xC9, 0xF8, 0x20, 0x7D, 0xEE, 0x10, + 0x60, 0xCB, 0x71, 0xC0, 0x4E, 0x80, 0xF5, 0x93, + 0x06, 0x0B, 0x07, 0xD2, 0x83, 0x08, 0xD7, 0xF4 + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 1); + } + { + /* Test vector 5 */ + const unsigned char pk[32] = { + 0xEE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, 0x50, + 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, 0x21, + 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, 0x87, + 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, 0x34 + }; + secp256k1_xonly_pubkey pk_parsed; + /* No need to check the signature of the test vector as parsing the pubkey already fails */ + CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk)); + } + { + /* Test vector 6 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0xFF, 0xF9, 0x7B, 0xD5, 0x75, 0x5E, 0xEE, 0xA4, + 0x20, 0x45, 0x3A, 0x14, 0x35, 0x52, 0x35, 0xD3, + 0x82, 0xF6, 0x47, 0x2F, 0x85, 0x68, 0xA1, 0x8B, + 0x2F, 0x05, 0x7A, 0x14, 0x60, 0x29, 0x75, 0x56, + 0x3C, 0xC2, 0x79, 0x44, 0x64, 0x0A, 0xC6, 0x07, + 0xCD, 0x10, 0x7A, 0xE1, 0x09, 0x23, 0xD9, 0xEF, + 0x7A, 0x73, 0xC6, 0x43, 0xE1, 0x66, 0xBE, 0x5E, + 0xBE, 0xAF, 0xA3, 0x4B, 0x1A, 0xC5, 0x53, 0xE2 + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + } + { + /* Test vector 7 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x1F, 0xA6, 0x2E, 0x33, 0x1E, 0xDB, 0xC2, 0x1C, + 0x39, 0x47, 0x92, 0xD2, 0xAB, 0x11, 0x00, 0xA7, + 0xB4, 0x32, 0xB0, 0x13, 0xDF, 0x3F, 0x6F, 0xF4, + 0xF9, 0x9F, 0xCB, 0x33, 0xE0, 0xE1, 0x51, 0x5F, + 0x28, 0x89, 0x0B, 0x3E, 0xDB, 0x6E, 0x71, 0x89, + 0xB6, 0x30, 0x44, 0x8B, 0x51, 0x5C, 0xE4, 0xF8, + 0x62, 0x2A, 0x95, 0x4C, 0xFE, 0x54, 0x57, 0x35, + 0xAA, 0xEA, 0x51, 0x34, 0xFC, 0xCD, 0xB2, 0xBD + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + } + { + /* Test vector 8 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x6C, 0xFF, 0x5C, 0x3B, 0xA8, 0x6C, 0x69, 0xEA, + 0x4B, 0x73, 0x76, 0xF3, 0x1A, 0x9B, 0xCB, 0x4F, + 0x74, 0xC1, 0x97, 0x60, 0x89, 0xB2, 0xD9, 0x96, + 0x3D, 0xA2, 0xE5, 0x54, 0x3E, 0x17, 0x77, 0x69, + 0x96, 0x17, 0x64, 0xB3, 0xAA, 0x9B, 0x2F, 0xFC, + 0xB6, 0xEF, 0x94, 0x7B, 0x68, 0x87, 0xA2, 0x26, + 0xE8, 0xD7, 0xC9, 0x3E, 0x00, 0xC5, 0xED, 0x0C, + 0x18, 0x34, 0xFF, 0x0D, 0x0C, 0x2E, 0x6D, 0xA6 + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + } + { + /* Test vector 9 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x3D, 0xDA, 0x83, 0x28, 0xAF, 0x9C, 0x23, + 0xA9, 0x4C, 0x1F, 0xEE, 0xCF, 0xD1, 0x23, 0xBA, + 0x4F, 0xB7, 0x34, 0x76, 0xF0, 0xD5, 0x94, 0xDC, + 0xB6, 0x5C, 0x64, 0x25, 0xBD, 0x18, 0x60, 0x51 + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + } + { + /* Test vector 10 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x76, 0x15, 0xFB, 0xAF, 0x5A, 0xE2, 0x88, 0x64, + 0x01, 0x3C, 0x09, 0x97, 0x42, 0xDE, 0xAD, 0xB4, + 0xDB, 0xA8, 0x7F, 0x11, 0xAC, 0x67, 0x54, 0xF9, + 0x37, 0x80, 0xD5, 0xA1, 0x83, 0x7C, 0xF1, 0x97 + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + } + { + /* Test vector 11 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x4A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x69, 0xE8, 0x9B, 0x4C, 0x55, 0x64, 0xD0, 0x03, + 0x49, 0x10, 0x6B, 0x84, 0x97, 0x78, 0x5D, 0xD7, + 0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F, + 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + } + { + /* Test vector 12 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, + 0x69, 0xE8, 0x9B, 0x4C, 0x55, 0x64, 0xD0, 0x03, + 0x49, 0x10, 0x6B, 0x84, 0x97, 0x78, 0x5D, 0xD7, + 0xD1, 0xD7, 0x13, 0xA8, 0xAE, 0x82, 0xB3, 0x2F, + 0xA7, 0x9D, 0x5F, 0x7F, 0xC4, 0x07, 0xD3, 0x9B + }; + test_schnorrsig_bip_vectors_check_verify(pk, msg, sig, 0); + } + { + /* Test vector 13 */ + const unsigned char pk[32] = { + 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, + 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, + 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8, + 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59 + }; + const unsigned char msg[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig[64] = { + 0x6C, 0xFF, 0x5C, 0x3B, 0xA8, 0x6C, 0x69, 0xEA, + 0x4B, 0x73, 0x76, 0xF3, 0x1A, 0x9B, 0xCB, 0x4F, + 0x74, 0xC1, 0x97, 0x60, 0x89, 0xB2, 0xD9, 0x96, + 0x3D, 0xA2, 0xE5, 0x54, 0x3E, 0x17, 0x77, 0x69, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 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 vector 14 */ + const unsigned char pk[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x30 + }; + secp256k1_xonly_pubkey pk_parsed; + /* No need to check the signature of the test vector as parsing the pubkey already fails */ + CHECK(!secp256k1_xonly_pubkey_parse(ctx, &pk_parsed, pk)); + } +} + +/* Nonce function that returns constant 0 */ +static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) { + (void) msg32; + (void) key32; + (void) xonly_pk32; + (void) algo16; + (void) data; + (void) nonce32; + return 0; +} + +/* Nonce function that sets nonce to 0 */ +static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) { + (void) msg32; + (void) key32; + (void) xonly_pk32; + (void) algo16; + (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 *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) { + (void) msg32; + (void) key32; + (void) xonly_pk32; + (void) algo16; + (void) data; + + memset(nonce32, 0xFF, 32); + return 1; +} + +void test_schnorrsig_sign(void) { + unsigned char sk[32]; + secp256k1_keypair keypair; + const unsigned char msg[32] = "this is a msg for a schnorrsig.."; + unsigned char sig[64]; + unsigned char zeros64[64] = { 0 }; + + secp256k1_rand256(sk); + CHECK(secp256k1_keypair_create(ctx, &keypair, sk)); + CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL) == 1); + + /* Test different nonce functions */ + memset(sig, 1, sizeof(sig)); + CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_failing, NULL) == 0); + CHECK(memcmp(sig, zeros64, sizeof(sig)) == 0); + memset(&sig, 1, sizeof(sig)); + CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_0, NULL) == 0); + CHECK(memcmp(sig, zeros64, sizeof(sig)) == 0); + CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_overflowing, NULL) == 1); + CHECK(memcmp(sig, zeros64, sizeof(sig)) != 0); +} + +#define N_SIGS 3 +/* 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) { + unsigned char sk[32]; + unsigned char msg[N_SIGS][32]; + unsigned char sig[N_SIGS][64]; + size_t i; + secp256k1_keypair keypair; + secp256k1_xonly_pubkey pk; + secp256k1_scalar s; + + secp256k1_rand256(sk); + CHECK(secp256k1_keypair_create(ctx, &keypair, sk)); + CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair)); + + 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)); + } + + { + /* Flip a few bits in the signature and in the message and check that + * verify and verify_batch (TODO) fail */ + size_t sig_idx = secp256k1_rand_int(N_SIGS); + 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)); + 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)); + 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)); + 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)); + } + + /* Test overflowing s */ + CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL, NULL)); + CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk)); + memset(&sig[0][32], 0xFF, 32); + CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk)); + + /* Test negative s */ + CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL, NULL)); + CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk)); + secp256k1_scalar_set_b32(&s, &sig[0][32], NULL); + secp256k1_scalar_negate(&s, &s); + secp256k1_scalar_get_b32(&sig[0][32], &s); + CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk)); +} +#undef N_SIGS + void run_schnorrsig_tests(void) { + int i; run_nonce_function_bip340_tests(); + + test_schnorrsig_api(); + test_schnorrsig_sha256_tagged(); + test_schnorrsig_bip_vectors(); + for (i = 0; i < count; i++) { + test_schnorrsig_sign(); + test_schnorrsig_sign_verify(); + } } #endif From 8dfd53ee3fa059562483d1867815f78b9e00d947 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Tue, 12 May 2020 21:24:38 +0000 Subject: [PATCH 13/19] schnorrsig: Add benchmark for sign and verify --- src/bench_schnorrsig.c | 102 +++++++++++++++++++++ src/modules/schnorrsig/Makefile.am.include | 7 +- 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 src/bench_schnorrsig.c diff --git a/src/bench_schnorrsig.c b/src/bench_schnorrsig.c new file mode 100644 index 00000000..315f5af2 --- /dev/null +++ b/src/bench_schnorrsig.c @@ -0,0 +1,102 @@ +/********************************************************************** + * Copyright (c) 2018-2020 Andrew Poelstra, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + + +#include "include/secp256k1.h" +#include "include/secp256k1_schnorrsig.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context *ctx; + int n; + + const secp256k1_keypair **keypairs; + const unsigned char **pk; + const unsigned char **sigs; + const unsigned char **msgs; +} bench_schnorrsig_data; + +void bench_schnorrsig_sign(void* arg, int iters) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + int i; + unsigned char msg[32] = "benchmarkexamplemessagetemplate"; + unsigned char sig[64]; + + for (i = 0; i < iters; i++) { + msg[0] = i; + msg[1] = i >> 8; + CHECK(secp256k1_schnorrsig_sign(data->ctx, sig, msg, data->keypairs[i], NULL, NULL)); + } +} + +void bench_schnorrsig_verify(void* arg, int iters) { + bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; + int i; + + for (i = 0; i < iters; i++) { + secp256k1_xonly_pubkey pk; + CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pk[i]) == 1); + CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk)); + } +} + +int main(void) { + int i; + bench_schnorrsig_data data; + int iters = get_iters(10000); + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); + data.keypairs = (const secp256k1_keypair **)malloc(iters * sizeof(secp256k1_keypair *)); + data.pk = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); + data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); + data.sigs = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); + + for (i = 0; i < iters; i++) { + unsigned char sk[32]; + unsigned char *msg = (unsigned char *)malloc(32); + unsigned char *sig = (unsigned char *)malloc(64); + secp256k1_keypair *keypair = (secp256k1_keypair *)malloc(sizeof(*keypair)); + unsigned char *pk_char = (unsigned char *)malloc(32); + secp256k1_xonly_pubkey pk; + msg[0] = sk[0] = i; + msg[1] = sk[1] = i >> 8; + msg[2] = sk[2] = i >> 16; + msg[3] = sk[3] = i >> 24; + memset(&msg[4], 'm', 28); + memset(&sk[4], 's', 28); + + data.keypairs[i] = keypair; + data.pk[i] = pk_char; + data.msgs[i] = msg; + data.sigs[i] = sig; + + CHECK(secp256k1_keypair_create(data.ctx, keypair, sk)); + CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, msg, keypair, NULL, NULL)); + CHECK(secp256k1_keypair_xonly_pub(data.ctx, &pk, NULL, keypair)); + CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, pk_char, &pk) == 1); + } + + 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 = 0; i < iters; i++) { + free((void *)data.keypairs[i]); + free((void *)data.pk[i]); + free((void *)data.msgs[i]); + free((void *)data.sigs[i]); + } + free(data.keypairs); + free(data.pk); + free(data.msgs); + free(data.sigs); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/modules/schnorrsig/Makefile.am.include b/src/modules/schnorrsig/Makefile.am.include index 0efafa8a..a82bafe4 100644 --- a/src/modules/schnorrsig/Makefile.am.include +++ b/src/modules/schnorrsig/Makefile.am.include @@ -1,3 +1,8 @@ include_HEADERS += include/secp256k1_schnorrsig.h noinst_HEADERS += src/modules/schnorrsig/main_impl.h -noinst_HEADERS += src/modules/schnorrsig/tests_impl.h \ No newline at end of file +noinst_HEADERS += src/modules/schnorrsig/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_schnorrsig +bench_schnorrsig_SOURCES = src/bench_schnorrsig.c +bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +endif From 16ffa9d97cef93f49544b016339c107882f9a1c3 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 14 Nov 2019 13:34:51 +0000 Subject: [PATCH 14/19] schnorrsig: Add taproot test case --- src/modules/schnorrsig/tests_impl.h | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h index 7a8ea9bb..88d8f564 100644 --- a/src/modules/schnorrsig/tests_impl.h +++ b/src/modules/schnorrsig/tests_impl.h @@ -753,6 +753,42 @@ void test_schnorrsig_sign_verify(void) { } #undef N_SIGS +void test_schnorrsig_taproot(void) { + unsigned char sk[32]; + secp256k1_keypair keypair; + secp256k1_xonly_pubkey internal_pk; + unsigned char internal_pk_bytes[32]; + secp256k1_xonly_pubkey output_pk; + unsigned char output_pk_bytes[32]; + unsigned char tweak[32]; + int pk_parity; + unsigned char msg[32]; + unsigned char sig[64]; + + /* Create output key */ + secp256k1_rand256(sk); + CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1); + CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1); + /* In actual taproot the tweak would be hash of internal_pk */ + CHECK(secp256k1_xonly_pubkey_serialize(ctx, tweak, &internal_pk) == 1); + CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1); + CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1); + CHECK(secp256k1_xonly_pubkey_serialize(ctx, output_pk_bytes, &output_pk) == 1); + + /* Key spend */ + secp256k1_rand256(msg); + CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL) == 1); + /* Verify key spend */ + CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk, output_pk_bytes) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, &output_pk) == 1); + + /* Script spend */ + CHECK(secp256k1_xonly_pubkey_serialize(ctx, internal_pk_bytes, &internal_pk) == 1); + /* Verify script spend */ + CHECK(secp256k1_xonly_pubkey_parse(ctx, &internal_pk, internal_pk_bytes) == 1); + CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, output_pk_bytes, pk_parity, &internal_pk, tweak) == 1); +} + void run_schnorrsig_tests(void) { int i; run_nonce_function_bip340_tests(); @@ -764,6 +800,7 @@ void run_schnorrsig_tests(void) { test_schnorrsig_sign(); test_schnorrsig_sign_verify(); } + test_schnorrsig_taproot(); } #endif From f431b3f28ac95a3645ad5a6dc96b878fa30a1de3 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 12 Mar 2020 20:05:07 +0000 Subject: [PATCH 15/19] valgrind_ctime_test: Add schnorrsig_sign --- src/valgrind_ctime_test.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/valgrind_ctime_test.c b/src/valgrind_ctime_test.c index 3d705633..e676a832 100644 --- a/src/valgrind_ctime_test.c +++ b/src/valgrind_ctime_test.c @@ -21,6 +21,10 @@ # include "include/secp256k1_extrakeys.h" #endif +#if ENABLE_MODULE_SCHNORRSIG +#include "include/secp256k1_schnorrsig.h" +#endif + int main(void) { secp256k1_context* ctx; secp256k1_ecdsa_signature signature; @@ -138,6 +142,16 @@ int main(void) { CHECK(ret == 1); #endif +#if ENABLE_MODULE_SCHNORRSIG + VALGRIND_MAKE_MEM_UNDEFINED(key, 32); + ret = secp256k1_keypair_create(ctx, &keypair, key); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); + ret = secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL); + VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret)); + CHECK(ret == 1); +#endif + secp256k1_context_destroy(ctx); return 0; } From bac746c55e72abc1cc1ba1e8e6fabb2fea503cfe Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 14 Oct 2020 14:50:44 +0000 Subject: [PATCH 16/19] (temporarily) disable musig module --- Makefile.am | 6 +++--- configure.ac | 34 +++++++++++++++++----------------- contrib/travis.sh | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Makefile.am b/Makefile.am index dcc8693f..27a16bb7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -155,9 +155,9 @@ if ENABLE_MODULE_SCHNORRSIG include src/modules/schnorrsig/Makefile.am.include endif -if ENABLE_MODULE_MUSIG -include src/modules/musig/Makefile.am.include -endif +#if ENABLE_MODULE_MUSIG +#include src/modules/musig/Makefile.am.include +#endif if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include diff --git a/configure.ac b/configure.ac index ddfc76bd..1107fb20 100644 --- a/configure.ac +++ b/configure.ac @@ -136,10 +136,10 @@ AC_ARG_ENABLE(module_schnorrsig, [enable_module_schnorrsig=$enableval], [enable_module_schnorrsig=no]) -AC_ARG_ENABLE(module_musig, - AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), - [enable_module_musig=$enableval], - [enable_module_musig=no]) +#AC_ARG_ENABLE(module_musig, +# AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), +# [enable_module_musig=$enableval], +# [enable_module_musig=no]) AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), @@ -468,9 +468,9 @@ if test x"$enable_module_schnorrsig" = x"yes"; then AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) fi -if test x"$enable_module_musig" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) -fi +#if test x"$enable_module_musig" = x"yes"; then +# AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) +#fi if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) @@ -514,15 +514,15 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist]) AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof]) AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) - AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) +# AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) AC_MSG_NOTICE([******]) - if test x"$enable_module_schnorrsig" != x"yes"; then - if test x"$enable_module_musig" = x"yes"; then - AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.]) - fi - fi +# if test x"$enable_module_schnorrsig" != x"yes"; then +# if test x"$enable_module_musig" = x"yes"; then +# AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.]) +# fi +# fi if test x"$enable_module_generator" != x"yes"; then if test x"$enable_module_rangeproof" = x"yes"; then @@ -545,9 +545,9 @@ else if test x"$enable_module_schnorrsig" = x"yes"; then AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) fi - if test x"$enable_module_musig" = x"yes"; then - AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) - fi +# if test x"$enable_module_musig" = x"yes"; then +# AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) +# fi if test x"$set_asm" = x"arm"; then AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) fi @@ -578,7 +578,7 @@ AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) -AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) +#AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) diff --git a/contrib/travis.sh b/contrib/travis.sh index fad6bc18..25124822 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -18,7 +18,7 @@ fi --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ - --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" \ + --enable-module-schnorrsig="$SCHNORRSIG" \ --host="$HOST" $EXTRAFLAGS if [ -n "$BUILD" ] From a11250330b24b3dffdf11d2de5d496397b4e4410 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 14 Oct 2020 14:53:21 +0000 Subject: [PATCH 17/19] (actually) remove schnorrsig module --- .gitignore | 1 - Makefile.am | 4 - configure.ac | 15 - contrib/travis.sh | 1 - include/secp256k1.h | 6 - include/secp256k1_schnorrsig.h | 129 ---- src/bench_schnorrsig.c | 129 ---- src/modules/schnorrsig/Makefile.am.include | 8 - src/modules/schnorrsig/main_impl.h | 338 ---------- src/modules/schnorrsig/tests_impl.h | 726 --------------------- src/secp256k1.c | 28 - src/tests.c | 9 - 12 files changed, 1394 deletions(-) delete mode 100644 include/secp256k1_schnorrsig.h delete mode 100644 src/bench_schnorrsig.c delete mode 100644 src/modules/schnorrsig/Makefile.am.include delete mode 100644 src/modules/schnorrsig/main_impl.h delete mode 100644 src/modules/schnorrsig/tests_impl.h diff --git a/.gitignore b/.gitignore index 085bd54d..b95250fc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ bench_ecdh bench_ecmult bench_generator bench_rangeproof -bench_schnorrsig bench_sign bench_verify bench_recover diff --git a/Makefile.am b/Makefile.am index 27a16bb7..99c01035 100644 --- a/Makefile.am +++ b/Makefile.am @@ -151,10 +151,6 @@ if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif -if ENABLE_MODULE_SCHNORRSIG -include src/modules/schnorrsig/Makefile.am.include -endif - #if ENABLE_MODULE_MUSIG #include src/modules/musig/Makefile.am.include #endif diff --git a/configure.ac b/configure.ac index 1107fb20..6121cc16 100644 --- a/configure.ac +++ b/configure.ac @@ -131,11 +131,6 @@ AC_ARG_ENABLE(module_ecdh, [enable_module_ecdh=$enableval], [enable_module_ecdh=no]) -AC_ARG_ENABLE(module_schnorrsig, - AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]), - [enable_module_schnorrsig=$enableval], - [enable_module_schnorrsig=no]) - #AC_ARG_ENABLE(module_musig, # AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), # [enable_module_musig=$enableval], @@ -464,10 +459,6 @@ if test x"$enable_module_ecdh" = x"yes"; then AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) fi -if test x"$enable_module_schnorrsig" = x"yes"; then - AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module]) -fi - #if test x"$enable_module_musig" = x"yes"; then # AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) #fi @@ -513,7 +504,6 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof]) AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist]) AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof]) - AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) # AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) AC_MSG_NOTICE([******]) @@ -542,9 +532,6 @@ else if test x"$enable_module_ecdh" = x"yes"; then AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) fi - if test x"$enable_module_schnorrsig" = x"yes"; then - AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.]) - fi # if test x"$enable_module_musig" = x"yes"; then # AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) # fi @@ -577,7 +564,6 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) -AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"]) #AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"]) @@ -604,7 +590,6 @@ echo " with benchmarks = $use_benchmark" echo " with coverage = $enable_coverage" echo " module ecdh = $enable_module_ecdh" echo " module recovery = $enable_module_recovery" -echo " module schnorrsig = $enable_module_schnorrsig" echo echo " asm = $set_asm" echo " bignum = $set_bignum" diff --git a/contrib/travis.sh b/contrib/travis.sh index 25124822..833536d9 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -18,7 +18,6 @@ fi --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ - --enable-module-schnorrsig="$SCHNORRSIG" \ --host="$HOST" $EXTRAFLAGS if [ -n "$BUILD" ] diff --git a/include/secp256k1.h b/include/secp256k1.h index 8413a401..2178c8e2 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -525,12 +525,6 @@ SECP256K1_API int secp256k1_ecdsa_signature_normalize( */ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; -/** An implementation of the nonce generation function as defined in BIP-schnorr. - * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of - * extra entropy. - */ -SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_bipschnorr; - /** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; diff --git a/include/secp256k1_schnorrsig.h b/include/secp256k1_schnorrsig.h deleted file mode 100644 index 4c0f263d..00000000 --- a/include/secp256k1_schnorrsig.h +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef SECP256K1_SCHNORRSIG_H -#define SECP256K1_SCHNORRSIG_H - -#include "secp256k1.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** This module implements a variant of Schnorr signatures compliant with - * BIP-schnorr - * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki). - */ - -/** Opaque data structure that holds a parsed Schnorr signature. - * - * The exact representation of data inside is implementation defined and not - * guaranteed to be portable between different platforms or versions. It is - * however guaranteed to be 64 bytes in size, and can be safely copied/moved. - * If you need to convert to a format suitable for storage, transmission, or - * comparison, use the `secp256k1_schnorrsig_serialize` and - * `secp256k1_schnorrsig_parse` functions. - */ -typedef struct { - unsigned char data[64]; -} secp256k1_schnorrsig; - -/** Serialize a Schnorr signature. - * - * Returns: 1 - * Args: ctx: a secp256k1 context object - * Out: out64: pointer to a 64-byte array to store the serialized signature - * In: sig: pointer to the signature - * - * See secp256k1_schnorrsig_parse for details about the encoding. - */ -SECP256K1_API int secp256k1_schnorrsig_serialize( - const secp256k1_context* ctx, - unsigned char *out64, - const secp256k1_schnorrsig* sig -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Parse a Schnorr signature. - * - * Returns: 1 when the signature could be parsed, 0 otherwise. - * Args: ctx: a secp256k1 context object - * Out: sig: pointer to a signature object - * In: in64: pointer to the 64-byte signature to be parsed - * - * The signature is serialized in the form R||s, where R is a 32-byte public - * key (x-coordinate only; the y-coordinate is considered to be the unique - * y-coordinate satisfying the curve equation that is a quadratic residue) - * and s is a 32-byte big-endian scalar. - * - * After the call, sig will always be initialized. If parsing failed or the - * encoded numbers are out of range, signature validation with it is - * guaranteed to fail for every message and public key. - */ -SECP256K1_API int secp256k1_schnorrsig_parse( - const secp256k1_context* ctx, - secp256k1_schnorrsig* sig, - const unsigned char *in64 -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); - -/** Create a Schnorr signature. - * - * Returns 1 on success, 0 on failure. - * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) - * Out: sig: pointer to the returned signature (cannot be NULL) - * nonce_is_negated: a pointer to an integer indicates if signing algorithm negated the - * 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 - * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) - */ -SECP256K1_API 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_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); - -/** Verify a Schnorr signature. - * - * Returns: 1: correct signature - * 0: incorrect or unparseable signature - * Args: ctx: a secp256k1 context object, initialized for verification. - * In: sig: the signature being verified (cannot be NULL) - * msg32: the 32-byte message hash being verified (cannot be NULL) - * pubkey: pointer to a public key to verify with (cannot be NULL) - */ -SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( - const secp256k1_context* ctx, - const secp256k1_schnorrsig *sig, - const unsigned char *msg32, - const secp256k1_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 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 smaller than - * 2^31 and smaller than half the maximum size_t value. 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 secp256k1_schnorrsig *const *sig, - const unsigned char *const *msg32, - const secp256k1_pubkey *const *pk, - size_t n_sigs -) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); - -#ifdef __cplusplus -} -#endif - -#endif /* SECP256K1_SCHNORRSIG_H */ diff --git a/src/bench_schnorrsig.c b/src/bench_schnorrsig.c deleted file mode 100644 index a22e3496..00000000 --- a/src/bench_schnorrsig.c +++ /dev/null @@ -1,129 +0,0 @@ -/********************************************************************** - * Copyright (c) 2018 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#include -#include - -#include "include/secp256k1.h" -#include "include/secp256k1_schnorrsig.h" -#include "util.h" -#include "bench.h" - -typedef struct { - secp256k1_context *ctx; - secp256k1_scratch_space *scratch; - int n; - const unsigned char **pk; - const secp256k1_schnorrsig **sigs; - const unsigned char **msgs; -} bench_schnorrsig_data; - -void bench_schnorrsig_sign(void* arg, int iters) { - bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; - int i; - unsigned char sk[32] = "benchmarkexample secrettemplate"; - unsigned char msg[32] = "benchmarkexamplemessagetemplate"; - secp256k1_schnorrsig sig; - - for (i = 0; i < iters; i++) { - msg[0] = i; - msg[1] = i >> 8; - sk[0] = i; - sk[1] = i >> 8; - CHECK(secp256k1_schnorrsig_sign(data->ctx, &sig, NULL, msg, sk, NULL, NULL)); - } -} - -void bench_schnorrsig_verify(void* arg, int iters) { - bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; - int i; - - for (i = 0; i < iters; i++) { - secp256k1_pubkey pk; - CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pk, data->pk[i], 33) == 1); - CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk)); - } -} - -void bench_schnorrsig_verify_n(void* arg, int iters) { - bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg; - int i, j; - const secp256k1_pubkey **pk = (const secp256k1_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_pubkey *pk_nonconst = (secp256k1_pubkey *)malloc(sizeof(*pk_nonconst)); - CHECK(secp256k1_ec_pubkey_parse(data->ctx, pk_nonconst, data->pk[i], 33) == 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; - int iters = get_iters(1000); - - data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); - data.scratch = secp256k1_scratch_space_create(data.ctx, 1024 * 1024 * 1024); - data.pk = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); - data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *)); - data.sigs = (const secp256k1_schnorrsig **)malloc(iters * sizeof(secp256k1_schnorrsig *)); - - for (i = 0; i < iters; i++) { - unsigned char sk[32]; - unsigned char *msg = (unsigned char *)malloc(32); - secp256k1_schnorrsig *sig = (secp256k1_schnorrsig *)malloc(sizeof(*sig)); - unsigned char *pk_char = (unsigned char *)malloc(33); - secp256k1_pubkey pk; - size_t pk_len = 33; - msg[0] = sk[0] = i; - msg[1] = sk[1] = i >> 8; - msg[2] = sk[2] = i >> 16; - msg[3] = sk[3] = i >> 24; - memset(&msg[4], 'm', 28); - memset(&sk[4], 's', 28); - - data.pk[i] = pk_char; - data.msgs[i] = msg; - data.sigs[i] = sig; - - CHECK(secp256k1_ec_pubkey_create(data.ctx, &pk, sk)); - CHECK(secp256k1_ec_pubkey_serialize(data.ctx, pk_char, &pk_len, &pk, SECP256K1_EC_COMPRESSED) == 1); - CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, NULL, msg, sk, NULL, NULL)); - } - - 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.pk[i]); - free((void *)data.msgs[i]); - free((void *)data.sigs[i]); - } - free(data.pk); - free(data.msgs); - free(data.sigs); - - secp256k1_scratch_space_destroy(data.ctx, data.scratch); - secp256k1_context_destroy(data.ctx); - return 0; -} diff --git a/src/modules/schnorrsig/Makefile.am.include b/src/modules/schnorrsig/Makefile.am.include deleted file mode 100644 index a82bafe4..00000000 --- a/src/modules/schnorrsig/Makefile.am.include +++ /dev/null @@ -1,8 +0,0 @@ -include_HEADERS += include/secp256k1_schnorrsig.h -noinst_HEADERS += src/modules/schnorrsig/main_impl.h -noinst_HEADERS += src/modules/schnorrsig/tests_impl.h -if USE_BENCHMARK -noinst_PROGRAMS += bench_schnorrsig -bench_schnorrsig_SOURCES = src/bench_schnorrsig.c -bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) -endif diff --git a/src/modules/schnorrsig/main_impl.h b/src/modules/schnorrsig/main_impl.h deleted file mode 100644 index b0310ab9..00000000 --- a/src/modules/schnorrsig/main_impl.h +++ /dev/null @@ -1,338 +0,0 @@ -/********************************************************************** - * Copyright (c) 2018 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_ -#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_ - -#include "include/secp256k1.h" -#include "include/secp256k1_schnorrsig.h" -#include "hash.h" - -int secp256k1_schnorrsig_serialize(const secp256k1_context* ctx, unsigned char *out64, const secp256k1_schnorrsig* sig) { - (void) ctx; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(out64 != NULL); - ARG_CHECK(sig != NULL); - memcpy(out64, sig->data, 64); - return 1; -} - -int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsig* sig, const unsigned char *in64) { - (void) ctx; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig != NULL); - ARG_CHECK(in64 != NULL); - memcpy(sig->data, in64, 64); - return 1; -} - -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; - secp256k1_scalar k; - secp256k1_gej pkj; - secp256k1_gej rj; - secp256k1_ge pk; - secp256k1_ge r; - secp256k1_sha256 sha; - int overflow; - unsigned char buf[33]; - size_t buflen = sizeof(buf); - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); - ARG_CHECK(sig != NULL); - ARG_CHECK(msg32 != NULL); - ARG_CHECK(seckey != NULL); - - if (noncefp == NULL) { - noncefp = secp256k1_nonce_function_bipschnorr; - } - secp256k1_scalar_set_b32(&x, seckey, &overflow); - /* Fail if the secret key is invalid. */ - if (overflow || secp256k1_scalar_is_zero(&x)) { - memset(sig, 0, sizeof(*sig)); - return 0; - } - - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pkj, &x); - secp256k1_ge_set_gej(&pk, &pkj); - - if (!noncefp(buf, msg32, seckey, NULL, (void*)ndata, 0)) { - return 0; - } - secp256k1_scalar_set_b32(&k, buf, NULL); - if (secp256k1_scalar_is_zero(&k)) { - return 0; - } - - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); - secp256k1_ge_set_gej(&r, &rj); - - if (nonce_is_negated != NULL) { - *nonce_is_negated = 0; - } - if (!secp256k1_fe_is_quad_var(&r.y)) { - secp256k1_scalar_negate(&k, &k); - if (nonce_is_negated != NULL) { - *nonce_is_negated = 1; - } - } - secp256k1_fe_normalize(&r.x); - secp256k1_fe_get_b32(&sig->data[0], &r.x); - - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, &sig->data[0], 32); - secp256k1_eckey_pubkey_serialize(&pk, buf, &buflen, 1); - secp256k1_sha256_write(&sha, buf, buflen); - secp256k1_sha256_write(&sha, msg32, 32); - secp256k1_sha256_finalize(&sha, buf); - - secp256k1_scalar_set_b32(&e, buf, NULL); - secp256k1_scalar_mul(&e, &e, &x); - secp256k1_scalar_add(&e, &e, &k); - - secp256k1_scalar_get_b32(&sig->data[32], &e); - secp256k1_scalar_clear(&k); - secp256k1_scalar_clear(&x); - - return 1; -} - -/* Helper function for verification and batch verification. - * Computes R = sG - eP. */ -static int secp256k1_schnorrsig_real_verify(const secp256k1_context* ctx, secp256k1_gej *rj, const secp256k1_scalar *s, const secp256k1_scalar *e, const secp256k1_pubkey *pk) { - secp256k1_scalar nege; - secp256k1_ge pkp; - secp256k1_gej pkj; - - secp256k1_scalar_negate(&nege, e); - - if (!secp256k1_pubkey_load(ctx, &pkp, pk)) { - return 0; - } - secp256k1_gej_set_ge(&pkj, &pkp); - - /* rj = s*G + (-e)*pkj */ - secp256k1_ecmult(&ctx->ecmult_ctx, rj, &pkj, &nege, s); - return 1; -} - -int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const secp256k1_schnorrsig *sig, const unsigned char *msg32, const secp256k1_pubkey *pk) { - secp256k1_scalar s; - secp256k1_scalar e; - secp256k1_gej rj; - secp256k1_fe rx; - secp256k1_sha256 sha; - unsigned char buf[33]; - size_t buflen = sizeof(buf); - int overflow; - - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); - ARG_CHECK(sig != NULL); - ARG_CHECK(msg32 != NULL); - ARG_CHECK(pk != NULL); - - if (!secp256k1_fe_set_b32(&rx, &sig->data[0])) { - return 0; - } - - secp256k1_scalar_set_b32(&s, &sig->data[32], &overflow); - if (overflow) { - return 0; - } - - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, &sig->data[0], 32); - secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, pk, SECP256K1_EC_COMPRESSED); - secp256k1_sha256_write(&sha, buf, buflen); - secp256k1_sha256_write(&sha, msg32, 32); - secp256k1_sha256_finalize(&sha, buf); - secp256k1_scalar_set_b32(&e, buf, NULL); - - if (!secp256k1_schnorrsig_real_verify(ctx, &rj, &s, &e, pk) - || !secp256k1_gej_has_quad_y_var(&rj) /* fails if rj is infinity */ - || !secp256k1_gej_eq_x_var(&rx, &rj)) { - return 0; - } - - return 1; -} - -/* 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 secp256k1_schnorrsig *const *sig; - const unsigned char *const *msg32; - const secp256k1_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]->data[0])) { - return 0; - } - if (!secp256k1_ge_set_xquad(pt, &rx)) { - return 0; - } - /* eP */ - } else { - unsigned char buf[33]; - size_t buflen = sizeof(buf); - secp256k1_sha256 sha; - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, &ecmult_context->sig[idx / 2]->data[0], 32); - secp256k1_ec_pubkey_serialize(ecmult_context->ctx, buf, &buflen, ecmult_context->pk[idx / 2], SECP256K1_EC_COMPRESSED); - secp256k1_sha256_write(&sha, buf, buflen); - 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]); - - if (!secp256k1_pubkey_load(ecmult_context->ctx, pt, ecmult_context->pk[idx / 2])) { - return 0; - } - } - 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 secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_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]->data, 64); - secp256k1_sha256_write(sha, msg32[i], 32); - secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, pk[i], SECP256K1_EC_COMPRESSED); - 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 secp256k1_schnorrsig *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]->data[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 secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_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 deleted file mode 100644 index 670b2d1a..00000000 --- a/src/modules/schnorrsig/tests_impl.h +++ /dev/null @@ -1,726 +0,0 @@ -/********************************************************************** - * Copyright (c) 2018 Andrew Poelstra * - * Distributed under the MIT software license, see the accompanying * - * file COPYING or http://www.opensource.org/licenses/mit-license.php.* - **********************************************************************/ - -#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_ -#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_ - -#include "secp256k1_schnorrsig.h" - -void test_schnorrsig_serialize(void) { - secp256k1_schnorrsig sig; - unsigned char in[64]; - unsigned char out[64]; - - memset(in, 0x12, 64); - CHECK(secp256k1_schnorrsig_parse(ctx, &sig, in)); - CHECK(secp256k1_schnorrsig_serialize(ctx, out, &sig)); - CHECK(memcmp(in, out, 64) == 0); -} - -void test_schnorrsig_api(secp256k1_scratch_space *scratch) { - unsigned char sk1[32]; - unsigned char sk2[32]; - unsigned char sk3[32]; - unsigned char msg[32]; - unsigned char sig64[64]; - secp256k1_pubkey pk[3]; - secp256k1_schnorrsig sig; - const secp256k1_schnorrsig *sigptr = &sig; - const unsigned char *msgptr = msg; - const secp256k1_pubkey *pkptr = &pk[0]; - int nonce_is_negated; - - /** setup **/ - secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); - secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); - secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); - secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); - int ecount; - - secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); - secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); - - secp256k1_rand256(sk1); - secp256k1_rand256(sk2); - secp256k1_rand256(sk3); - secp256k1_rand256(msg); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk1) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk2) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[2], sk3) == 1); - - /** main test body **/ - ecount = 0; - CHECK(secp256k1_schnorrsig_sign(none, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_sign(vrfy, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_sign(sign, NULL, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_sign(sign, &sig, NULL, msg, sk1, NULL, NULL) == 1); - CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, NULL, sk1, NULL, NULL) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, msg, NULL, NULL, NULL) == 0); - CHECK(ecount == 5); - - ecount = 0; - CHECK(secp256k1_schnorrsig_serialize(none, sig64, &sig) == 1); - CHECK(ecount == 0); - CHECK(secp256k1_schnorrsig_serialize(none, NULL, &sig) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_serialize(none, sig64, NULL) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_parse(none, &sig, sig64) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_parse(none, NULL, sig64) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_parse(none, &sig, NULL) == 0); - CHECK(ecount == 4); - - ecount = 0; - CHECK(secp256k1_schnorrsig_verify(none, &sig, msg, &pk[0]) == 0); - CHECK(ecount == 1); - CHECK(secp256k1_schnorrsig_verify(sign, &sig, msg, &pk[0]) == 0); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, &pk[0]) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, &pk[0]) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, NULL, &pk[0]) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, NULL) == 0); - CHECK(ecount == 5); - - 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, scratch, NULL, NULL, NULL, 0) == 1); - CHECK(ecount == 2); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, &msgptr, &pkptr, 1) == 0); - CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, NULL, &pkptr, 1) == 0); - CHECK(ecount == 4); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, NULL, 1) == 0); - CHECK(ecount == 5); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (size_t)1 << (sizeof(size_t)*8-1)) == 0); - CHECK(ecount == 6); - CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (uint32_t)1 << 31) == 0); - CHECK(ecount == 7); - - secp256k1_context_destroy(none); - secp256k1_context_destroy(sign); - secp256k1_context_destroy(vrfy); - secp256k1_context_destroy(both); -} - -/* Helper function for schnorrsig_bip_vectors - * Signs the message and checks that it's the same as expected_sig. */ -void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, const unsigned char *msg, const unsigned char *expected_sig, const int expected_nonce_is_negated) { - secp256k1_schnorrsig sig; - unsigned char serialized_sig[64]; - secp256k1_pubkey pk; - int nonce_is_negated; - - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, &nonce_is_negated, msg, sk, NULL, NULL)); - CHECK(nonce_is_negated == expected_nonce_is_negated); - CHECK(secp256k1_schnorrsig_serialize(ctx, serialized_sig, &sig)); - CHECK(memcmp(serialized_sig, expected_sig, 64) == 0); - - CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, pk_serialized, 33)); - CHECK(secp256k1_schnorrsig_verify(ctx, &sig, msg, &pk)); -} - -/* Helper function for schnorrsig_bip_vectors - * 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_serialized, int expected) { - const unsigned char *msg_arr[1]; - const secp256k1_schnorrsig *sig_arr[1]; - const secp256k1_pubkey *pk_arr[1]; - secp256k1_pubkey pk; - secp256k1_schnorrsig sig; - - CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, pk_serialized, 33)); - CHECK(secp256k1_schnorrsig_parse(ctx, &sig, sig_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-schnorr - * (https://github.com/sipa/bips/blob/7f6a73e53c8bbcf2d008ea0546f76433e22094a8/bip-schnorr/test-vectors.csv). - */ -void test_schnorrsig_bip_vectors(secp256k1_scratch_space *scratch) { - { - /* Test vector 1 */ - const unsigned char sk1[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 - }; - const unsigned char pk1[33] = { - 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, - 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, - 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, - 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, - 0x98 - }; - const unsigned char msg1[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - const unsigned char sig1[64] = { - 0x78, 0x7A, 0x84, 0x8E, 0x71, 0x04, 0x3D, 0x28, - 0x0C, 0x50, 0x47, 0x0E, 0x8E, 0x15, 0x32, 0xB2, - 0xDD, 0x5D, 0x20, 0xEE, 0x91, 0x2A, 0x45, 0xDB, - 0xDD, 0x2B, 0xD1, 0xDF, 0xBF, 0x18, 0x7E, 0xF6, - 0x70, 0x31, 0xA9, 0x88, 0x31, 0x85, 0x9D, 0xC3, - 0x4D, 0xFF, 0xEE, 0xDD, 0xA8, 0x68, 0x31, 0x84, - 0x2C, 0xCD, 0x00, 0x79, 0xE1, 0xF9, 0x2A, 0xF1, - 0x77, 0xF7, 0xF2, 0x2C, 0xC1, 0xDC, 0xED, 0x05 - }; - test_schnorrsig_bip_vectors_check_signing(sk1, pk1, msg1, sig1, 1); - test_schnorrsig_bip_vectors_check_verify(scratch, pk1, msg1, sig1, 1); - } - { - /* Test vector 2 */ - const unsigned char sk2[32] = { - 0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, - 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, - 0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56, - 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF - }; - const unsigned char pk2[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg2[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig2[64] = { - 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, - 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, - 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, - 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, - 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, - 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, - 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, - 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD - }; - test_schnorrsig_bip_vectors_check_signing(sk2, pk2, msg2, sig2, 0); - test_schnorrsig_bip_vectors_check_verify(scratch, pk2, msg2, sig2, 1); - } - { - /* Test vector 3 */ - const unsigned char sk3[32] = { - 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, - 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, - 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, - 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x14, 0xE5, 0xC7 - }; - const unsigned char pk3[33] = { - 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, - 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, - 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, - 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, - 0x4B - }; - const unsigned char msg3[32] = { - 0x5E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, - 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, - 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, - 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C - }; - const unsigned char sig3[64] = { - 0x00, 0xDA, 0x9B, 0x08, 0x17, 0x2A, 0x9B, 0x6F, - 0x04, 0x66, 0xA2, 0xDE, 0xFD, 0x81, 0x7F, 0x2D, - 0x7A, 0xB4, 0x37, 0xE0, 0xD2, 0x53, 0xCB, 0x53, - 0x95, 0xA9, 0x63, 0x86, 0x6B, 0x35, 0x74, 0xBE, - 0x00, 0x88, 0x03, 0x71, 0xD0, 0x17, 0x66, 0x93, - 0x5B, 0x92, 0xD2, 0xAB, 0x4C, 0xD5, 0xC8, 0xA2, - 0xA5, 0x83, 0x7E, 0xC5, 0x7F, 0xED, 0x76, 0x60, - 0x77, 0x3A, 0x05, 0xF0, 0xDE, 0x14, 0x23, 0x80 - }; - test_schnorrsig_bip_vectors_check_signing(sk3, pk3, msg3, sig3, 0); - test_schnorrsig_bip_vectors_check_verify(scratch, pk3, msg3, sig3, 1); - } - { - /* Test vector 4 */ - const unsigned char pk4[33] = { - 0x03, 0xDE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, - 0x50, 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, - 0x21, 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, - 0x87, 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, - 0x34 - }; - const unsigned char msg4[32] = { - 0x4D, 0xF3, 0xC3, 0xF6, 0x8F, 0xCC, 0x83, 0xB2, - 0x7E, 0x9D, 0x42, 0xC9, 0x04, 0x31, 0xA7, 0x24, - 0x99, 0xF1, 0x78, 0x75, 0xC8, 0x1A, 0x59, 0x9B, - 0x56, 0x6C, 0x98, 0x89, 0xB9, 0x69, 0x67, 0x03 - }; - const unsigned char sig4[64] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x3B, 0x78, 0xCE, 0x56, 0x3F, - 0x89, 0xA0, 0xED, 0x94, 0x14, 0xF5, 0xAA, 0x28, - 0xAD, 0x0D, 0x96, 0xD6, 0x79, 0x5F, 0x9C, 0x63, - 0x02, 0xA8, 0xDC, 0x32, 0xE6, 0x4E, 0x86, 0xA3, - 0x33, 0xF2, 0x0E, 0xF5, 0x6E, 0xAC, 0x9B, 0xA3, - 0x0B, 0x72, 0x46, 0xD6, 0xD2, 0x5E, 0x22, 0xAD, - 0xB8, 0xC6, 0xBE, 0x1A, 0xEB, 0x08, 0xD4, 0x9D - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk4, msg4, sig4, 1); - } - { - /* Test vector 5 */ - const unsigned char pk5[33] = { - 0x03, 0x1B, 0x84, 0xC5, 0x56, 0x7B, 0x12, 0x64, - 0x40, 0x99, 0x5D, 0x3E, 0xD5, 0xAA, 0xBA, 0x05, - 0x65, 0xD7, 0x1E, 0x18, 0x34, 0x60, 0x48, 0x19, - 0xFF, 0x9C, 0x17, 0xF5, 0xE9, 0xD5, 0xDD, 0x07, - 0x8F - }; - const unsigned char msg5[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - const unsigned char sig5[64] = { - 0x52, 0x81, 0x85, 0x79, 0xAC, 0xA5, 0x97, 0x67, - 0xE3, 0x29, 0x1D, 0x91, 0xB7, 0x6B, 0x63, 0x7B, - 0xEF, 0x06, 0x20, 0x83, 0x28, 0x49, 0x92, 0xF2, - 0xD9, 0x5F, 0x56, 0x4C, 0xA6, 0xCB, 0x4E, 0x35, - 0x30, 0xB1, 0xDA, 0x84, 0x9C, 0x8E, 0x83, 0x04, - 0xAD, 0xC0, 0xCF, 0xE8, 0x70, 0x66, 0x03, 0x34, - 0xB3, 0xCF, 0xC1, 0x8E, 0x82, 0x5E, 0xF1, 0xDB, - 0x34, 0xCF, 0xAE, 0x3D, 0xFC, 0x5D, 0x81, 0x87 - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk5, msg5, sig5, 1); - } - { - /* Test vector 6 */ - const unsigned char pk6[33] = { - 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, - 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, - 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, - 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, - 0x4B - }; - const unsigned char msg6[32] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF - }; - const unsigned char sig6[64] = { - 0x57, 0x0D, 0xD4, 0xCA, 0x83, 0xD4, 0xE6, 0x31, - 0x7B, 0x8E, 0xE6, 0xBA, 0xE8, 0x34, 0x67, 0xA1, - 0xBF, 0x41, 0x9D, 0x07, 0x67, 0x12, 0x2D, 0xE4, - 0x09, 0x39, 0x44, 0x14, 0xB0, 0x50, 0x80, 0xDC, - 0xE9, 0xEE, 0x5F, 0x23, 0x7C, 0xBD, 0x10, 0x8E, - 0xAB, 0xAE, 0x1E, 0x37, 0x75, 0x9A, 0xE4, 0x7F, - 0x8E, 0x42, 0x03, 0xDA, 0x35, 0x32, 0xEB, 0x28, - 0xDB, 0x86, 0x0F, 0x33, 0xD6, 0x2D, 0x49, 0xBD - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk6, msg6, sig6, 1); - } - { - /* Test vector 7 */ - const unsigned char pk7[33] = { - 0x03, 0xEE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, - 0x50, 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, - 0x21, 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, - 0x87, 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, - 0x34 - }; - secp256k1_pubkey pk7_parsed; - /* No need to check the signature of the test vector as parsing the pubkey already fails */ - CHECK(!secp256k1_ec_pubkey_parse(ctx, &pk7_parsed, pk7, 33)); - } - { - /* Test vector 8 */ - const unsigned char pk8[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg8[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig8[64] = { - 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, - 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, - 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, - 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, - 0xFA, 0x16, 0xAE, 0xE0, 0x66, 0x09, 0x28, 0x0A, - 0x19, 0xB6, 0x7A, 0x24, 0xE1, 0x97, 0x7E, 0x46, - 0x97, 0x71, 0x2B, 0x5F, 0xD2, 0x94, 0x39, 0x14, - 0xEC, 0xD5, 0xF7, 0x30, 0x90, 0x1B, 0x4A, 0xB7 - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk8, msg8, sig8, 0); - } - { - /* Test vector 9 */ - const unsigned char pk9[33] = { - 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, - 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, - 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, - 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, - 0x4B - }; - const unsigned char msg9[32] = { - 0x5E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, - 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, - 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, - 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C - }; - const unsigned char sig9[64] = { - 0x00, 0xDA, 0x9B, 0x08, 0x17, 0x2A, 0x9B, 0x6F, - 0x04, 0x66, 0xA2, 0xDE, 0xFD, 0x81, 0x7F, 0x2D, - 0x7A, 0xB4, 0x37, 0xE0, 0xD2, 0x53, 0xCB, 0x53, - 0x95, 0xA9, 0x63, 0x86, 0x6B, 0x35, 0x74, 0xBE, - 0xD0, 0x92, 0xF9, 0xD8, 0x60, 0xF1, 0x77, 0x6A, - 0x1F, 0x74, 0x12, 0xAD, 0x8A, 0x1E, 0xB5, 0x0D, - 0xAC, 0xCC, 0x22, 0x2B, 0xC8, 0xC0, 0xE2, 0x6B, - 0x20, 0x56, 0xDF, 0x2F, 0x27, 0x3E, 0xFD, 0xEC - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk9, msg9, sig9, 0); - } - { - /* Test vector 10 */ - const unsigned char pk10[33] = { - 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, - 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, - 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, - 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, - 0x98 - }; - const unsigned char msg10[32] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - const unsigned char sig10[64] = { - 0x78, 0x7A, 0x84, 0x8E, 0x71, 0x04, 0x3D, 0x28, - 0x0C, 0x50, 0x47, 0x0E, 0x8E, 0x15, 0x32, 0xB2, - 0xDD, 0x5D, 0x20, 0xEE, 0x91, 0x2A, 0x45, 0xDB, - 0xDD, 0x2B, 0xD1, 0xDF, 0xBF, 0x18, 0x7E, 0xF6, - 0x8F, 0xCE, 0x56, 0x77, 0xCE, 0x7A, 0x62, 0x3C, - 0xB2, 0x00, 0x11, 0x22, 0x57, 0x97, 0xCE, 0x7A, - 0x8D, 0xE1, 0xDC, 0x6C, 0xCD, 0x4F, 0x75, 0x4A, - 0x47, 0xDA, 0x6C, 0x60, 0x0E, 0x59, 0x54, 0x3C - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk10, msg10, sig10, 0); - } - { - /* Test vector 11 */ - const unsigned char pk11[33] = { - 0x03, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg11[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig11[64] = { - 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, - 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, - 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, - 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, - 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, - 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, - 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, - 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk11, msg11, sig11, 0); - } - { - /* Test vector 12 */ - const unsigned char pk12[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg12[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig12[64] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9E, 0x9D, 0x01, 0xAF, 0x98, 0x8B, 0x5C, 0xED, - 0xCE, 0x47, 0x22, 0x1B, 0xFA, 0x9B, 0x22, 0x27, - 0x21, 0xF3, 0xFA, 0x40, 0x89, 0x15, 0x44, 0x4A, - 0x4B, 0x48, 0x90, 0x21, 0xDB, 0x55, 0x77, 0x5F - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk12, msg12, sig12, 0); - } - { - /* Test vector 13 */ - const unsigned char pk13[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg13[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig13[64] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0xD3, 0x7D, 0xDF, 0x02, 0x54, 0x35, 0x18, 0x36, - 0xD8, 0x4B, 0x1B, 0xD6, 0xA7, 0x95, 0xFD, 0x5D, - 0x52, 0x30, 0x48, 0xF2, 0x98, 0xC4, 0x21, 0x4D, - 0x18, 0x7F, 0xE4, 0x89, 0x29, 0x47, 0xF7, 0x28 - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk13, msg13, sig13, 0); - } - { - /* Test vector 14 */ - const unsigned char pk14[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg14[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x14, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig14[64] = { - 0x4A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, - 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, - 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, - 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, - 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, - 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, - 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, - 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk14, msg14, sig14, 0); - } - { - /* Test vector 15 */ - const unsigned char pk15[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg15[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig15[64] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x2F, - 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, - 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, - 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, - 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk15, msg15, sig15, 0); - } - { - /* Test vector 16 */ - const unsigned char pk16[33] = { - 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, - 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, - 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, - 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, - 0x59 - }; - const unsigned char msg16[32] = { - 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, - 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, - 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, - 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 - }; - const unsigned char sig16[64] = { - 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, - 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, - 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, - 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, - 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, - 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 - }; - test_schnorrsig_bip_vectors_check_verify(scratch, pk16, msg16, sig16, 0); - } -} - -/* Nonce function that returns constant 0 */ -static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { - (void) msg32; - (void) key32; - (void) algo16; - (void) data; - (void) counter; - (void) nonce32; - return 0; -} - -/* Nonce function that sets nonce to 0 */ -static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { - (void) msg32; - (void) key32; - (void) algo16; - (void) data; - (void) counter; - - memset(nonce32, 0, 32); - return 1; -} - -void test_schnorrsig_sign(void) { - unsigned char sk[32]; - const unsigned char msg[32] = "this is a msg for a schnorrsig.."; - secp256k1_schnorrsig sig; - - memset(sk, 23, sizeof(sk)); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, NULL) == 1); - - /* Overflowing secret key */ - memset(sk, 0xFF, sizeof(sk)); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, NULL) == 0); - memset(sk, 23, sizeof(sk)); - - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, nonce_function_failing, NULL) == 0); - CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, nonce_function_0, NULL) == 0); -} - -#define N_SIGS 200 -/* 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]; - secp256k1_schnorrsig sig[N_SIGS]; - size_t i; - const secp256k1_schnorrsig *sig_arr[N_SIGS]; - const unsigned char *msg_arr[N_SIGS]; - const secp256k1_pubkey *pk_arr[N_SIGS]; - secp256k1_pubkey pk; - - CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk)); - - 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], NULL, msg[i], sk, 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 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].data[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].data[byte_idx] ^= xorbyte; - - byte_idx = secp256k1_rand_int(32); - sig[sig_idx].data[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].data[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 - -void run_schnorrsig_tests(void) { - secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); - - test_schnorrsig_serialize(); - test_schnorrsig_api(scratch); - test_schnorrsig_bip_vectors(scratch); - test_schnorrsig_sign(); - test_schnorrsig_sign_verify(scratch); - - secp256k1_scratch_space_destroy(ctx, scratch); -} - -#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 0c3a09e3..7258d8c8 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -449,29 +449,6 @@ static SECP256K1_INLINE void buffer_append(unsigned char *buf, unsigned int *off *offset += len; } -/* This nonce function is described in BIP-schnorr - * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki) */ -static int nonce_function_bipschnorr(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { - secp256k1_sha256 sha; - (void) counter; - VERIFY_CHECK(counter == 0); - - /* Hash x||msg as per the spec */ - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, key32, 32); - secp256k1_sha256_write(&sha, msg32, 32); - /* Hash in algorithm, which is not in the spec, but may be critical to - * users depending on it to avoid nonce reuse across algorithms. */ - if (algo16 != NULL) { - secp256k1_sha256_write(&sha, algo16, 16); - } - if (data != NULL) { - secp256k1_sha256_write(&sha, data, 32); - } - secp256k1_sha256_finalize(&sha, nonce32); - return 1; -} - static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { unsigned char keydata[112]; unsigned int offset = 0; @@ -502,7 +479,6 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m return 1; } -const secp256k1_nonce_function secp256k1_nonce_function_bipschnorr = nonce_function_bipschnorr; const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; @@ -777,10 +753,6 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/ecdh/main_impl.h" #endif -#ifdef ENABLE_MODULE_SCHNORRSIG -# include "modules/schnorrsig/main_impl.h" -#endif - #ifdef ENABLE_MODULE_MUSIG # include "modules/musig/main_impl.h" #endif diff --git a/src/tests.c b/src/tests.c index c594dee9..274d26cc 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5504,10 +5504,6 @@ void run_ecdsa_openssl(void) { # include "modules/ecdh/tests_impl.h" #endif -#ifdef ENABLE_MODULE_SCHNORRSIG -# include "modules/schnorrsig/tests_impl.h" -#endif - #ifdef ENABLE_MODULE_MUSIG # include "modules/musig/tests_impl.h" #endif @@ -5824,11 +5820,6 @@ int main(int argc, char **argv) { run_ecdh_tests(); #endif -#ifdef ENABLE_MODULE_SCHNORRSIG - /* Schnorrsig tests */ - run_schnorrsig_tests(); -#endif - #ifdef ENABLE_MODULE_MUSIG run_musig_tests(); #endif From 23900a0d86730f719c52aebba41a1c1cdb9288bd Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Fri, 15 Nov 2019 21:38:37 +0000 Subject: [PATCH 18/19] Fix the MuSig module after integrating bip-schnorr updates 1. using xonly_pubkeys in MuSig for input public keys and the combined pk. For that to work we need to store whether the MuSig aggregated point has an even y in the session, may need to negate each signers secret key and may need to negate each signers public key in musig_partial_sig_verify. 2. using a tagged hash for the message hash. 3. use !fe_is_odd in place of fe_is_quad_var --- include/secp256k1_musig.h | 82 ++++--- src/modules/musig/example.c | 31 +-- src/modules/musig/main_impl.h | 149 ++++++------ src/modules/musig/tests_impl.h | 405 ++++++++++++++++----------------- src/secp256k1.c | 23 +- 5 files changed, 356 insertions(+), 334 deletions(-) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index b1c5b912..c5352512 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -1,6 +1,8 @@ #ifndef SECP256K1_MUSIG_H #define SECP256K1_MUSIG_H +#include "secp256k1_extrakeys.h" + #ifdef __cplusplus extern "C" { #endif @@ -8,15 +10,30 @@ extern "C" { #include /** This module implements a Schnorr-based multi-signature scheme called MuSig - * (https://eprint.iacr.org/2018/068.pdf). There's an example C source file in the - * module's directory (src/modules/musig/example.c) that demonstrates how it can be - * used. + * (https://eprint.iacr.org/2018/068.pdf). It is compatible with bip-schnorr. + * There's an example C source file in the module's directory + * (src/modules/musig/example.c) that demonstrates how it can be used. * * The documentation in this include file is for reference and may not be sufficient * for users to begin using the library. A full description of API usage can be found * in src/modules/musig/musig.md */ +/** Data structure containing auxiliary data generated in `pubkey_combine` and + * required for `session_*_initialize`. + * Fields: + * magic: Set during initialization in `pubkey_combine` in order to allow + * detecting an uninitialized object. + * pk_hash: The 32-byte hash of the original public keys + * is_negated: Whether the MuSig-aggregated point was negated when + * converting it to the combined xonly pubkey. + */ +typedef struct { + uint64_t magic; + unsigned char pk_hash[32]; + int is_negated; +} secp256k1_musig_pre_session; + /** Data structure containing data related to a signing session resulting in a single * signature. * @@ -28,14 +45,14 @@ extern "C" { * structure. * * Fields: - * combined_pk: MuSig-computed combined public key + * combined_pk: MuSig-computed combined xonly public key + * pre_session: Auxiliary data created in `pubkey_combine` * n_signers: Number of signers - * pk_hash: The 32-byte hash of the original public keys * combined_nonce: Summed combined public nonce (undefined if `nonce_is_set` is false) * nonce_is_set: Whether the above nonce has been set * nonce_is_negated: If `nonce_is_set`, whether the above nonce was negated after * summing the participants' nonces. Needed to ensure the nonce's y - * coordinate has a quadratic-residue y coordinate + * coordinate is even. * msg: The 32-byte message (hash) to be signed * msg_is_set: Whether the above message has been set * has_secret_data: Whether this session object has a signers' secret data; if this @@ -49,9 +66,9 @@ extern "C" { * nonce_commitments_hash has been set */ typedef struct { - secp256k1_pubkey combined_pk; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; uint32_t n_signers; - unsigned char pk_hash[32]; secp256k1_pubkey combined_nonce; int nonce_is_set; int nonce_is_negated; @@ -119,9 +136,9 @@ typedef struct { * (cannot be NULL) * scratch: scratch space used to compute the combined pubkey by * multiexponentiation. If NULL, an inefficient algorithm is used. - * Out: combined_pk: the MuSig-combined public key (cannot be NULL) - * pk_hash32: if non-NULL, filled with the 32-byte hash of all input public - * keys in order to be used in `musig_session_initialize`. + * Out: combined_pk: the MuSig-combined xonly public key (cannot be NULL) + * pre_session: if non-NULL, pointer to a musig_pre_session struct to be used in + * `musig_session_initialize`. * In: pubkeys: input array of public keys to combine. The order is important; * a different order will result in a different combined public * key (cannot be NULL) @@ -130,9 +147,9 @@ typedef struct { SECP256K1_API int secp256k1_musig_pubkey_combine( const secp256k1_context* ctx, secp256k1_scratch_space *scratch, - secp256k1_pubkey *combined_pk, - unsigned char *pk_hash32, - const secp256k1_pubkey *pubkeys, + secp256k1_xonly_pubkey *combined_pk, + secp256k1_musig_pre_session *pre_session, + const secp256k1_xonly_pubkey *pubkeys, size_t n_pubkeys ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); @@ -154,9 +171,9 @@ SECP256K1_API int secp256k1_musig_pubkey_combine( * require sharing nonce commitments before the message is known * because it reduces nonce misuse resistance. If NULL, must be * set with `musig_session_get_public_nonce`. - * combined_pk: the combined public key of all signers (cannot be NULL) - * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be - * NULL) + * combined_pk: the combined xonly public key of all signers (cannot be NULL) + * pre_session: pointer to a musig_pre_session struct from + * `musig_pubkey_combine` (cannot be NULL) * n_signers: length of signers array. Number of signers participating in * the MuSig. Must be greater than 0 and at most 2^32 - 1. * my_index: index of this signer in the signers array @@ -169,8 +186,8 @@ SECP256K1_API int secp256k1_musig_session_initialize( unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, - const secp256k1_pubkey *combined_pk, - const unsigned char *pk_hash32, + const secp256k1_xonly_pubkey *combined_pk, + const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey @@ -213,7 +230,9 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_publi * signers: an array of signers' data to be initialized. Array length must * equal to `n_signers`(cannot be NULL) * In: msg32: the 32-byte message to be signed (cannot be NULL) - * combined_pk: the combined public key of all signers (cannot be NULL) + * combined_pk: the combined xonly public key of all signers (cannot be NULL) + * pre_session: pointer to a musig_pre_session struct from + * `musig_pubkey_combine` (cannot be NULL) * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be NULL) * commitments: array of 32-byte nonce commitments. Array length must equal to * `n_signers` (cannot be NULL) @@ -226,8 +245,8 @@ SECP256K1_API int secp256k1_musig_session_initialize_verifier( secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, - const secp256k1_pubkey *combined_pk, - const unsigned char *pk_hash32, + const secp256k1_xonly_pubkey *combined_pk, + const secp256k1_musig_pre_session *pre_session, const unsigned char *const *commitments, size_t n_signers ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); @@ -343,7 +362,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verif const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, - const secp256k1_pubkey *pubkey + const secp256k1_xonly_pubkey *pubkey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); /** Combines partial signatures @@ -354,23 +373,16 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verif * Args: ctx: pointer to a context object (cannot be NULL) * session: initialized session for which the combined nonce has been * computed (cannot be NULL) - * Out: sig: complete signature (cannot be NULL) + * Out: sig64: complete signature (cannot be NULL) * In: partial_sigs: array of partial signatures to combine (cannot be NULL) * n_sigs: number of signatures in the partial_sigs array - * tweak32: if `combined_pk` was tweaked with `ec_pubkey_tweak_add` after - * `musig_pubkey_combine` and before `musig_session_initialize` then - * the same tweak must be provided here in order to get a valid - * signature for the tweaked key. Otherwise `tweak` should be NULL. - * If the tweak is larger than the group order or 0 this function will - * return 0. (can be NULL) */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_combine( const secp256k1_context* ctx, const secp256k1_musig_session *session, - secp256k1_schnorrsig *sig, + unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, - size_t n_sigs, - const unsigned char *tweak32 + size_t n_sigs ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); /** Converts a partial signature to an adaptor signature by adding a given secret @@ -403,7 +415,7 @@ SECP256K1_API int secp256k1_musig_partial_sig_adapt( * 0: otherwise * Args: ctx: pointer to a context object (cannot be NULL) * Out:sec_adaptor32: 32-byte secret adaptor (cannot be NULL) - * In: sig: complete 2-of-2 signature (cannot be NULL) + * In: sig64: complete 2-of-2 signature (cannot be NULL) * partial_sigs: array of partial signatures (cannot be NULL) * n_partial_sigs: number of elements in partial_sigs array * nonce_is_negated: the `nonce_is_negated` output of `musig_session_combine_nonces` @@ -411,7 +423,7 @@ SECP256K1_API int secp256k1_musig_partial_sig_adapt( SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_adaptor( const secp256k1_context* ctx, unsigned char *sec_adaptor32, - const secp256k1_schnorrsig *sig, + const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_is_negated diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c index b4c9a95d..4670d442 100644 --- a/src/modules/musig/example.c +++ b/src/modules/musig/example.c @@ -18,8 +18,9 @@ /* Number of public keys involved in creating the aggregate signature */ #define N_SIGNERS 3 /* Create a key pair and store it in seckey and pubkey */ -int create_key(const secp256k1_context* ctx, unsigned char* seckey, secp256k1_pubkey* pubkey) { +int create_keypair(const secp256k1_context* ctx, unsigned char *seckey, secp256k1_xonly_pubkey *pubkey) { int ret; + secp256k1_keypair keypair; FILE *frand = fopen("/dev/urandom", "r"); if (frand == NULL) { return 0; @@ -32,12 +33,14 @@ int create_key(const secp256k1_context* ctx, unsigned char* seckey, secp256k1_pu /* The probability that this not a valid secret key is approximately 2^-128 */ } while (!secp256k1_ec_seckey_verify(ctx, seckey)); fclose(frand); - ret = secp256k1_ec_pubkey_create(ctx, pubkey, seckey); + ret = secp256k1_keypair_create(ctx, &keypair, seckey); + ret &= secp256k1_keypair_xonly_pub(ctx, pubkey, NULL, &keypair); + return ret; } /* Sign a message hash with the given key pairs and store the result in sig */ -int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_pubkey* pubkeys, const unsigned char* msg32, secp256k1_schnorrsig *sig) { +int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_xonly_pubkey* pubkeys, const unsigned char* msg32, unsigned char *sig64) { secp256k1_musig_session musig_session[N_SIGNERS]; unsigned char nonce_commitment[N_SIGNERS][32]; const unsigned char *nonce_commitment_ptr[N_SIGNERS]; @@ -49,11 +52,11 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 for (i = 0; i < N_SIGNERS; i++) { FILE *frand; unsigned char session_id32[32]; - unsigned char pk_hash[32]; - secp256k1_pubkey combined_pk; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; /* Create combined pubkey and initialize signer data */ - if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, pk_hash, pubkeys, N_SIGNERS)) { + if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, &pre_session, pubkeys, N_SIGNERS)) { return 0; } /* Create random session ID. It is absolutely necessary that the session ID @@ -69,7 +72,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 } fclose(frand); /* Initialize session */ - if (!secp256k1_musig_session_initialize(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, pk_hash, N_SIGNERS, i, seckeys[i])) { + if (!secp256k1_musig_session_initialize(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, i, seckeys[i])) { return 0; } nonce_commitment_ptr[i] = &nonce_commitment[i][0]; @@ -119,23 +122,23 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 } } } - return secp256k1_musig_partial_sig_combine(ctx, &musig_session[0], sig, partial_sig, N_SIGNERS, NULL); + return secp256k1_musig_partial_sig_combine(ctx, &musig_session[0], sig64, partial_sig, N_SIGNERS); } int main(void) { secp256k1_context* ctx; int i; unsigned char seckeys[N_SIGNERS][32]; - secp256k1_pubkey pubkeys[N_SIGNERS]; - secp256k1_pubkey combined_pk; + secp256k1_xonly_pubkey pubkeys[N_SIGNERS]; + secp256k1_xonly_pubkey combined_pk; unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!"; - secp256k1_schnorrsig sig; + unsigned char sig[64]; /* Create a context for signing and verification */ ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); printf("Creating key pairs......"); for (i = 0; i < N_SIGNERS; i++) { - if (!create_key(ctx, seckeys[i], &pubkeys[i])) { + if (!create_keypair(ctx, seckeys[i], &pubkeys[i])) { printf("FAILED\n"); return 1; } @@ -148,13 +151,13 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25 } printf("ok\n"); printf("Signing message........."); - if (!sign(ctx, seckeys, pubkeys, msg, &sig)) { + if (!sign(ctx, seckeys, pubkeys, msg, sig)) { printf("FAILED\n"); return 1; } printf("ok\n"); printf("Verifying signature....."); - if (!secp256k1_schnorrsig_verify(ctx, &sig, msg, &combined_pk)) { + if (!secp256k1_schnorrsig_verify(ctx, sig, msg, &combined_pk)) { printf("FAILED\n"); return 1; } diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index d1123539..5f9cb0d0 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -7,23 +7,23 @@ #ifndef _SECP256K1_MODULE_MUSIG_MAIN_ #define _SECP256K1_MODULE_MUSIG_MAIN_ +#include #include "include/secp256k1.h" #include "include/secp256k1_musig.h" #include "hash.h" /* Computes ell = SHA256(pk[0], ..., pk[np-1]) */ -static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_pubkey *pk, size_t np) { +static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_xonly_pubkey *pk, size_t np) { secp256k1_sha256 sha; size_t i; secp256k1_sha256_initialize(&sha); for (i = 0; i < np; i++) { - unsigned char ser[33]; - size_t serlen = sizeof(ser); - if (!secp256k1_ec_pubkey_serialize(ctx, ser, &serlen, &pk[i], SECP256K1_EC_COMPRESSED)) { + unsigned char ser[32]; + if (!secp256k1_xonly_pubkey_serialize(ctx, ser, &pk[i])) { return 0; } - secp256k1_sha256_write(&sha, ser, serlen); + secp256k1_sha256_write(&sha, ser, 32); } secp256k1_sha256_finalize(&sha, ell); return 1; @@ -77,14 +77,14 @@ static void secp256k1_musig_coefficient(secp256k1_scalar *r, const unsigned char typedef struct { const secp256k1_context *ctx; unsigned char ell[32]; - const secp256k1_pubkey *pks; + const secp256k1_xonly_pubkey *pks; } secp256k1_musig_pubkey_combine_ecmult_data; /* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */ static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data; secp256k1_musig_coefficient(sc, ctx->ell, idx); - return secp256k1_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]); + return secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]); } @@ -97,10 +97,13 @@ static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *si } } -int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_pubkey *combined_pk, unsigned char *pk_hash32, const secp256k1_pubkey *pubkeys, size_t n_pubkeys) { +static const uint64_t pre_session_magic = 0xf4adbbdf7c7dd304UL; + +int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const secp256k1_xonly_pubkey *pubkeys, size_t n_pubkeys) { secp256k1_musig_pubkey_combine_ecmult_data ecmult_data; secp256k1_gej pkj; secp256k1_ge pkp; + int is_negated; VERIFY_CHECK(ctx != NULL); ARG_CHECK(combined_pk != NULL); @@ -117,23 +120,26 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat return 0; } secp256k1_ge_set_gej(&pkp, &pkj); - secp256k1_pubkey_save(combined_pk, &pkp); + secp256k1_fe_normalize(&pkp.y); + is_negated = secp256k1_extrakeys_ge_even_y(&pkp); + secp256k1_xonly_pubkey_save(combined_pk, &pkp); - if (pk_hash32 != NULL) { - memcpy(pk_hash32, ecmult_data.ell, 32); + if (pre_session != NULL) { + pre_session->magic = pre_session_magic; + memcpy(pre_session->pk_hash, ecmult_data.ell, 32); + pre_session->is_negated = is_negated; } return 1; } -int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, size_t n_signers, size_t my_index, const unsigned char *seckey) { - unsigned char combined_ser[33]; - size_t combined_ser_size = sizeof(combined_ser); +int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) { + unsigned char combined_ser[32]; int overflow; secp256k1_scalar secret; secp256k1_scalar mu; secp256k1_sha256 sha; - secp256k1_gej rj; - secp256k1_ge rp; + secp256k1_gej pj; + secp256k1_ge p; VERIFY_CHECK(ctx != NULL); ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); @@ -142,7 +148,8 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m ARG_CHECK(nonce_commitment32 != NULL); ARG_CHECK(session_id32 != NULL); ARG_CHECK(combined_pk != NULL); - ARG_CHECK(pk_hash32 != NULL); + ARG_CHECK(pre_session != NULL); + ARG_CHECK(pre_session->magic == pre_session_magic); ARG_CHECK(seckey != NULL); memset(session, 0, sizeof(*session)); @@ -154,7 +161,7 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m session->msg_is_set = 0; } memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); - memcpy(session->pk_hash, pk_hash32, 32); + session->pre_session = *pre_session; session->nonce_is_set = 0; session->has_secret_data = 1; if (n_signers == 0 || my_index >= n_signers) { @@ -173,7 +180,25 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m secp256k1_scalar_clear(&secret); return 0; } - secp256k1_musig_coefficient(&mu, pk_hash32, (uint32_t) my_index); + secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, (uint32_t) my_index); + /* Compute the signers public key point and determine if the secret needs to + * be negated before signing. If the signer's pubkey is negated XOR the + * MuSig-combined pubkey is negated the secret has to be negated. This can + * be seen by looking at the secret key belonging to `combined_pk`. Let's + * define + * P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key + * point x_i*G, mu_i is the i-th musig coefficient and |.| is a function + * that normalizes a point to an even Y by negating if necessary similar to + * secp256k1_extrakeys_ge_even_y. Then we have + * P := |P'| the combined xonly public key. Also, P = x*G where x = + * sum_i(b_i*mu_i*x_i) and b_i = -1 if (P != |P'| XOR P_i != |P_i|) and 1 + * otherwise. */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); + secp256k1_ge_set_gej(&p, &pj); + secp256k1_fe_normalize(&p.y); + if (secp256k1_fe_is_odd(&p.y) != session->pre_session.is_negated) { + secp256k1_scalar_negate(&secret, &secret); + } secp256k1_scalar_mul(&secret, &secret, &mu); secp256k1_scalar_get_b32(session->seckey, &secret); @@ -183,8 +208,8 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m if (session->msg_is_set) { secp256k1_sha256_write(&sha, msg32, 32); } - secp256k1_ec_pubkey_serialize(ctx, combined_ser, &combined_ser_size, combined_pk, SECP256K1_EC_COMPRESSED); - secp256k1_sha256_write(&sha, combined_ser, combined_ser_size); + secp256k1_xonly_pubkey_serialize(ctx, combined_ser, combined_pk); + secp256k1_sha256_write(&sha, combined_ser, 32); secp256k1_sha256_write(&sha, seckey, 32); secp256k1_sha256_finalize(&sha, session->secnonce); secp256k1_scalar_set_b32(&secret, session->secnonce, &overflow); @@ -194,9 +219,9 @@ int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_m } /* Compute public nonce and commitment */ - secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &secret); - secp256k1_ge_set_gej(&rp, &rj); - secp256k1_pubkey_save(&session->nonce, &rp); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret); + secp256k1_ge_set_gej(&p, &pj); + secp256k1_pubkey_save(&session->nonce, &p); if (nonce_commitment32 != NULL) { unsigned char commit[33]; @@ -256,7 +281,7 @@ int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp2 return 1; } -int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, const unsigned char *const *commitments, size_t n_signers) { +int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, const unsigned char *const *commitments, size_t n_signers) { size_t i; VERIFY_CHECK(ctx != NULL); @@ -264,7 +289,8 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se ARG_CHECK(signers != NULL); ARG_CHECK(msg32 != NULL); ARG_CHECK(combined_pk != NULL); - ARG_CHECK(pk_hash32 != NULL); + ARG_CHECK(pre_session != NULL); + ARG_CHECK(pre_session->magic == pre_session_magic); ARG_CHECK(commitments != NULL); /* Check n_signers before checking commitments to allow testing the case where * n_signers is big without allocating the space. */ @@ -279,13 +305,14 @@ int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, se memset(session, 0, sizeof(*session)); memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); + session->pre_session = *pre_session; if (n_signers == 0) { return 0; } session->n_signers = (uint32_t) n_signers; secp256k1_musig_signers_init(signers, session->n_signers); - memcpy(session->pk_hash, pk_hash32, 32); + session->pre_session = *pre_session; session->nonce_is_set = 0; session->msg_is_set = 1; memcpy(session->msg, msg32, 32); @@ -365,7 +392,8 @@ int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256 secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); } secp256k1_ge_set_gej(&combined_noncep, &combined_noncej); - if (secp256k1_fe_is_quad_var(&combined_noncep.y)) { + secp256k1_fe_normalize(&combined_noncep.y); + if (!secp256k1_fe_is_odd(&combined_noncep.y)) { session->nonce_is_negated = 0; } else { session->nonce_is_negated = 1; @@ -397,21 +425,20 @@ int secp256k1_musig_partial_signature_parse(const secp256k1_context* ctx, secp25 /* Compute msghash = SHA256(combined_nonce, combined_pk, msg) */ static int secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) { - unsigned char buf[33]; - size_t bufsize = 33; + unsigned char buf[32]; secp256k1_ge rp; secp256k1_sha256 sha; - secp256k1_sha256_initialize(&sha); + secp256k1_schnorrsig_sha256_tagged(&sha); if (!session->nonce_is_set) { return 0; } secp256k1_pubkey_load(ctx, &rp, &session->combined_nonce); secp256k1_fe_get_b32(buf, &rp.x); secp256k1_sha256_write(&sha, buf, 32); - secp256k1_ec_pubkey_serialize(ctx, buf, &bufsize, &session->combined_pk, SECP256K1_EC_COMPRESSED); - VERIFY_CHECK(bufsize == 33); - secp256k1_sha256_write(&sha, buf, bufsize); + + secp256k1_xonly_pubkey_serialize(ctx, buf, &session->combined_pk); + secp256k1_sha256_write(&sha, buf, 32); if (!session->msg_is_set) { return 0; } @@ -466,14 +493,14 @@ int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_m return 1; } -int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs, const unsigned char *tweak32) { +int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs) { size_t i; secp256k1_scalar s; secp256k1_ge noncep; (void) ctx; VERIFY_CHECK(ctx != NULL); - ARG_CHECK(sig != NULL); + ARG_CHECK(sig64 != NULL); ARG_CHECK(partial_sigs != NULL); ARG_CHECK(session != NULL); @@ -495,40 +522,23 @@ int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp secp256k1_scalar_add(&s, &s, &term); } - /* If there is a tweak then add `msghash` times `tweak` to `s`.*/ - if (tweak32 != NULL) { - unsigned char msghash[32]; - secp256k1_scalar e, scalar_tweak; - int overflow = 0; - - if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) { - return 0; - } - secp256k1_scalar_set_b32(&e, msghash, NULL); - secp256k1_scalar_set_b32(&scalar_tweak, tweak32, &overflow); - if (overflow || !secp256k1_eckey_privkey_tweak_mul(&e, &scalar_tweak)) { - /* This mimics the behavior of secp256k1_ec_privkey_tweak_mul regarding - * overflow and tweak32 being 0. */ - return 0; - } - secp256k1_scalar_add(&s, &s, &e); - } - secp256k1_pubkey_load(ctx, &noncep, &session->combined_nonce); - VERIFY_CHECK(secp256k1_fe_is_quad_var(&noncep.y)); + VERIFY_CHECK(!secp256k1_fe_is_odd(&noncep.y)); secp256k1_fe_normalize(&noncep.x); - secp256k1_fe_get_b32(&sig->data[0], &noncep.x); - secp256k1_scalar_get_b32(&sig->data[32], &s); + secp256k1_fe_get_b32(&sig64[0], &noncep.x); + secp256k1_scalar_get_b32(&sig64[32], &s); return 1; } -int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, const secp256k1_pubkey *pubkey) { +int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, const secp256k1_xonly_pubkey *pubkey) { unsigned char msghash[32]; secp256k1_scalar s; secp256k1_scalar e; secp256k1_scalar mu; + secp256k1_gej pkj; secp256k1_gej rj; + secp256k1_ge pkp; secp256k1_ge rp; int overflow; @@ -554,16 +564,27 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2 /* Multiplying the messagehash by the musig coefficient is equivalent * to multiplying the signer's public key by the coefficient, except * much easier to do. */ - secp256k1_musig_coefficient(&mu, session->pk_hash, signer->index); + secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, signer->index); secp256k1_scalar_mul(&e, &e, &mu); if (!secp256k1_pubkey_load(ctx, &rp, &signer->nonce)) { return 0; } + /* If the MuSig-combined point is negated, the signers will sign for the + * negation of their individual xonly public key such that the combined + * signature is valid for the MuSig aggregated xonly key. */ + if (session->pre_session.is_negated) { + secp256k1_scalar_negate(&e, &e); + } - if (!secp256k1_schnorrsig_real_verify(ctx, &rj, &s, &e, pubkey)) { + /* Compute rj = s*G + (-e)*pkj */ + secp256k1_scalar_negate(&e, &e); + if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) { return 0; } + secp256k1_gej_set_ge(&pkj, &pkp); + secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s); + if (!session->nonce_is_negated) { secp256k1_ge_neg(&rp, &rp); } @@ -603,7 +624,7 @@ int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_mu return 1; } -int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_is_negated) { +int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const unsigned char *sig64, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_is_negated) { secp256k1_scalar t; secp256k1_scalar s; int overflow; @@ -612,10 +633,10 @@ int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigne (void) ctx; VERIFY_CHECK(ctx != NULL); ARG_CHECK(sec_adaptor32 != NULL); - ARG_CHECK(sig != NULL); + ARG_CHECK(sig64 != NULL); ARG_CHECK(partial_sigs != NULL); - secp256k1_scalar_set_b32(&t, &sig->data[32], &overflow); + secp256k1_scalar_set_b32(&t, &sig64[32], &overflow); if (overflow) { return 0; } diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index ce5b37d0..0930e90a 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -9,6 +9,69 @@ #include "secp256k1_musig.h" +int secp256k1_xonly_pubkey_create(secp256k1_xonly_pubkey *pk, const unsigned char *seckey) { + int ret; + secp256k1_keypair keypair; + ret = secp256k1_keypair_create(ctx, &keypair, seckey); + ret &= secp256k1_keypair_xonly_pub(ctx, pk, NULL, &keypair); + return ret; +} + +/* Just a simple (non-adaptor, non-tweaked) 2-of-2 MuSig combine, sign, verify + * test. */ +void musig_simple_test(secp256k1_scratch_space *scratch) { + unsigned char sk[2][32]; + secp256k1_musig_session session[2]; + secp256k1_musig_session_signer_data signer0[2]; + secp256k1_musig_session_signer_data signer1[2]; + unsigned char nonce_commitment[2][32]; + unsigned char msg[32]; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; + unsigned char session_id[2][32]; + secp256k1_xonly_pubkey pk[2]; + const unsigned char *ncs[2]; + secp256k1_pubkey public_nonce[3]; + secp256k1_musig_partial_signature partial_sig[2]; + unsigned char final_sig[64]; + + secp256k1_rand256(session_id[0]); + secp256k1_rand256(session_id[1]); + secp256k1_rand256(sk[0]); + secp256k1_rand256(sk[1]); + secp256k1_rand256(msg); + + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); + + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signer0, &public_nonce[0], ncs, 2, NULL) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signer1, &public_nonce[1], ncs, 2, NULL) == 1); + + CHECK(secp256k1_musig_set_nonce(ctx, &signer0[0], &public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer0[1], &public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer1[0], &public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signer1[1], &public_nonce[1]) == 1); + + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signer0, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signer1, 2, NULL, NULL) == 1); + + CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signer0[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1); + + CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], final_sig, partial_sig, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig, msg, &combined_pk) == 1); +} + void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_scratch_space *scratch_small; secp256k1_musig_session session[2]; @@ -19,8 +82,8 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_musig_partial_signature partial_sig[2]; secp256k1_musig_partial_signature partial_sig_adapted[2]; secp256k1_musig_partial_signature partial_sig_overflow; - secp256k1_schnorrsig final_sig; - secp256k1_schnorrsig final_sig_cmp; + unsigned char final_sig[64]; + unsigned char final_sig_cmp[64]; unsigned char buf[32]; unsigned char sk[2][32]; @@ -31,9 +94,10 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { const unsigned char *ncs[2]; unsigned char msg[32]; unsigned char msghash[32]; - secp256k1_pubkey combined_pk; - unsigned char pk_hash[32]; - secp256k1_pubkey pk[2]; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; + secp256k1_musig_pre_session pre_session_uninitialized; + secp256k1_xonly_pubkey pk[2]; unsigned char tweak[32]; unsigned char sec_adaptor[32]; @@ -54,6 +118,10 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); memset(ones, 0xff, 32); + /* Simulate pre_session being uninitialized by setting it to 0s. Actually providing + * an unitialized pre_session object to a initialize_*_session would be undefined + * behavior */ + memset(&pre_session_uninitialized, 0, sizeof(pre_session_uninitialized)); secp256k1_rand256(session_id[0]); secp256k1_rand256(session_id[1]); @@ -63,104 +131,108 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { secp256k1_rand256(sec_adaptor); secp256k1_rand256(tweak); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor) == 1); + /** main test body **/ /* Key combination */ ecount = 0; - CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, pk_hash, pk, 2) == 0); + CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, &pre_session, pk, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, pk_hash, pk, 2) == 0); + CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, &pre_session, pk, 2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); CHECK(ecount == 2); /* pubkey_combine does not require a scratch space */ - CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, &pre_session, pk, 2) == 1); CHECK(ecount == 2); /* A small scratch space works too, but will result in using an ineffecient algorithm */ scratch_small = secp256k1_scratch_space_create(ctx, 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, &pre_session, pk, 2) == 1); secp256k1_scratch_space_destroy(ctx, scratch_small); CHECK(ecount == 2); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, pk_hash, pk, 2) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, &pre_session, pk, 2) == 0); CHECK(ecount == 3); CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, NULL, pk, 2) == 1); CHECK(ecount == 3); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, NULL, 2) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 2) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 0) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 0) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, NULL, 0) == 0); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 0) == 0); CHECK(ecount == 6); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); - CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1); /** Session creation **/ ecount = 0; - CHECK(secp256k1_musig_session_initialize(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_initialize(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_initialize(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 3); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 6); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); CHECK(ecount == 6); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, pk_hash, 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, 0, sk[0]) == 0); CHECK(ecount == 7); CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0); CHECK(ecount == 8); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 0, 0, sk[0]) == 0); - CHECK(ecount == 8); + /* Uninitialized pre_session */ + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, 0, sk[0]) == 0); + CHECK(ecount == 9); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, 0, sk[0]) == 0); + CHECK(ecount == 9); /* If more than UINT32_MAX fits in a size_t, test that session_initialize * rejects n_signers that high. */ if (SIZE_MAX > UINT32_MAX) { - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); } - CHECK(ecount == 8); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, NULL) == 0); CHECK(ecount == 9); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0); + CHECK(ecount == 10); /* secret key overflows */ - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, ones) == 0); - CHECK(ecount == 9); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, ones) == 0); + CHECK(ecount == 10); - CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); - CHECK(secp256k1_musig_session_initialize(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, pk_hash, 2, 1, sk[1]) == 1); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); ncs[0] = nonce_commitment[0]; ncs[1] = nonce_commitment[1]; ecount = 0; - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 1); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); CHECK(ecount == 0); - CHECK(secp256k1_musig_session_initialize_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, pk_hash, ncs, 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, &pre_session, ncs, 2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, pk_hash, ncs, 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, &pre_session, ncs, 2) == 0); CHECK(ecount == 3); CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0); CHECK(ecount == 4); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, NULL, 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, NULL, 2) == 0); CHECK(ecount == 5); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 0) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 0) == 0); CHECK(ecount == 5); if (SIZE_MAX > UINT32_MAX) { - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, ((size_t) UINT32_MAX) + 2) == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, ((size_t) UINT32_MAX) + 2) == 0); } CHECK(ecount == 5); - CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 1); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, &pre_session, ncs, 2) == 1); CHECK(secp256k1_musig_compute_messagehash(none, msghash, &verifier_session) == 0); CHECK(secp256k1_musig_compute_messagehash(none, msghash, &session[0]) == 0); @@ -306,65 +378,59 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { /** Signing combining and verification */ ecount = 0; - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2, NULL) == 1); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2, NULL) == 1); - CHECK(memcmp(&final_sig, &final_sig_cmp, sizeof(final_sig)) == 0); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2, NULL) == 1); - CHECK(memcmp(&final_sig, &final_sig_cmp, sizeof(final_sig)) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(memcmp(final_sig, final_sig_cmp, sizeof(final_sig)) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(memcmp(final_sig, final_sig_cmp, sizeof(final_sig)) == 0); - CHECK(secp256k1_musig_partial_sig_combine(none, NULL, &final_sig, partial_sig_adapted, 2, tweak) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, NULL, final_sig, partial_sig_adapted, 2) == 0); CHECK(ecount == 1); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2, tweak) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0); CHECK(ecount == 2); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, NULL, 2, tweak) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, NULL, 2) == 0); CHECK(ecount == 3); { secp256k1_musig_partial_signature partial_sig_tmp[2]; partial_sig_tmp[0] = partial_sig_adapted[0]; partial_sig_tmp[1] = partial_sig_overflow; - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_tmp, 2, tweak) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_tmp, 2) == 0); } CHECK(ecount == 3); /* Wrong number of partial sigs */ - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 1, tweak) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 1) == 0); CHECK(ecount == 3); - { - /* Overflowing tweak */ - unsigned char overflowing_tweak[32]; - memset(overflowing_tweak, 0xff, sizeof(overflowing_tweak)); - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2, overflowing_tweak) == 0); - CHECK(ecount == 3); - } - CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2, NULL) == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], final_sig, partial_sig_adapted, 2) == 1); CHECK(ecount == 3); - CHECK(secp256k1_schnorrsig_verify(vrfy, &final_sig, msg, &combined_pk) == 1); + CHECK(secp256k1_schnorrsig_verify(vrfy, final_sig, msg, &combined_pk) == 1); /** Secret adaptor can be extracted from signature */ ecount = 0; - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 2, nonce_is_negated) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, nonce_is_negated) == 1); CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) == 0); - CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, &final_sig, partial_sig, 2, 0) == 0); + CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, final_sig, partial_sig, 2, 0) == 0); CHECK(ecount == 1); CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, NULL, partial_sig, 2, 0) == 0); CHECK(ecount == 2); { - secp256k1_schnorrsig final_sig_tmp = final_sig; - memcpy(&final_sig_tmp.data[32], ones, 32); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig_tmp, partial_sig, 2, nonce_is_negated) == 0); + unsigned char final_sig_tmp[64]; + memcpy(final_sig_tmp, final_sig, sizeof(final_sig_tmp)); + memcpy(&final_sig_tmp[32], ones, 32); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig_tmp, partial_sig, 2, nonce_is_negated) == 0); } CHECK(ecount == 2); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, NULL, 2, 0) == 0); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, NULL, 2, 0) == 0); CHECK(ecount == 3); { secp256k1_musig_partial_signature partial_sig_tmp[2]; partial_sig_tmp[0] = partial_sig[0]; partial_sig_tmp[1] = partial_sig_overflow; - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig_tmp, 2, nonce_is_negated) == 0); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig_tmp, 2, nonce_is_negated) == 0); } CHECK(ecount == 3); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 0, 0) == 1); - CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 2, 1) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 0, 0) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, final_sig, partial_sig, 2, 1) == 1); /** cleanup **/ memset(&session, 0, sizeof(session)); @@ -380,26 +446,26 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { * ones and return the resulting messagehash. This should not result in a different * messagehash because the public keys of the signers are only used during session * initialization. */ -int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, const unsigned char * const *nonce_commitments, unsigned char *msg, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id) { +int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const unsigned char * const *nonce_commitments, unsigned char *msg, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id) { secp256k1_musig_session session; secp256k1_musig_session session_tmp; unsigned char nonce_commitment[32]; secp256k1_musig_session_signer_data signers[2]; secp256k1_musig_session_signer_data signers_tmp[2]; unsigned char sk_dummy[32]; - secp256k1_pubkey pks_tmp[2]; - secp256k1_pubkey combined_pk_tmp; - unsigned char pk_hash_tmp[32]; + secp256k1_xonly_pubkey pks_tmp[2]; + secp256k1_xonly_pubkey combined_pk_tmp; + secp256k1_musig_pre_session pre_session_tmp; secp256k1_pubkey nonce; /* Set up signers with different public keys */ secp256k1_rand256(sk_dummy); pks_tmp[0] = pks[0]; - CHECK(secp256k1_ec_pubkey_create(ctx, &pks_tmp[1], sk_dummy) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, pk_hash_tmp, pks_tmp, 2) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, pk_hash_tmp, 2, 1, sk_dummy) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pks_tmp[1], sk_dummy) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp, 2) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, 1, sk_dummy) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 0, sk) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 0, sk) == 1); CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); /* Call get_public_nonce with different signers than the signers the session was * initialized with. */ @@ -417,7 +483,7 @@ int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256 * commitments of signers_other do not match the nonce commitments the new session * was initialized with. If do_test is 0, the correct signers are being used and * therefore the function should return 1. */ -int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) { +int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) { secp256k1_musig_session session; secp256k1_musig_session_signer_data signers[2]; secp256k1_musig_session_signer_data *signers_to_use; @@ -428,7 +494,7 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_pubkey *combin /* Initialize new signers */ secp256k1_rand256(session_id); - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 1, sk) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, NULL) == 1); @@ -448,7 +514,7 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_pubkey *combin * parameters but without a message. Will test that the message must be * provided with `get_public_nonce`. */ -void musig_state_machine_late_msg_test(secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { +void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { /* Create context for testing ARG_CHECKs by setting an illegal_callback. */ secp256k1_context *ctx_tmp = secp256k1_context_create(SECP256K1_CONTEXT_NONE); int ecount = 0; @@ -460,7 +526,7 @@ void musig_state_machine_late_msg_test(secp256k1_pubkey *pks, secp256k1_pubkey * secp256k1_musig_partial_signature partial_sig; secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount); - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pk_hash, 2, 1, sk) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; @@ -488,17 +554,17 @@ void musig_state_machine_late_msg_test(secp256k1_pubkey *pks, secp256k1_pubkey * * and tries to verify and combine partial sigs. If do_combine is 0, the * combine_nonces step is left out. In that case verify and combine should fail and * this function should return 0. */ -int musig_state_machine_missing_combine_test(secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, secp256k1_musig_partial_signature *partial_sig_other, unsigned char *msg, unsigned char *sk, unsigned char *session_id, secp256k1_musig_partial_signature *partial_sig, int do_combine) { +int musig_state_machine_missing_combine_test(secp256k1_xonly_pubkey *pks, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, secp256k1_musig_partial_signature *partial_sig_other, unsigned char *msg, unsigned char *sk, unsigned char *session_id, secp256k1_musig_partial_signature *partial_sig, int do_combine) { secp256k1_musig_session session; secp256k1_musig_session_signer_data signers[2]; unsigned char nonce_commitment[32]; const unsigned char *ncs[2]; secp256k1_pubkey nonce; secp256k1_musig_partial_signature partial_sigs[2]; - secp256k1_schnorrsig sig; + unsigned char sig[64]; int partial_verify, sig_combine; - CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 1, sk) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1); ncs[0] = nonce_commitment_other; ncs[1] = nonce_commitment; CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2, NULL) == 1); @@ -511,7 +577,7 @@ int musig_state_machine_missing_combine_test(secp256k1_pubkey *pks, secp256k1_pu CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); } partial_verify = secp256k1_musig_partial_sig_verify(ctx, &session, signers, partial_sig_other, &pks[0]); - sig_combine = secp256k1_musig_partial_sig_combine(ctx, &session, &sig, partial_sigs, 2, NULL); + sig_combine = secp256k1_musig_partial_sig_combine(ctx, &session, sig, partial_sigs, 2); if (do_combine != 0) { /* Return 1 if both succeeded */ return partial_verify && sig_combine; @@ -529,9 +595,9 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { unsigned char session_id[2][32]; unsigned char msg[32]; unsigned char sk[2][32]; - secp256k1_pubkey pk[2]; - secp256k1_pubkey combined_pk; - unsigned char pk_hash[32]; + secp256k1_xonly_pubkey pk[2]; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; secp256k1_pubkey nonce[2]; const unsigned char *ncs[2]; secp256k1_musig_partial_signature partial_sig[2]; @@ -547,11 +613,11 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { secp256k1_rand256(sk[0]); secp256k1_rand256(sk[1]); secp256k1_rand256(msg); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, pk_hash, pk, 2) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, pk_hash, 2, 1, sk[1]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1); + CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1); /* Set nonce commitments */ ncs[0] = nonce_commitment[0]; @@ -583,8 +649,8 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], &nonce[1]) == 1); /* Can't combine nonces from signers of a different session */ - CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, pk_hash, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 1) == 0); - CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, pk_hash, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 0) == 1); + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 1) == 0); + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, &pre_session, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 0) == 1); /* Partially sign */ CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); @@ -597,7 +663,7 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { * with different signers (i.e. they diff in public keys). This is because the * public keys of the signers is set in stone when initializing the session. */ CHECK(secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]) == 1); - CHECK(musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, pk_hash, ncs, msg, &nonce[0], sk[1], session_id[1]) == 1); + CHECK(musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, &pre_session, ncs, msg, &nonce[0], sk[1], session_id[1]) == 1); CHECK(memcmp(msghash1, msghash2, 32) == 0); CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); @@ -605,11 +671,11 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) { /* Wrong signature */ CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0); /* Can't get the public nonce until msg is set */ - musig_state_machine_late_msg_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], sk[1], session_id[1], msg); + musig_state_machine_late_msg_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], sk[1], session_id[1], msg); /* Can't verify and combine partial sigs until nonces are combined */ - CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 0) == 0); - CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 1) == 1); + CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 0) == 0); + CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, &pre_session, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 1) == 1); } } @@ -618,8 +684,8 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { * while the indices 0 and 1 refer to the two signers. Here signer 0 is * sending a-coins to signer 1, while signer 1 is sending b-coins to signer * 0. Signer 0 produces the adaptor signatures. */ - secp256k1_schnorrsig final_sig_a; - secp256k1_schnorrsig final_sig_b; + unsigned char final_sig_a[64]; + unsigned char final_sig_b[64]; secp256k1_musig_partial_signature partial_sig_a[2]; secp256k1_musig_partial_signature partial_sig_b_adapted[2]; secp256k1_musig_partial_signature partial_sig_b[2]; @@ -629,12 +695,12 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { unsigned char seckey_a[2][32]; unsigned char seckey_b[2][32]; - secp256k1_pubkey pk_a[2]; - secp256k1_pubkey pk_b[2]; - unsigned char pk_hash_a[32]; - unsigned char pk_hash_b[32]; - secp256k1_pubkey combined_pk_a; - secp256k1_pubkey combined_pk_b; + secp256k1_xonly_pubkey pk_a[2]; + secp256k1_xonly_pubkey pk_b[2]; + secp256k1_musig_pre_session pre_session_a; + secp256k1_musig_pre_session pre_session_b; + secp256k1_xonly_pubkey combined_pk_a; + secp256k1_xonly_pubkey combined_pk_b; secp256k1_musig_session musig_session_a[2]; secp256k1_musig_session musig_session_b[2]; unsigned char noncommit_a[2][32]; @@ -659,22 +725,22 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { secp256k1_rand256(seckey_b[1]); secp256k1_rand256(sec_adaptor); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk_a[0], seckey_a[0])); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk_a[1], seckey_a[1])); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk_b[0], seckey_b[0])); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk_b[1], seckey_b[1])); + CHECK(secp256k1_xonly_pubkey_create(&pk_a[0], seckey_a[0])); + CHECK(secp256k1_xonly_pubkey_create(&pk_a[1], seckey_a[1])); + CHECK(secp256k1_xonly_pubkey_create(&pk_b[0], seckey_b[0])); + CHECK(secp256k1_xonly_pubkey_create(&pk_b[1], seckey_b[1])); CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor)); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, pk_hash_a, pk_a, 2)); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, pk_hash_b, pk_b, 2)); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a, 2)); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b, 2)); - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, pk_hash_a, 2, 0, seckey_a[0])); - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, pk_hash_a, 2, 1, seckey_a[1])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 0, seckey_a[0])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 1, seckey_a[1])); noncommit_a_ptr[0] = noncommit_a[0]; noncommit_a_ptr[1] = noncommit_a[1]; - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, pk_hash_b, 2, 0, seckey_b[0])); - CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, pk_hash_b, 2, 1, seckey_b[1])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 0, seckey_b[0])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 1, seckey_b[1])); noncommit_b_ptr[0] = noncommit_b[0]; noncommit_b_ptr[1] = noncommit_b[1]; @@ -707,17 +773,17 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { * is broadcasted by signer 0 to take B-coins. */ CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, nonce_is_negated_b)); memcpy(&partial_sig_b_adapted[1], &partial_sig_b[1], sizeof(partial_sig_b_adapted[1])); - CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], &final_sig_b, partial_sig_b_adapted, 2, NULL) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig_b, msg32_b, &combined_pk_b) == 1); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], final_sig_b, partial_sig_b_adapted, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_b, msg32_b, &combined_pk_b) == 1); /* Step 6: Signer 1 extracts adaptor from the published signature, applies it to * other partial signature, and takes A-coins. */ - CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, &final_sig_b, partial_sig_b, 2, nonce_is_negated_b) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, final_sig_b, partial_sig_b, 2, nonce_is_negated_b) == 1); CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, nonce_is_negated_a)); CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[1], &partial_sig_a[1])); - CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], &final_sig_a, partial_sig_a, 2, NULL) == 1); - CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig_a, msg32_a, &combined_pk_a) == 1); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], final_sig_a, partial_sig_a, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, &combined_pk_a) == 1); } /* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the @@ -753,93 +819,13 @@ void sha256_tag_test(void) { CHECK(memcmp(buf, buf2, 32) == 0); } - -void musig_tweak_test_helper(const secp256k1_pubkey* combined_pubkey, const unsigned char *ec_commit_tweak, const unsigned char *sk0, const unsigned char *sk1, const unsigned char *pk_hash) { - secp256k1_musig_session session[2]; - secp256k1_musig_session_signer_data signers0[2]; - secp256k1_musig_session_signer_data signers1[2]; - secp256k1_pubkey pk[2]; - unsigned char session_id[2][32]; - unsigned char msg[32]; - unsigned char nonce_commitment[2][32]; - secp256k1_pubkey nonce[2]; - const unsigned char *ncs[2]; - secp256k1_musig_partial_signature partial_sig[2]; - secp256k1_schnorrsig final_sig; - - secp256k1_rand256(session_id[0]); - secp256k1_rand256(session_id[1]); - secp256k1_rand256(msg); - - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk0) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk1) == 1); - - /* want to show that can both sign for Q and P */ - CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pk_hash, 2, 0, sk0) == 1); - CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pk_hash, 2, 1, sk1) == 1); - /* Set nonce commitments */ - ncs[0] = nonce_commitment[0]; - ncs[1] = nonce_commitment[1]; - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2, NULL) == 1); - CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2, NULL) == 1); - /* Set nonces */ - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], &nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[1]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], &nonce[0]) == 1); - CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], &nonce[1]) == 1); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); - CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); - CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); - CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[0], &signers0[1], &partial_sig[1], &pk[1]) == 1); - CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); - CHECK(secp256k1_musig_partial_sig_combine(ctx, &session[0], &final_sig, partial_sig, 2, ec_commit_tweak)); - CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig, msg, combined_pubkey) == 1); -} - -/* In this test we create a combined public key P and a commitment Q = P + - * hash(P, contract)*G. Then we test that we can sign for both public keys. In - * order to sign for Q we use the tweak32 argument of partial_sig_combine. */ -void musig_tweak_test(secp256k1_scratch_space *scratch) { - unsigned char sk[2][32]; - secp256k1_pubkey pk[2]; - unsigned char pk_hash[32]; - secp256k1_pubkey P; - unsigned char P_serialized[33]; - size_t compressed_size = 33; - secp256k1_pubkey Q; - - secp256k1_sha256 sha; - unsigned char contract[32]; - unsigned char ec_commit_tweak[32]; - - /* Setup */ - secp256k1_rand256(sk[0]); - secp256k1_rand256(sk[1]); - secp256k1_rand256(contract); - - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); - CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); - CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, pk_hash, pk, 2) == 1); - - CHECK(secp256k1_ec_pubkey_serialize(ctx, P_serialized, &compressed_size, &P, SECP256K1_EC_COMPRESSED) == 1); - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, P_serialized, 33); - secp256k1_sha256_write(&sha, contract, 32); - secp256k1_sha256_finalize(&sha, ec_commit_tweak); - memcpy(&Q, &P, sizeof(secp256k1_pubkey)); - CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &Q, ec_commit_tweak)); - - /* Test signing for P */ - musig_tweak_test_helper(&P, NULL, sk[0], sk[1], pk_hash); - /* Test signing for Q */ - musig_tweak_test_helper(&Q, ec_commit_tweak, sk[0], sk[1], pk_hash); -} - void run_musig_tests(void) { int i; secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); + for (i = 0; i < count; i++) { + musig_simple_test(scratch); + } musig_api_tests(scratch); musig_state_machine_tests(scratch); for (i = 0; i < count; i++) { @@ -847,7 +833,6 @@ void run_musig_tests(void) { scriptless_atomic_swap(scratch); } sha256_tag_test(); - musig_tweak_test(scratch); secp256k1_scratch_space_destroy(ctx, scratch); } diff --git a/src/secp256k1.c b/src/secp256k1.c index dee63ac9..fb4283f1 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -768,14 +768,22 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/ecdh/main_impl.h" #endif -#ifdef ENABLE_MODULE_MUSIG -# include "modules/musig/main_impl.h" -#endif - #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/main_impl.h" #endif +#ifdef ENABLE_MODULE_EXTRAKEYS +# include "modules/extrakeys/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORRSIG +# include "modules/schnorrsig/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_MUSIG +# include "modules/musig/main_impl.h" +#endif + #ifdef ENABLE_MODULE_GENERATOR # include "modules/generator/main_impl.h" #endif @@ -792,10 +800,3 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/surjection/main_impl.h" #endif -#ifdef ENABLE_MODULE_EXTRAKEYS -# include "modules/extrakeys/main_impl.h" -#endif - -#ifdef ENABLE_MODULE_SCHNORRSIG -# include "modules/schnorrsig/main_impl.h" -#endif From 96b9236c425125f348c15b6629b3a73c8a3062f5 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Wed, 14 Oct 2020 15:03:26 +0000 Subject: [PATCH 19/19] re-enable musig module --- Makefile.am | 6 +++--- configure.ac | 34 +++++++++++++++++----------------- contrib/travis.sh | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Makefile.am b/Makefile.am index 715f930f..1cba7a34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -151,9 +151,9 @@ if ENABLE_MODULE_ECDH include src/modules/ecdh/Makefile.am.include endif -#if ENABLE_MODULE_MUSIG -#include src/modules/musig/Makefile.am.include -#endif +if ENABLE_MODULE_MUSIG +include src/modules/musig/Makefile.am.include +endif if ENABLE_MODULE_RECOVERY include src/modules/recovery/Makefile.am.include diff --git a/configure.ac b/configure.ac index 6f177b00..e60583c9 100644 --- a/configure.ac +++ b/configure.ac @@ -131,10 +131,10 @@ AC_ARG_ENABLE(module_ecdh, [enable_module_ecdh=$enableval], [enable_module_ecdh=no]) -#AC_ARG_ENABLE(module_musig, -# AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), -# [enable_module_musig=$enableval], -# [enable_module_musig=no]) +AC_ARG_ENABLE(module_musig, + AS_HELP_STRING([--enable-module-musig],[enable MuSig module (experimental)]), + [enable_module_musig=$enableval], + [enable_module_musig=no]) AC_ARG_ENABLE(module_recovery, AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module [default=no]]), @@ -469,9 +469,9 @@ if test x"$enable_module_ecdh" = x"yes"; then AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) fi -#if test x"$enable_module_musig" = x"yes"; then -# AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) -#fi +if test x"$enable_module_musig" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module]) +fi if test x"$enable_module_recovery" = x"yes"; then AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) @@ -525,17 +525,17 @@ if test x"$enable_experimental" = x"yes"; then AC_MSG_NOTICE([Building range proof module: $enable_module_rangeproof]) AC_MSG_NOTICE([Building key whitelisting module: $enable_module_whitelist]) AC_MSG_NOTICE([Building surjection proof module: $enable_module_surjectionproof]) -# AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) + AC_MSG_NOTICE([Building MuSig module: $enable_module_musig]) AC_MSG_NOTICE([Building extrakeys module: $enable_module_extrakeys]) AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig]) AC_MSG_NOTICE([******]) -# if test x"$enable_module_schnorrsig" != x"yes"; then -# if test x"$enable_module_musig" = x"yes"; then -# AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.]) -# fi -# fi + if test x"$enable_module_schnorrsig" != x"yes"; then + if test x"$enable_module_musig" = x"yes"; then + AC_MSG_ERROR([MuSig module requires the schnorrsig module. Use --enable-module-schnorrsig to allow.]) + fi + fi if test x"$enable_module_generator" != x"yes"; then if test x"$enable_module_rangeproof" = x"yes"; then @@ -555,9 +555,9 @@ else if test x"$enable_module_ecdh" = x"yes"; then AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) fi -# if test x"$enable_module_musig" = x"yes"; then -# AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) -# fi + if test x"$enable_module_musig" = x"yes"; then + AC_MSG_ERROR([MuSig module is experimental. Use --enable-experimental to allow.]) + fi if test x"$enable_module_extrakeys" = x"yes"; then AC_MSG_ERROR([extrakeys module is experimental. Use --enable-experimental to allow.]) fi @@ -593,7 +593,7 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) -#AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_MUSIG], [test x"$enable_module_musig" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_GENERATOR], [test x"$enable_module_generator" = x"yes"]) AM_CONDITIONAL([ENABLE_MODULE_RANGEPROOF], [test x"$enable_module_rangeproof" = x"yes"]) diff --git a/contrib/travis.sh b/contrib/travis.sh index 25124822..fad6bc18 100755 --- a/contrib/travis.sh +++ b/contrib/travis.sh @@ -18,7 +18,7 @@ fi --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \ --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \ --enable-module-rangeproof="$RANGEPROOF" --enable-module-whitelist="$WHITELIST" --enable-module-generator="$GENERATOR" \ - --enable-module-schnorrsig="$SCHNORRSIG" \ + --enable-module-schnorrsig="$SCHNORRSIG" --enable-module-musig="$MUSIG" \ --host="$HOST" $EXTRAFLAGS if [ -n "$BUILD" ]