diff --git a/Makefile.am b/Makefile.am index cc15338b7e..3ef756c2dd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,6 +38,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 @@ -49,10 +51,13 @@ libsecp256k1_la_LIBADD = $(SECP_LIBS) noinst_PROGRAMS = if USE_BENCHMARK -noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal +noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal bench_schnorr_verify bench_verify_SOURCES = src/bench_verify.c bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) bench_verify_LDFLAGS = -static +bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c +bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_schnorr_verify_LDFLAGS = -static bench_recover_SOURCES = src/bench_recover.c bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS) bench_recover_LDFLAGS = -static diff --git a/include/secp256k1.h b/include/secp256k1.h index 06afd4c65b..8fc3daeceb 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -339,6 +339,9 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( const unsigned char *seed32 ) SECP256K1_ARG_NONNULL(1); +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); +int secp256k1_schnorr_verify(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig64, const unsigned char *pubkey, int pubkeylen); +int secp256k1_schnorr_verify_batch(const secp256k1_context_t* ctx, int n, const unsigned char *msg32, const unsigned char **sig64, const unsigned char **pubkey, const int *pubkeylen); # ifdef __cplusplus } diff --git a/src/bench_schnorr_verify.c b/src/bench_schnorr_verify.c new file mode 100644 index 0000000000..71af58ee44 --- /dev/null +++ b/src/bench_schnorr_verify.c @@ -0,0 +1,96 @@ +/********************************************************************** + * 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 "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++) { + 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, data->sigs[k].pubkey, &data->sigs[k].pubkeylen, data->sigs[k].key, 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++) { + data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF); + CHECK(secp256k1_schnorr_verify(data->ctx, data->msg, data->sigs[0].sig, data->sigs[0].pubkey, data->sigs[0].pubkeylen) == ((i & 0xFF) == 0)); + data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF); + } +} + +static void benchmark_schnorr_verify_batch(void* arg) { + int i, k; + benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg; + + const unsigned char *sig_ptr[64]; + const unsigned char *pubkey_ptr[64]; + int pubkeylen[64]; + + for (k = 0; k < data->numsigs; k++) { + sig_ptr[k] = &data->sigs[k].sig[0]; + pubkey_ptr[k] = &data->sigs[k].pubkey[0]; + pubkeylen[k] = data->sigs[k].pubkeylen; + } + + for (i = 0; i < 20000 / data->numsigs; i++) { + data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF); + CHECK(secp256k1_schnorr_verify_batch(data->ctx, data->numsigs, data->msg, sig_ptr, pubkey_ptr, pubkeylen) == ((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); + run_benchmark("schnorr_verify_batch1", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000); + data.numsigs = 2; + run_benchmark("schnorr_verify_batch2", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000); + data.numsigs = 4; + run_benchmark("schnorr_verify_batch4", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000); + data.numsigs = 8; + run_benchmark("schnorr_verify_batch8", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000); + data.numsigs = 16; + run_benchmark("schnorr_verify_batch16", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000); + data.numsigs = 32; + run_benchmark("schnorr_verify_batch32", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/src/ecmult.h b/src/ecmult.h index bab9e4ef52..a8e145d17d 100644 --- a/src/ecmult.h +++ b/src/ecmult.h @@ -28,4 +28,8 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context_t *c /** Double multiply: R = na*A + ng*G */ static void secp256k1_ecmult(const secp256k1_ecmult_context_t *ctx, secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng); +static void secp256k1_ecmult_mult(const secp256k1_ecmult_context_t *ctx, int points, secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng); + +#define MAX_MULTI 64 + #endif diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h index 771d398823..4e8039afa2 100644 --- a/src/ecmult_impl.h +++ b/src/ecmult_impl.h @@ -84,6 +84,38 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej_t *prej, s #endif } +/** Fill a table 'prej' with a concatenation of precomputed off multiples of the + * points in a. Prej will contain the values + * [1*a[0],3*a[0],...,(2*n-1)*a[0],1*a[1],3*a[1],...,(2*n-1)*a[k-1]], so it + * needs space for k * n values. zr[0] will contain prej[0].z / a[0].z. The + * other zr[i] values = prej[i].z / prej[i-1].z. */ +static void secp256k1_ecmult_multi_odd_multiples_table(int k, int n, secp256k1_gej_t *prej, secp256k1_fe_t *zr, const secp256k1_gej_t *a) { + int j; + for (j = 0; j < k; j++) { + secp256k1_gej_t aa; + secp256k1_fe_t z2, z3; + if (j != 0) { + /* Make the Z coordinate of each input a known multiple of the + * last prej output of the previous input point. */ + secp256k1_fe_sqr(&z2, &prej[n * j - 1].z); + secp256k1_fe_mul(&z3, &z2, &prej[n * j - 1].z); + secp256k1_fe_mul(&aa.x, &a[j].x, &z2); + secp256k1_fe_mul(&aa.y, &a[j].y, &z3); + secp256k1_fe_mul(&aa.z, &a[j].z, &prej[n * j - 1].z); + aa.infinity = 0; + } else { + aa = a[0]; + } + secp256k1_ecmult_odd_multiples_table(n, &prej[n * j], &zr[n * j], &aa); + if (j != 0) { + /* Correct the first Z ratio output of this point, by multiplying it + * with the current point's input Z coordinate, chaining them + * together */ + secp256k1_fe_mul(zr + n * j, zr + n * j, &a[j].z); + } + } +} + /** Fill a table 'pre' with precomputed odd multiples of a. * * There are two versions of this function: @@ -91,6 +123,10 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej_t *prej, s * resulting point set to a single constant Z denominator, stores the X and Y * coordinates as ge_storage points in pre, and stores the global Z in rz. * It only operates on tables sized for WINDOW_A wnaf multiples. + * - secp256k1_ecmult_multi_odd_multiples_table_globalz_windowa which is + * identical to secp256k1_ecmult_odd_multiples_table_globalz_windowa, but + * works on several input points at once, and brings them all to a single + * global Z. * - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its * resulting point set to actually affine points, and stores those in pre. * It operates on tables of any size, but uses heap-allocated temporaries. @@ -109,6 +145,16 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge_t secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr); } +static void secp256k1_ecmult_multi_odd_multiples_table_globalz_windowa(int k, secp256k1_ge_t *pre, secp256k1_fe_t *globalz, const secp256k1_gej_t *a) { + secp256k1_gej_t prej[MAX_MULTI * ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_fe_t zr[MAX_MULTI * ECMULT_TABLE_SIZE(WINDOW_A)]; + + /* Compute the odd multiples of all inputs in Jacobian form. */ + secp256k1_ecmult_multi_odd_multiples_table(k, ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a); + /* Bring them to the same Z denominator. */ + secp256k1_ge_globalz_set_table_gej(k * ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr); +} + static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage_t *pre, const secp256k1_gej_t *a) { secp256k1_gej_t *prej = checked_malloc(sizeof(secp256k1_gej_t) * n); secp256k1_ge_t *prea = checked_malloc(sizeof(secp256k1_ge_t) * n); @@ -389,4 +435,121 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context_t *ctx, secp256k1_ge } } +static void secp256k1_ecmult_mult(const secp256k1_ecmult_context_t *ctx, int points, secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng) { + secp256k1_ge_t pre_a[MAX_MULTI][ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge_t tmpa; + secp256k1_fe_t Z; +#ifdef USE_ENDOMORPHISM + secp256k1_ge_t pre_a_lam[MAX_MULTI][ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_scalar_t na_1[MAX_MULTI], na_lam[MAX_MULTI]; + /* Splitted G factors. */ + secp256k1_scalar_t ng_1, ng_128; + int wnaf_na_1[MAX_MULTI][130]; + int wnaf_na_lam[MAX_MULTI][130]; + int bits_na_1[MAX_MULTI]; + int bits_na_lam[MAX_MULTI]; + int wnaf_ng_1[129]; + int bits_ng_1; + int wnaf_ng_128[129]; + int bits_ng_128; +#else + int wnaf_na[MAX_MULTI][256]; + int bits_na[MAX_MULTI]; + int wnaf_ng[257]; + int bits_ng; +#endif + int i; + int bits = 0; + int k; + + VERIFY_CHECK(points >= 1); + VERIFY_CHECK(points <= MAX_MULTI); + + for (k = 0; k < points; k++) { +#ifdef USE_ENDOMORPHISM + /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ + secp256k1_scalar_split_lambda_var(&na_1[k], &na_lam[k], &na[k]); + + /* build wnaf representation for na_1 and na_lam. */ + bits_na_1[k] = secp256k1_ecmult_wnaf(wnaf_na_1[k], &na_1[k], WINDOW_A); + bits_na_lam[k] = secp256k1_ecmult_wnaf(wnaf_na_lam[k], &na_lam[k], WINDOW_A); + VERIFY_CHECK(bits_na_1[k] <= 130); + VERIFY_CHECK(bits_na_lam[k] <= 130); + if (bits_na_1[k] > bits) bits = bits_na_1[k]; + if (bits_na_lam[k] > bits) bits = bits_na_lam[k]; +#else + /* build wnaf representation for na. */ + bits_na[k] = secp256k1_ecmult_wnaf(wnaf_na[k], &na[k], WINDOW_A); + if (bits_na[k] > bits) bits = bits_na[k]; +#endif + } + + /* calculate odd multiples of all a's */ + secp256k1_ecmult_multi_odd_multiples_table_globalz_windowa(points, &pre_a[0][0], &Z, a); + +#ifdef USE_ENDOMORPHISM + for (k = 0; k < points; k++) { + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&pre_a_lam[k][i], &pre_a[k][i]); + } + } +#endif + +#ifdef USE_ENDOMORPHISM + /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */ + secp256k1_scalar_split_128(&ng_1, &ng_128, ng); + + /* Build wnaf representation for ng_1 and ng_128 */ + bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, &ng_1, WINDOW_G); + bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, &ng_128, WINDOW_G); + if (bits_ng_1 > bits) bits = bits_ng_1; + if (bits_ng_128 > bits) bits = bits_ng_128; +#else + bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, ng, WINDOW_G); + if (bits_ng > bits) bits = bits_ng; +#endif + + secp256k1_gej_set_infinity(r); + + for (i = bits-1; i >= 0; i--) { + int n; + secp256k1_gej_double_var(r, r, NULL); +#ifdef USE_ENDOMORPHISM + for (k = 0; k < points; k++) { + if (i < bits_na_1[k] && (n = wnaf_na_1[k][i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a[k], n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < bits_na_lam[k] && (n = wnaf_na_lam[k][i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam[k], n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + } + if (i < bits_ng_1 && (n = wnaf_ng_1[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } + if (i < bits_ng_128 && (n = wnaf_ng_128[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g_128, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } +#else + for (k = 0; k < points; k++) { + if (i < bits_na[k] && (n = wnaf_na[k][i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a[k], n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + } + if (i < bits_ng && (n = wnaf_ng[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } +#endif + } + + if (!r->infinity) { + secp256k1_fe_mul(&r->z, &r->z, &Z); + } +} + #endif diff --git a/src/schnorr.h b/src/schnorr.h new file mode 100644 index 0000000000..8f403b8d29 --- /dev/null +++ b/src/schnorr.h @@ -0,0 +1,19 @@ +/*********************************************************************** + * Copyright (c) 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, 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_verify_batch(const secp256k1_ecmult_context_t* ctx, int n, unsigned char (* const sig64)[64], const secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32); + +#endif diff --git a/src/schnorr_impl.h b/src/schnorr_impl.h new file mode 100644 index 0000000000..cc48297650 --- /dev/null +++ b/src/schnorr_impl.h @@ -0,0 +1,223 @@ +/*********************************************************************** + * Copyright (c) 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: + * + * 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. + * 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. + * 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, 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; + + if (secp256k1_scalar_is_zero(key) || secp256k1_scalar_is_zero(nonce)) { + return 0; + } + + secp256k1_ecmult_gen(ctx, &Rj, nonce); + secp256k1_ge_set_gej(&Ra, &Rj); + secp256k1_fe_normalize(&Ra.y); + secp256k1_fe_normalize(&Ra.x); + if (secp256k1_fe_is_odd(&Ra.y)) { + return 0; + } + 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)) { + return 0; + } + secp256k1_scalar_mul(&s, &h, key); + secp256k1_scalar_negate(&s, &s); + secp256k1_scalar_add(&s, &s, nonce); + 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 const secp256k1_scalar_t secp256k1_schnorr_batch_coefs[] = { + SECP256K1_SCALAR_CONST(1,1,2,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,3,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,5,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,7,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,9,1,1), + SECP256K1_SCALAR_CONST(1,3,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,5,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,7,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,9,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,8,1), + SECP256K1_SCALAR_CONST(1,1,3,1,2,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,2,1,4,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1), + SECP256K1_SCALAR_CONST(1,1,1,1,1,1,1,1) +}; + + +static int secp256k1_schnorr_sig_verify_batch(const secp256k1_ecmult_context_t* ctx, int n, unsigned char (* const sig64)[64], const secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32) { + secp256k1_gej_t S; + secp256k1_gej_t P[MAX_MULTI]; + secp256k1_ge_t R0; + secp256k1_scalar_t f[MAX_MULTI]; + secp256k1_scalar_t ss; + secp256k1_fe_t Rx; + + unsigned char hh[32]; + int overflow; + int k; + + VERIFY_CHECK(n * 2 - 1 <= MAX_MULTI); + VERIFY_CHECK(n >= 1); + + secp256k1_scalar_set_int(&f[0], 1); + + for (k = 0; k < n; k++) { + secp256k1_scalar_t s; + secp256k1_ge_t Ra; + + if (k) { + f[k - 1] = secp256k1_schnorr_batch_coefs[k - 1]; + } + + /* Find Rs (into the first half of P). */ + if (!secp256k1_fe_set_b32(&Rx, sig64[k])) { + return 0; + } + secp256k1_ge_set_xo_var(&Ra, &Rx, 1); + if (k) { + secp256k1_gej_set_ge(&P[k - 1], &Ra); + } else { + R0 = Ra; + } + + /* Find s (multiplied by corresponding f, added together in ss). */ + overflow = 0; + secp256k1_scalar_set_b32(&s, sig64[k] + 32, &overflow); + if (overflow) { + return 0; + } + if (k) { + secp256k1_scalar_mul(&s, &s, &f[k - 1]); + secp256k1_scalar_add(&ss, &ss, &s); + } else { + ss = s; + } + + /* Find Qs (into the second half of P). */ + if (secp256k1_ge_is_infinity(&pubkey[k])) { + return 0; + } + secp256k1_gej_set_ge(&P[k + n - 1], &pubkey[k]); + + /* Find h (multiplied by corresponding f, into the second half of f). */ + hash(hh, sig64[k], msg32); + overflow = 0; + secp256k1_scalar_set_b32(&f[k + n - 1], hh, &overflow); + if (overflow || secp256k1_scalar_is_zero(&f[k + n - 1])) { + return 0; + } + if (k) { + secp256k1_scalar_mul(&f[k + n - 1], &f[k + n - 1], &f[k - 1]); + } + } + + secp256k1_ecmult_mult(ctx, 2 * n - 1, &S, P, f, &ss); + secp256k1_gej_add_ge_var(&S, &S, &R0, NULL); + return (secp256k1_gej_is_infinity(&S)); +} + +#endif diff --git a/src/secp256k1.c b/src/secp256k1.c index d6192dc4ed..dceeaeed09 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" @@ -417,3 +418,83 @@ int secp256k1_context_randomize(secp256k1_context_t* ctx, const unsigned char *s secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); return 1; } + +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); +} + +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, 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, 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 unsigned char *pubkey, int pubkeylen) { + 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_eckey_pubkey_parse(&q, pubkey, pubkeylen)) { + return 0; + } + return secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32); +} + +int secp256k1_schnorr_verify_batch(const secp256k1_context_t* ctx, int n, const unsigned char *msg32, const unsigned char **sig64, const unsigned char **pubkey, const int *pubkeylen) { + secp256k1_ge_t q[MAX_MULTI]; + unsigned char sig[MAX_MULTI][64]; + int k; + + 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); + DEBUG_CHECK(pubkeylen != NULL); + DEBUG_CHECK(n < MAX_MULTI); + + for (k = 0; k < n; k++) { + memcpy(&sig[k], sig64[k], 64); + if (!secp256k1_eckey_pubkey_parse(&q[k], pubkey[k], pubkeylen[k])) { + return 0; + } + } + + return secp256k1_schnorr_sig_verify_batch(&ctx->ecmult_ctx, n, sig, q, secp256k1_schnorr_msghash_sha256, msg32); +} diff --git a/src/tests.c b/src/tests.c index 31203271c3..f5366e5097 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1284,6 +1284,47 @@ void test_point_times_order(const secp256k1_gej_t *point) { CHECK(secp256k1_eckey_pubkey_serialize(&res3, pub, &psize, 1) == 0); } +void test_ecmult_multi_ecmult(void) { + secp256k1_scalar_t x[3]; + secp256k1_scalar_t t[3], tall; + secp256k1_gej_t a[3]; + secp256k1_gej_t tmp, res1, res2; + random_scalar_order_test(&x[0]); + random_scalar_order_test(&x[1]); + random_scalar_order_test(&x[2]); + random_scalar_order_test(&t[0]); + random_scalar_order_test(&t[1]); + random_scalar_order_test(&t[2]); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &a[1], &t[0]); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &a[2], &t[1]); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &a[0], &t[2]); + + /* Compute res1 = (x1*a1 + t1*g) + (x2*a2 + t2) + (x3*a3 + t3). */ + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, &a[0], &x[0], &t[0]); + secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &a[1], &x[1], &t[1]); + secp256k1_gej_add_var(&res1, &res1, &tmp, NULL); + secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &a[2], &x[2], &t[2]); + secp256k1_gej_add_var(&res1, &res1, &tmp, NULL); + + /* Compute res2 = x1*a1 + x2*a2 + x3*a3 + (t1+t2+t3)*g. */ + secp256k1_scalar_add(&tall, &t[0], &t[1]); + secp256k1_scalar_add(&tall, &tall, &t[2]); + secp256k1_ecmult_mult(&ctx->ecmult_ctx, 3, &res2, a, x, &tall); + + /* Compare res1 == res2. */ + secp256k1_gej_neg(&res2, &res2); + secp256k1_gej_add_var(&res2, &res2, &res1, NULL); + CHECK(secp256k1_gej_is_infinity(&res2)); +} + +void run_ecmult_multi_ecmult(void) { + int i; + for (i = 0; i < 8 * count; i++) { + test_ecmult_multi_ecmult(); + } +} + + void run_point_times_order(void) { int i; secp256k1_fe_t x = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2); @@ -2040,6 +2081,67 @@ void run_ecdsa_edge_cases(void) { test_ecdsa_edge_cases(); } +/** 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], &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; + } + } + + CHECK(secp256k1_schnorr_sig_verify_batch(&ctx->ecmult_ctx, 3, sig64, pubkey, test_schnorr_hash, msg32)); + + for (i = 0; i < 4; i++) { + int pos = secp256k1_rand32() % 64; + int mod = 1 + (secp256k1_rand32() % 255); + k = secp256k1_rand32() % 3; + sig64[k][pos] ^= mod; + CHECK(secp256k1_schnorr_sig_verify_batch(&ctx->ecmult_ctx, 3, sig64, pubkey, &test_schnorr_hash, msg32) == 0); + sig64[k][pos] ^= mod; + } +} + +void run_schnorr_sign_verify(void) { + int i; + for (i = 0; i < 32 * count; i++) { + test_schnorr_sign_verify(); + } +} + #ifdef ENABLE_OPENSSL_TESTS EC_KEY *get_openssl_key(const secp256k1_scalar_t *key) { unsigned char privkey[300]; @@ -2175,6 +2277,7 @@ int main(int argc, char **argv) { run_ecmult_chain(); run_ecmult_constants(); run_ecmult_gen_blind(); + run_ecmult_multi_ecmult(); /* ecdsa tests */ run_random_pubkeys(); @@ -2185,6 +2288,9 @@ int main(int argc, char **argv) { run_ecdsa_openssl(); #endif + /* Schnorr tests */ + run_schnorr_sign_verify(); + 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]);