diff --git a/.travis.yml b/.travis.yml index 8d6c1f0b75..b1fa343f1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ compiler: - gcc env: global: - - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=no ASM=no BUILD=check EXTRAFLAGS= HOST= + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=no ASM=no BUILD=check EXTRAFLAGS= HOST= SCHNORR=no matrix: - SCALAR=32bit - SCALAR=64bit @@ -16,10 +16,10 @@ env: - FIELD=64bit ENDOMORPHISM=yes - FIELD=64bit ASM=x86_64 - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 - - FIELD=32bit + - FIELD=32bit SCHNORR=yes - FIELD=32bit ENDOMORPHISM=yes - BIGNUM=no - - BIGNUM=no ENDOMORPHISM=yes + - BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes - BIGNUM=no STATICPRECOMPUTATION=no - BUILD=distcheck - EXTRAFLAGS=CFLAGS=-DDETERMINISTIC diff --git a/Makefile.am b/Makefile.am index 2609c7feed..69337a95fa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,6 +2,9 @@ ACLOCAL_AMFLAGS = -I build-aux/m4 lib_LTLIBRARIES = libsecp256k1.la include_HEADERS = include/secp256k1.h +if ENABLE_MODULE_SCHNORR +include_HEADERS += include/secp256k1_schnorr.h +endif noinst_HEADERS = noinst_HEADERS += src/scalar.h noinst_HEADERS += src/scalar_4x64.h @@ -38,6 +41,8 @@ noinst_HEADERS += src/hash_impl.h noinst_HEADERS += src/field.h noinst_HEADERS += src/field_impl.h noinst_HEADERS += src/bench.h +noinst_HEADERS += src/schnorr.h +noinst_HEADERS += src/schnorr_impl.h pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libsecp256k1.pc @@ -50,6 +55,12 @@ libsecp256k1_la_LIBADD = $(SECP_LIBS) noinst_PROGRAMS = if USE_BENCHMARK noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal +if ENABLE_MODULE_SCHNORR +noinst_PROGRAMS += bench_schnorr_verify +bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c +bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_schnorr_verify_LDFLAGS = -static +endif bench_verify_SOURCES = src/bench_verify.c bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) bench_verify_LDFLAGS = -static diff --git a/configure.ac b/configure.ac index 70c9e593f6..8a17bd3457 100644 --- a/configure.ac +++ b/configure.ac @@ -102,6 +102,11 @@ AC_ARG_ENABLE(ecmult_static_precomputation, [use_ecmult_static_precomputation=$enableval], [use_ecmult_static_precomputation=yes]) +AC_ARG_ENABLE(module_schnorr, + AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (default is no)]), + [enable_module_schnorr=$enableval], + [enable_module_schnorr=no]) + AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto], [Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto]) @@ -315,6 +320,10 @@ if test x"$use_ecmult_static_precomputation" = x"yes"; then AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table]) fi +if test x"$enable_module_schnorr" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_SCHNORR, 1, [Define this symbol to enable the Schnorr signature module]) +fi + AC_C_BIGENDIAN() AC_MSG_NOTICE([Using assembly optimizations: $set_asm]) @@ -323,6 +332,8 @@ AC_MSG_NOTICE([Using bignum implementation: $set_bignum]) AC_MSG_NOTICE([Using scalar implementation: $set_scalar]) AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism]) +AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr]) + AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) AC_CONFIG_FILES([Makefile libsecp256k1.pc]) AC_SUBST(SECP_INCLUDES) @@ -332,6 +343,7 @@ AC_SUBST(SECP_TEST_INCLUDES) AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"]) AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$use_ecmult_static_precomputation" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_SCHNORR], [test x"$enable_module_schnorr" = x"yes"]) dnl make sure nothing new is exported so that we don't break the cache PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" diff --git a/include/secp256k1.h b/include/secp256k1.h index c570a5fde6..f3a8e3f55b 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -141,6 +141,8 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( * Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail. * 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) + * algo16: pointer to a 16-byte array describing the signature + * algorithm (will be NULL for ECDSA for compatibility). * attempt: how many iterations we have tried to find a nonce. * This will almost always be 0, but different attempt values * are required to result in a different nonce. @@ -153,6 +155,7 @@ typedef int (*secp256k1_nonce_function_t)( unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, + const unsigned char *algo16, unsigned int attempt, const void *data ); @@ -344,6 +347,23 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( const unsigned char *seed32 ) SECP256K1_ARG_NONNULL(1); +/** Add a number of public keys together. + * Returns: 1: the sum of the public keys is valid. + * 0: the sum of the public keys is not valid. + * In: ctx: pointer to a context object + * out: pointer to pubkey for placing the resulting public key + * (cannot be NULL) + * n: the number of public keys to add together (must be at least 1) + * ins: pointer to array of pointers to public keys (cannot be NULL) + * Use secp256k1_ec_pubkey_compress and secp256k1_ec_pubkey_decompress if the + * uncompressed format is needed. + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( + const secp256k1_context_t* ctx, + secp256k1_pubkey_t *out, + int n, + const secp256k1_pubkey_t * const * ins +) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); # ifdef __cplusplus } diff --git a/include/secp256k1_schnorr.h b/include/secp256k1_schnorr.h new file mode 100644 index 0000000000..ff7b977f40 --- /dev/null +++ b/include/secp256k1_schnorr.h @@ -0,0 +1,173 @@ +#ifndef _SECP256K1_SCHNORR_ +# define _SECP256K1_SCHNORR_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +/** Create a signature using a custom EC-Schnorr-SHA256 construction. It + * produces non-malleable 64-byte signatures which support public key recovery + * batch validation, and multiparty signing. + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was + * invalid. + * In: ctx: pointer to a context object, initialized for signing + * (cannot be NULL) + * 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_default is used + * ndata: pointer to arbitrary data used by the nonce generation + * function (can be NULL) + * Out: sig64: pointer to a 64-byte array where the signature will be + * placed (cannot be NULL) + */ +int secp256k1_schnorr_sign( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + unsigned char *sig64, + const unsigned char *seckey, + secp256k1_nonce_function_t noncefp, + const void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verify a signature created by secp256k1_schnorr_sign. + * Returns: 1: correct signature + * 0: incorrect signature + * In: ctx: a secp256k1 context object, initialized for verification. + * msg32: the 32-byte message hash being verified (cannot be NULL) + * sig64: the 64-byte signature being verified (cannot be NULL) + * pubkey: the public key to verify with (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_verify( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + const unsigned char *sig64, + const secp256k1_pubkey_t *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Recover an EC public key from a Schnorr signature created using + * secp256k1_schnorr_sign. + * Returns: 1: public key successfully recovered (which guarantees a correct + * signature). + * 0: otherwise. + * In: ctx: pointer to a context object, initialized for + * verification (cannot be NULL) + * msg32: the 32-byte message hash assumed to be signed (cannot + * be NULL) + * sig64: signature as 64 byte array (cannot be NULL) + * Out: pubkey: pointer to a pubkey to set to the recovered public key + * (cannot be NULL). + */ +int secp256k1_schnorr_recover( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + const unsigned char *sig64, + secp256k1_pubkey_t *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Generate a nonce pair deterministically for use with + * secp256k1_schnorr_partial_sign. + * Returns: 1: valid nonce pair was generated. + * 0: otherwise (nonce generation function failed) + * In: ctx: pointer to a context object, initialized for signing + * (cannot be NULL) + * msg32: the 32-byte message hash assumed to be signed (cannot + * be NULL) + * sec32: the 32-byte private key (cannot be NULL) + * noncefp: pointer to a nonce generation function. If NULL, + * secp256k1_nonce_function_default is used + * noncedata: pointer to arbitrary data used by the nonce generation + * function (can be NULL) + * Out: pubnonce: public side of the nonce (cannot be NULL) + * privnonce32: private side of the nonce (32 byte) (cannot be NULL) + * + * Do not use the output as a private/public key pair for signing/validation. + */ +int secp256k1_schnorr_generate_nonce_pair( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + const unsigned char *sec32, + secp256k1_nonce_function_t noncefp, + const void* noncedata, + secp256k1_pubkey_t *pubnonce, + unsigned char *privnonce32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); + +/** Produce a partial Schnorr signature, which can be combined using + * secp256k1_schnorr_partial_combine, to end up with a full signature that is + * verifiable using secp256k1_schnorr_verify. + * Returns: 1: signature created succesfully. + * 0: no valid signature exists with this combination of keys, nonces + * and message (chance around 1 in 2^128) + * -1: invalid private key, nonce, or public nonces. + * In: ctx: pointer to context object, initialized for signing (cannot + * be NULL) + * msg32: pointer to 32-byte message to sign + * sec32: pointer to 32-byte private key + * secnonce32: pointer to 32-byte array containing our nonce + * pubnonce_others: pointer to pubkey containing the sum of the other's + * nonces (see secp256k1_ec_pubkey_combine) + * Out: sig64: pointer to 64-byte array to put partial signature in + * + * The intended procedure for creating a multiparty signature is: + * - Each signer S[i] with private key x[i] and public key Q[i] runs + * secp256k1_schnorr_generate_nonce_pair to produce a pair (k[i],R[i]) of + * private/public nonces. + * - All signers communicate their public nonces to each other (revealing your + * private nonce can lead to discovery of your private key, so it should be + * considered secret). + * - All signers combine all the public nonces they received (excluding their + * own) using secp256k1_ec_pubkey_combine to obtain an + * Rall[i] = sum(R[0..i-1,i+1..n]). + * - All signers produce a partial signature using + * secp256k1_schnorr_partial_sign, passing in their own private key x[i], + * their own private nonce k[i], and the sum of the others' public nonces + * Rall[i]. + * - All signers communicate their partial signatures to each other. + * - Someone combines all partial signatures using + * secp256k1_schnorr_partial_combine, to obtain a full signature. + * - The resulting signature is validatable using secp256k1_schnorr_verify, with + * public key equal to the result of secp256k1_ec_pubkey_combine of the + * signers' public keys (sum(Q[0..n])). + * + * Note that secp256k1_schnorr_partial_combine and secp256k1_ec_pubkey_combine + * function take their arguments in any order, and it is possible to + * pre-combine several inputs already with one call, and add more inputs later + * by calling the function again (they are commutative and associative). + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_sign( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + unsigned char *sig64, + const unsigned char *sec32, + const unsigned char *secnonce32, + const secp256k1_pubkey_t *pubnonce_others +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + +/** Combine multiple Schnorr partial signatures. + * Returns: 1: the passed signatures were succesfully combined. + * 0: the resulting signature is not valid (chance of 1 in 2^256) + * -1: some inputs were invalid, or the signatures were not created + * using the same set of nonces + * In: ctx: pointer to a context object + * sig64: pointer to a 64-byte array to place the combined signature + * (cannot be NULL) + * n: the number of signatures to combine (at least 1) + * Out: sig64sin: pointer to an array of n pointers to 64-byte input + * signatures + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_combine( + const secp256k1_context_t* ctx, + unsigned char *sig64, + int n, + const unsigned char * const * sig64sin +) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/src/bench_schnorr_verify.c b/src/bench_schnorr_verify.c new file mode 100644 index 0000000000..274b6e02db --- /dev/null +++ b/src/bench_schnorr_verify.c @@ -0,0 +1,69 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * 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_schnorr.h" +#include "util.h" +#include "bench.h" + +typedef struct { + unsigned char key[32]; + unsigned char sig[64]; + unsigned char pubkey[33]; + int pubkeylen; +} benchmark_schnorr_sig_t; + +typedef struct { + secp256k1_context_t *ctx; + unsigned char msg[32]; + benchmark_schnorr_sig_t sigs[64]; + int numsigs; +} benchmark_schnorr_verify_t; + +static void benchmark_schnorr_init(void* arg) { + int i, k; + benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg; + + for (i = 0; i < 32; i++) data->msg[i] = 1 + i; + for (k = 0; k < data->numsigs; k++) { + secp256k1_pubkey_t pubkey; + for (i = 0; i < 32; i++) data->sigs[k].key[i] = 33 + i + k; + secp256k1_schnorr_sign(data->ctx, data->msg, data->sigs[k].sig, data->sigs[k].key, NULL, NULL); + data->sigs[k].pubkeylen = 33; + CHECK(secp256k1_ec_pubkey_create(data->ctx, &pubkey, data->sigs[k].key)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, data->sigs[k].pubkey, &data->sigs[k].pubkeylen, &pubkey, 1)); + } +} + +static void benchmark_schnorr_verify(void* arg) { + int i; + benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg; + + for (i = 0; i < 20000 / data->numsigs; i++) { + secp256k1_pubkey_t pubkey; + data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->sigs[0].pubkey, data->sigs[0].pubkeylen)); + CHECK(secp256k1_schnorr_verify(data->ctx, data->msg, data->sigs[0].sig, &pubkey) == ((i & 0xFF) == 0)); + data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF); + } +} + + + +int main(void) { + benchmark_schnorr_verify_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + data.numsigs = 1; + run_benchmark("schnorr_verify", benchmark_schnorr_verify, benchmark_schnorr_init, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/schnorr.h b/src/schnorr.h new file mode 100644 index 0000000000..34311ab0bc --- /dev/null +++ b/src/schnorr.h @@ -0,0 +1,20 @@ +/*********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php. * + ***********************************************************************/ + +#ifndef _SECP256K1_SCHNORR_ +#define _SECP256K1_SCHNORR_ + +#include "scalar.h" +#include "group.h" + +typedef void (*secp256k1_schnorr_msghash_t)(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32); + +static int secp256k1_schnorr_sig_sign(const secp256k1_ecmult_gen_context_t* ctx, unsigned char *sig64, const secp256k1_scalar_t *key, const secp256k1_scalar_t *nonce, const secp256k1_ge_t *pubnonce, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32); +static int secp256k1_schnorr_sig_verify(const secp256k1_ecmult_context_t* ctx, const unsigned char *sig64, const secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32); +static int secp256k1_schnorr_sig_recover(const secp256k1_ecmult_context_t* ctx, const unsigned char *sig64, secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32); +static int secp256k1_schnorr_sig_combine(unsigned char *sig64, int n, const unsigned char * const *sig64ins); + +#endif diff --git a/src/schnorr_impl.h b/src/schnorr_impl.h new file mode 100644 index 0000000000..e8e971fa93 --- /dev/null +++ b/src/schnorr_impl.h @@ -0,0 +1,207 @@ +/*********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php. * + ***********************************************************************/ + +#ifndef _SECP256K1_SCHNORR_IMPL_H_ +#define _SECP256K1_SCHNORR_IMPL_H_ + +#include + +#include "schnorr.h" +#include "num.h" +#include "field.h" +#include "group.h" +#include "ecmult.h" +#include "ecmult_gen.h" + +/** + * Custom Schnorr-based signature scheme. They support multiparty signing, public key + * recovery and batch validation. + * + * Rationale for verifying R's y coordinate: + * In order to support batch validation and public key recovery, the full R point must + * be known to verifiers, rather than just its x coordinate. In order to not risk + * being more strict in batch validation than normal validation, validators must be + * required to reject signatures with incorrect y coordinate. This is only possible + * by including a (relatively slow) field inverse, or a field square root. However, + * batch validation offers potentially much higher benefits than this cost. + * + * Rationale for having an implicit y coordinate oddness: + * If we commit to having the full R point known to verifiers, there are two mechanism. + * Either include its oddness in the signature, or give it an implicit fixed value. + * As the R y coordinate can be flipped by a simple negation of the nonce, we choose the + * latter, as it comes with nearly zero impact on signing or validation performance, and + * saves a byte in the signature. + * + * Signing: + * Inputs: 32-byte message m, 32-byte scalar key x (!=0), 32-byte scalar nonce k (!=0) + * + * Compute point R = k * G. Reject nonce if R's y coordinate is odd (or negate nonce). + * Compute 32-byte r, the serialization of R's x coordinate. + * Compute scalar h = Hash(r || m). Reject nonce if h == 0 or h >= order. + * Compute scalar s = k - h * x. + * The signature is (r, s). + * + * + * Verification: + * Inputs: 32-byte message m, public key point Q, signature: (32-byte r, scalar s) + * + * Signature is invalid if s >= order. + * Signature is invalid if r >= p. + * Compute scalar h = Hash(r || m). Signature is invalid if h == 0 or h >= order. + * Option 1 (faster for single verification): + * Compute point R = h * Q + s * G. Signature is invalid if R is infinity or R's y coordinate is odd. + * Signature is valid if the serialization of R's x coordinate equals r. + * Option 2 (allows batch validation and pubkey recovery): + * Decompress x coordinate r into point R, with odd y coordinate. Fail if R is not on the curve. + * Signature is valid if R + h * Q + s * G == 0. + */ + +static int secp256k1_schnorr_sig_sign(const secp256k1_ecmult_gen_context_t* ctx, unsigned char *sig64, const secp256k1_scalar_t *key, const secp256k1_scalar_t *nonce, const secp256k1_ge_t *pubnonce, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32) { + secp256k1_gej_t Rj; + secp256k1_ge_t Ra; + unsigned char h32[32]; + secp256k1_scalar_t h, s; + int overflow; + secp256k1_scalar_t n; + + if (secp256k1_scalar_is_zero(key) || secp256k1_scalar_is_zero(nonce)) { + return 0; + } + n = *nonce; + + secp256k1_ecmult_gen(ctx, &Rj, &n); + if (pubnonce) { + secp256k1_gej_add_ge(&Rj, &Rj, pubnonce); + } + secp256k1_ge_set_gej(&Ra, &Rj); + secp256k1_fe_normalize(&Ra.y); + if (secp256k1_fe_is_odd(&Ra.y)) { + /* R's y coordinate is odd, which is not allowed (see rationale above). + Force it to be even by negating the nonce. Note that this even works + for multiparty signing, as the R point is known to all participants, + which can all decide to flip the sign in unison, resulting in the + overall R point to be negated too. */ + secp256k1_scalar_negate(&n, &n); + } + secp256k1_fe_normalize(&Ra.x); + secp256k1_fe_get_b32(sig64, &Ra.x); + hash(h32, sig64, msg32); + overflow = 0; + secp256k1_scalar_set_b32(&h, h32, &overflow); + if (overflow || secp256k1_scalar_is_zero(&h)) { + secp256k1_scalar_clear(&n); + return 0; + } + secp256k1_scalar_mul(&s, &h, key); + secp256k1_scalar_negate(&s, &s); + secp256k1_scalar_add(&s, &s, &n); + secp256k1_scalar_clear(&n); + secp256k1_scalar_get_b32(sig64 + 32, &s); + return 1; +} + +static int secp256k1_schnorr_sig_verify(const secp256k1_ecmult_context_t* ctx, const unsigned char *sig64, const secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32) { + secp256k1_gej_t Qj, Rj; + secp256k1_ge_t Ra; + secp256k1_fe_t Rx; + secp256k1_scalar_t h, s; + unsigned char hh[32]; + int overflow; + + if (secp256k1_ge_is_infinity(pubkey)) { + return 0; + } + hash(hh, sig64, msg32); + overflow = 0; + secp256k1_scalar_set_b32(&h, hh, &overflow); + if (overflow || secp256k1_scalar_is_zero(&h)) { + return 0; + } + overflow = 0; + secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow); + if (overflow) { + return 0; + } + if (!secp256k1_fe_set_b32(&Rx, sig64)) { + return 0; + } + secp256k1_gej_set_ge(&Qj, pubkey); + secp256k1_ecmult(ctx, &Rj, &Qj, &h, &s); + if (secp256k1_gej_is_infinity(&Rj)) { + return 0; + } + secp256k1_ge_set_gej_var(&Ra, &Rj); + secp256k1_fe_normalize_var(&Ra.y); + if (secp256k1_fe_is_odd(&Ra.y)) { + return 0; + } + return secp256k1_fe_equal_var(&Rx, &Ra.x); +} + +static int secp256k1_schnorr_sig_recover(const secp256k1_ecmult_context_t* ctx, const unsigned char *sig64, secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32) { + secp256k1_gej_t Qj, Rj; + secp256k1_ge_t Ra; + secp256k1_fe_t Rx; + secp256k1_scalar_t h, s; + unsigned char hh[32]; + int overflow; + + hash(hh, sig64, msg32); + overflow = 0; + secp256k1_scalar_set_b32(&h, hh, &overflow); + if (overflow || secp256k1_scalar_is_zero(&h)) { + return 0; + } + overflow = 0; + secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow); + if (overflow) { + return 0; + } + if (!secp256k1_fe_set_b32(&Rx, sig64)) { + return 0; + } + if (!secp256k1_ge_set_xo_var(&Ra, &Rx, 0)) { + return 0; + } + secp256k1_gej_set_ge(&Rj, &Ra); + secp256k1_scalar_inverse_var(&h, &h); + secp256k1_scalar_negate(&s, &s); + secp256k1_scalar_mul(&s, &s, &h); + secp256k1_ecmult(ctx, &Qj, &Rj, &h, &s); + if (secp256k1_gej_is_infinity(&Qj)) { + return 0; + } + secp256k1_ge_set_gej(pubkey, &Qj); + return 1; +} + +static int secp256k1_schnorr_sig_combine(unsigned char *sig64, int n, const unsigned char * const *sig64ins) { + secp256k1_scalar_t s = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + int i; + for (i = 0; i < n; i++) { + secp256k1_scalar_t si; + int overflow; + secp256k1_scalar_set_b32(&si, sig64ins[i] + 32, &overflow); + if (overflow) { + return -1; + } + if (i) { + if (memcmp(sig64ins[i - 1], sig64ins[i], 32) != 0) { + return -1; + } + } + secp256k1_scalar_add(&s, &s, &si); + } + if (secp256k1_scalar_is_zero(&s)) { + return 0; + } + memcpy(sig64, sig64ins[0], 32); + secp256k1_scalar_get_b32(sig64 + 32, &s); + secp256k1_scalar_clear(&s); + return 1; +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index 752ba3362f..147c291053 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -16,6 +16,7 @@ #include "ecmult_impl.h" #include "ecmult_gen_impl.h" #include "ecdsa_impl.h" +#include "schnorr_impl.h" #include "eckey_impl.h" #include "hash_impl.h" @@ -122,20 +123,29 @@ int secp256k1_ecdsa_verify(const secp256k1_context_t* ctx, const unsigned char * return ret; } -static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) { - unsigned char keydata[96]; +static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, unsigned int counter, const void *data) { + unsigned char keydata[112]; + int keylen = 64; secp256k1_rfc6979_hmac_sha256_t rng; unsigned int i; /* We feed a byte array to the PRNG as input, consisting of: * - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d. * - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data. + * - optionally 16 extra bytes with the algorithm name (the extra data bytes + * are set to zeroes when not present, while the algorithm name is). */ memcpy(keydata, key32, 32); memcpy(keydata + 32, msg32, 32); if (data != NULL) { memcpy(keydata + 64, data, 32); + keylen = 96; } - secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, data != NULL ? 96 : 64); + if (algo16 != NULL) { + memset(keydata + keylen, 0, 96 - keylen); + memcpy(keydata + 96, algo16, 16); + keylen = 112; + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen); memset(keydata, 0, sizeof(keydata)); for (i = 0; i <= counter; i++) { secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); @@ -169,7 +179,7 @@ int secp256k1_ecdsa_sign(const secp256k1_context_t* ctx, const unsigned char *ms secp256k1_scalar_set_b32(&msg, msg32, NULL); while (1) { unsigned char nonce32[32]; - ret = noncefp(nonce32, msg32, seckey, count, noncedata); + ret = noncefp(nonce32, msg32, seckey, NULL, count, noncedata); if (!ret) { break; } @@ -216,7 +226,7 @@ int secp256k1_ecdsa_sign_compact(const secp256k1_context_t* ctx, const unsigned secp256k1_scalar_set_b32(&msg, msg32, NULL); while (1) { unsigned char nonce32[32]; - ret = noncefp(nonce32, msg32, seckey, count, noncedata); + ret = noncefp(nonce32, msg32, seckey, NULL, count, noncedata); if (!ret) { break; } @@ -439,3 +449,179 @@ int secp256k1_context_randomize(secp256k1_context_t* ctx, const unsigned char *s secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); return 1; } + + +int secp256k1_ec_pubkey_combine(const secp256k1_context_t* ctx, secp256k1_pubkey_t *pubnonce, int n, const secp256k1_pubkey_t * const *pubnonces) { + int i; + secp256k1_gej_t Qj; + secp256k1_ge_t Q; + + (void)ctx; + DEBUG_CHECK(pubnonce != NULL); + DEBUG_CHECK(n >= 1); + DEBUG_CHECK(pubnonces != NULL); + + secp256k1_gej_set_infinity(&Qj); + + for (i = 0; i < n; i++) { + secp256k1_pubkey_load(&Q, pubnonces[i]); + secp256k1_gej_add_ge(&Qj, &Qj, &Q); + } + if (secp256k1_gej_is_infinity(&Qj)) { + memset(pubnonce, 0, sizeof(*pubnonce)); + return 0; + } + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(pubnonce, &Q); + return 1; +} + +#ifdef ENABLE_MODULE_SCHNORR +void secp256k1_schnorr_msghash_sha256(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) { + secp256k1_sha256_t sha; + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, r32, 32); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, h32); +} + +static const unsigned char secp256k1_schnorr_algo16[16] = "Schnorr+SHA256 "; + +int secp256k1_schnorr_sign(const secp256k1_context_t* ctx, const unsigned char *msg32, unsigned char *sig64, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void* noncedata) { + secp256k1_scalar_t sec, non; + int ret = 0; + int overflow = 0; + unsigned int count = 0; + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + DEBUG_CHECK(msg32 != NULL); + DEBUG_CHECK(sig64 != NULL); + DEBUG_CHECK(seckey != NULL); + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_default; + } + + secp256k1_scalar_set_b32(&sec, seckey, NULL); + while (1) { + unsigned char nonce32[32]; + ret = noncefp(nonce32, msg32, seckey, secp256k1_schnorr_algo16, count, noncedata); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&non, nonce32, &overflow); + memset(nonce32, 0, 32); + if (!secp256k1_scalar_is_zero(&non) && !overflow) { + if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, NULL, secp256k1_schnorr_msghash_sha256, msg32)) { + break; + } + } + count++; + } + secp256k1_scalar_clear(&non); + secp256k1_scalar_clear(&sec); + return ret; +} + +int secp256k1_schnorr_verify(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig64, const secp256k1_pubkey_t *pubkey) { + secp256k1_ge_t q; + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + DEBUG_CHECK(msg32 != NULL); + DEBUG_CHECK(sig64 != NULL); + DEBUG_CHECK(pubkey != NULL); + + secp256k1_pubkey_load(&q, pubkey); + return secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32); +} + +int secp256k1_schnorr_recover(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig64, secp256k1_pubkey_t *pubkey) { + secp256k1_ge_t q; + + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + DEBUG_CHECK(msg32 != NULL); + DEBUG_CHECK(sig64 != NULL); + DEBUG_CHECK(pubkey != NULL); + + if (!secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32)) { + memset(pubkey, 0, sizeof(*pubkey)); + return 0; + } + secp256k1_pubkey_save(pubkey, &q); + return 1; +} + +int secp256k1_schnorr_generate_nonce_pair(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sec32, secp256k1_nonce_function_t noncefp, const void* noncedata, secp256k1_pubkey_t *pubnonce, unsigned char *privnonce32) { + int count = 0; + int ret = 1; + secp256k1_gej_t Qj; + secp256k1_ge_t Q; + secp256k1_scalar_t sec; + + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + DEBUG_CHECK(msg32 != NULL); + DEBUG_CHECK(sec32 != NULL); + DEBUG_CHECK(pubnonce != NULL); + DEBUG_CHECK(privnonce32 != NULL); + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_default; + } + + do { + int overflow; + ret = noncefp(privnonce32, msg32, sec32, secp256k1_schnorr_algo16, count++, noncedata); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&sec, privnonce32, &overflow); + if (overflow || secp256k1_scalar_is_zero(&sec)) { + continue; + } + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sec); + secp256k1_ge_set_gej(&Q, &Qj); + + secp256k1_pubkey_save(pubnonce, &Q); + break; + } while(1); + + secp256k1_scalar_clear(&sec); + if (!ret) { + memset(pubnonce, 0, sizeof(*pubnonce)); + } + return ret; +} + +int secp256k1_schnorr_partial_sign(const secp256k1_context_t* ctx, const unsigned char *msg32, unsigned char *sig64, const unsigned char *sec32, const unsigned char *secnonce32, const secp256k1_pubkey_t *pubnonce_others) { + int overflow = 0; + secp256k1_scalar_t sec, non; + secp256k1_ge_t pubnon; + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + DEBUG_CHECK(msg32 != NULL); + DEBUG_CHECK(sig64 != NULL); + DEBUG_CHECK(sec32 != NULL); + DEBUG_CHECK(secnonce32 != NULL); + DEBUG_CHECK(pubnonce_others != NULL); + + secp256k1_scalar_set_b32(&sec, sec32, &overflow); + if (overflow || secp256k1_scalar_is_zero(&sec)) { + return -1; + } + secp256k1_scalar_set_b32(&non, secnonce32, &overflow); + if (overflow || secp256k1_scalar_is_zero(&non)) { + return -1; + } + secp256k1_pubkey_load(&pubnon, pubnonce_others); + return secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, &pubnon, secp256k1_schnorr_msghash_sha256, msg32); +} + +int secp256k1_schnorr_partial_combine(const secp256k1_context_t* ctx, unsigned char *sig64, int n, const unsigned char * const *sig64sin) { + (void)ctx; + DEBUG_CHECK(sig64 != NULL); + DEBUG_CHECK(n >= 1); + DEBUG_CHECK(sig64sin != NULL); + return secp256k1_schnorr_sig_combine(sig64, n, sig64sin); +} +#endif diff --git a/src/tests.c b/src/tests.c index df9adeca1f..d2c8d101f2 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1252,6 +1252,38 @@ void run_ge(void) { test_add_neg_y_diff_x(); } +void test_ec_combine(void) { + secp256k1_scalar_t sum = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_pubkey_t data[6]; + const secp256k1_pubkey_t* d[6]; + secp256k1_pubkey_t sd; + secp256k1_pubkey_t sd2; + secp256k1_gej_t Qj; + secp256k1_ge_t Q; + int i; + for (i = 1; i <= 6; i++) { + secp256k1_scalar_t s; + random_scalar_order_test(&s); + secp256k1_scalar_add(&sum, &sum, &s); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &s); + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(&data[i - 1], &Q); + d[i - 1] = &data[i - 1]; + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sum); + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(&sd, &Q); + CHECK(secp256k1_ec_pubkey_combine(ctx, &sd2, i, d) == 1); + CHECK(memcmp(&sd, &sd2, sizeof(sd)) == 0); + } +} + +void run_ec_combine(void) { + int i; + for (i = 0; i < count * 8; i++) { + test_ec_combine(); + } +} + /***** ECMULT TESTS *****/ void run_ecmult_chain(void) { @@ -1525,22 +1557,23 @@ void run_ecdsa_sign_verify(void) { } /** Dummy nonce generation function that just uses a precomputed nonce, and fails if it is not accepted. Use only for testing. */ -static int precomputed_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) { +static int precomputed_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, unsigned int counter, const void *data) { (void)msg32; (void)key32; + (void)algo16; memcpy(nonce32, data, 32); return (counter == 0); } -static int nonce_function_test_fail(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) { +static int nonce_function_test_fail(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, unsigned int counter, const void *data) { /* Dummy nonce generator that has a fatal error on the first counter value. */ if (counter == 0) { return 0; } - return nonce_function_rfc6979(nonce32, msg32, key32, counter - 1, data); + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, counter - 1, data); } -static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) { +static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, unsigned int counter, const void *data) { /* Dummy nonce generator that produces unacceptable nonces for the first several counter values. */ if (counter < 3) { memset(nonce32, counter==0 ? 0 : 255, 32); @@ -1567,7 +1600,7 @@ static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char if (counter > 5) { return 0; } - return nonce_function_rfc6979(nonce32, msg32, key32, counter - 5, data); + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, counter - 5, data); } int is_empty_compact_signature(const unsigned char *sig64) { @@ -1581,6 +1614,9 @@ void test_ecdsa_end_to_end(void) { unsigned char message[32]; unsigned char privkey2[32]; unsigned char csignature[64]; +#ifdef ENABLE_MODULE_SCHNORR + unsigned char schnorr_signature[64]; +#endif unsigned char signature[72]; unsigned char signature2[72]; unsigned char signature3[72]; @@ -1691,6 +1727,19 @@ void test_ecdsa_end_to_end(void) { csignature[secp256k1_rand32() % 64] += 1 + (secp256k1_rand32() % 255); CHECK(secp256k1_ecdsa_recover_compact(ctx, message, csignature, &recpubkey, recid) != 1 || memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0); + +#ifdef ENABLE_MODULE_SCHNORR + /* Schnorr sign. */ + CHECK(secp256k1_schnorr_sign(ctx, message, schnorr_signature, privkey, NULL, NULL) == 1); + CHECK(secp256k1_schnorr_verify(ctx, message, schnorr_signature, &pubkey) == 1); + CHECK(secp256k1_schnorr_recover(ctx, message, schnorr_signature, &recpubkey) == 1); + CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0); + /* Destroy signature and verify again. */ + schnorr_signature[secp256k1_rand32() % 64] += 1 + (secp256k1_rand32() % 255); + CHECK(secp256k1_schnorr_verify(ctx, message, schnorr_signature, &pubkey) == 0); + CHECK(secp256k1_schnorr_recover(ctx, message, schnorr_signature, &recpubkey) != 1 || + memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0); +#endif } void test_random_pubkeys(void) { @@ -2088,6 +2137,142 @@ void run_ecdsa_edge_cases(void) { test_ecdsa_edge_cases(); } +#ifdef ENABLE_MODULE_SCHNORR +/** Horribly broken hash function. Do not use for anything but tests. */ +void test_schnorr_hash(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) { + int i; + for (i = 0; i < 32; i++) { + h32[i] = r32[i] ^ msg32[i]; + } +} + +void test_schnorr_sign_verify(void) { + unsigned char msg32[32]; + unsigned char sig64[3][64]; + secp256k1_gej_t pubkeyj[3]; + secp256k1_ge_t pubkey[3]; + secp256k1_scalar_t nonce[3], key[3]; + int i = 0; + int k; + + secp256k1_rand256_test(msg32); + + for (k = 0; k < 3; k++) { + random_scalar_order_test(&key[k]); + + do { + random_scalar_order_test(&nonce[k]); + if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64[k], &key[k], &nonce[k], NULL, &test_schnorr_hash, msg32)) { + break; + } + } while(1); + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj[k], &key[k]); + secp256k1_ge_set_gej_var(&pubkey[k], &pubkeyj[k]); + CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32)); + + for (i = 0; i < 4; i++) { + int pos = secp256k1_rand32() % 64; + int mod = 1 + (secp256k1_rand32() % 255); + sig64[k][pos] ^= mod; + CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32) == 0); + sig64[k][pos] ^= mod; + } + } +} + +void test_schnorr_threshold(void) { + unsigned char msg[32]; + unsigned char sec[5][32]; + secp256k1_pubkey_t pub[5]; + unsigned char nonce[5][32]; + secp256k1_pubkey_t pubnonce[5]; + unsigned char sig[5][64]; + const unsigned char* sigs[5]; + unsigned char allsig[64]; + const secp256k1_pubkey_t* pubs[5]; + secp256k1_pubkey_t allpub; + int n, i; + int damage; + int ret = 0; + + damage = (secp256k1_rand32() % 2) ? (1 + (secp256k1_rand32() % 4)) : 0; + secp256k1_rand256_test(msg); + n = 2 + (secp256k1_rand32() % 4); + for (i = 0; i < n; i++) { + do { + secp256k1_rand256_test(sec[i]); + } while (!secp256k1_ec_seckey_verify(ctx, sec[i])); + CHECK(secp256k1_ec_pubkey_create(ctx, &pub[i], sec[i])); + CHECK(secp256k1_schnorr_generate_nonce_pair(ctx, msg, sec[i], NULL, NULL, &pubnonce[i], nonce[i])); + pubs[i] = &pub[i]; + } + if (damage == 1) { + nonce[secp256k1_rand32() % n][secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255); + } else if (damage == 2) { + sec[secp256k1_rand32() % n][secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255); + } + for (i = 0; i < n; i++) { + secp256k1_pubkey_t allpubnonce; + const secp256k1_pubkey_t *pubnonces[4]; + int j; + for (j = 0; j < i; j++) { + pubnonces[j] = &pubnonce[j]; + } + for (j = i + 1; j < n; j++) { + pubnonces[j - 1] = &pubnonce[j]; + } + CHECK(secp256k1_ec_pubkey_combine(ctx, &allpubnonce, n - 1, pubnonces)); + ret |= (secp256k1_schnorr_partial_sign(ctx, msg, sig[i], sec[i], nonce[i], &allpubnonce) != 1) * 1; + sigs[i] = sig[i]; + } + if (damage == 3) { + sig[secp256k1_rand32() % n][secp256k1_rand32() % 64] ^= 1 + (secp256k1_rand32() % 255); + } + ret |= (secp256k1_ec_pubkey_combine(ctx, &allpub, n, pubs) != 1) * 2; + ret |= (secp256k1_schnorr_partial_combine(ctx, allsig, n, sigs) != 1) * 4; + if (damage == 4) { + allsig[secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255); + } + ret |= (secp256k1_schnorr_verify(ctx, msg, allsig, &allpub) != 1) * 8; + CHECK((ret == 0) == (damage == 0)); +} + +void test_schnorr_recovery(void) { + unsigned char msg32[32]; + unsigned char sig64[64]; + secp256k1_ge_t Q; + + secp256k1_rand256_test(msg32); + secp256k1_rand256_test(sig64); + secp256k1_rand256_test(sig64 + 32); + if (secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1) { + CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1); + } +} + +void run_schnorr_sign_verify(void) { + int i; + for (i = 0; i < 32 * count; i++) { + test_schnorr_sign_verify(); + } +} + +void run_schnorr_recovery(void) { + int i; + for (i = 0; i < 16 * count; i++) { + test_schnorr_recovery(); + } +} + +void run_schnorr_threshold(void) { + int i; + for (i = 0; i < 10 * count; i++) { + test_schnorr_threshold(); + } +} +#endif + #ifdef ENABLE_OPENSSL_TESTS EC_KEY *get_openssl_key(const secp256k1_scalar_t *key) { unsigned char privkey[300]; @@ -2223,6 +2408,7 @@ int main(int argc, char **argv) { run_ecmult_chain(); run_ecmult_constants(); run_ecmult_gen_blind(); + run_ec_combine(); /* ecdsa tests */ run_random_pubkeys(); @@ -2233,6 +2419,13 @@ int main(int argc, char **argv) { run_ecdsa_openssl(); #endif +#ifdef ENABLE_MODULE_SCHNORR + /* Schnorr tests */ + run_schnorr_sign_verify(); + run_schnorr_recovery(); + run_schnorr_threshold(); +#endif + secp256k1_rand256(run32); printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]);