Skip to content

Commit 3faad3b

Browse files
committed
Add support for Schnorr signatures.
1 parent 08d9798 commit 3faad3b

File tree

9 files changed

+700
-1
lines changed

9 files changed

+700
-1
lines changed

Makefile.am

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ noinst_HEADERS += src/hash_impl.h
3838
noinst_HEADERS += src/field.h
3939
noinst_HEADERS += src/field_impl.h
4040
noinst_HEADERS += src/bench.h
41+
noinst_HEADERS += src/schnorr.h
42+
noinst_HEADERS += src/schnorr_impl.h
4143

4244
pkgconfigdir = $(libdir)/pkgconfig
4345
pkgconfig_DATA = libsecp256k1.pc
@@ -49,10 +51,13 @@ libsecp256k1_la_LIBADD = $(SECP_LIBS)
4951

5052
noinst_PROGRAMS =
5153
if USE_BENCHMARK
52-
noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal
54+
noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal bench_schnorr_verify
5355
bench_verify_SOURCES = src/bench_verify.c
5456
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
5557
bench_verify_LDFLAGS = -static
58+
bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c
59+
bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
60+
bench_schnorr_verify_LDFLAGS = -static
5661
bench_recover_SOURCES = src/bench_recover.c
5762
bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS)
5863
bench_recover_LDFLAGS = -static

include/secp256k1.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,10 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
285285
const unsigned char *tweak
286286
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3);
287287

288+
int secp256k1_schnorr_sign(const unsigned char *msg32, unsigned char *sig64, const unsigned char *seckey, secp256k1_nonce_function_t noncefp, const void* noncedata);
289+
int secp256k1_schnorr_verify(const unsigned char *msg32, const unsigned char *sig64, const unsigned char *pubkey, int pubkeylen);
290+
int secp256k1_schnorr_verify_batch(int n, const unsigned char *msg32, const unsigned char **sig64, const unsigned char **pubkey, const int *pubkeylen);
291+
288292
# ifdef __cplusplus
289293
}
290294
# endif

src/bench_schnorr_verify.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**********************************************************************
2+
* Copyright (c) 2014 Pieter Wuille *
3+
* Distributed under the MIT software license, see the accompanying *
4+
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
5+
**********************************************************************/
6+
7+
#include <stdio.h>
8+
#include <string.h>
9+
10+
#include "include/secp256k1.h"
11+
#include "util.h"
12+
#include "bench.h"
13+
14+
typedef struct {
15+
unsigned char key[32];
16+
unsigned char sig[64];
17+
unsigned char pubkey[33];
18+
int pubkeylen;
19+
} benchmark_schnorr_sig_t;
20+
21+
typedef struct {
22+
unsigned char msg[32];
23+
benchmark_schnorr_sig_t sigs[64];
24+
int numsigs;
25+
} benchmark_schnorr_verify_t;
26+
27+
static void benchmark_schnorr_init(void* arg) {
28+
int i, k;
29+
benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg;
30+
31+
for (i = 0; i < 32; i++) data->msg[i] = 1 + i;
32+
for (k = 0; k < data->numsigs; k++) {
33+
for (i = 0; i < 32; i++) data->sigs[k].key[i] = 33 + i + k;
34+
secp256k1_schnorr_sign(data->msg, data->sigs[k].sig, data->sigs[k].key, NULL, NULL);
35+
data->sigs[k].pubkeylen = 33;
36+
CHECK(secp256k1_ec_pubkey_create(data->sigs[k].pubkey, &data->sigs[k].pubkeylen, data->sigs[k].key, 1));
37+
}
38+
}
39+
40+
static void benchmark_schnorr_verify(void* arg) {
41+
int i;
42+
benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg;
43+
44+
for (i = 0; i < 20000 / data->numsigs; i++) {
45+
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
46+
CHECK(secp256k1_schnorr_verify(data->msg, data->sigs[0].sig, data->sigs[0].pubkey, data->sigs[0].pubkeylen) == ((i & 0xFF) == 0));
47+
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
48+
}
49+
}
50+
51+
static void benchmark_schnorr_verify_batch(void* arg) {
52+
int i, k;
53+
benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg;
54+
55+
const unsigned char *sig_ptr[64];
56+
const unsigned char *pubkey_ptr[64];
57+
int pubkeylen[64];
58+
59+
for (k = 0; k < data->numsigs; k++) {
60+
sig_ptr[k] = &data->sigs[k].sig[0];
61+
pubkey_ptr[k] = &data->sigs[k].pubkey[0];
62+
pubkeylen[k] = data->sigs[k].pubkeylen;
63+
}
64+
65+
for (i = 0; i < 20000 / data->numsigs; i++) {
66+
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
67+
CHECK(secp256k1_schnorr_verify_batch(data->numsigs, data->msg, sig_ptr, pubkey_ptr, pubkeylen) == ((i & 0xFF) == 0));
68+
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
69+
}
70+
}
71+
72+
73+
74+
int main(void) {
75+
benchmark_schnorr_verify_t data;
76+
77+
secp256k1_start(SECP256K1_START_VERIFY | SECP256K1_START_SIGN);
78+
79+
data.numsigs = 1;
80+
run_benchmark("schnorr_verify", benchmark_schnorr_verify, benchmark_schnorr_init, NULL, &data, 10, 20000);
81+
run_benchmark("schnorr_verify_batch1", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
82+
data.numsigs = 2;
83+
run_benchmark("schnorr_verify_batch2", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
84+
data.numsigs = 4;
85+
run_benchmark("schnorr_verify_batch4", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
86+
data.numsigs = 8;
87+
run_benchmark("schnorr_verify_batch8", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
88+
data.numsigs = 16;
89+
run_benchmark("schnorr_verify_batch16", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
90+
data.numsigs = 32;
91+
run_benchmark("schnorr_verify_batch32", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
92+
93+
secp256k1_stop();
94+
return 0;
95+
}

src/ecmult.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,8 @@ static void secp256k1_ecmult_stop(void);
1616
/** Double multiply: R = na*A + ng*G */
1717
static void secp256k1_ecmult(secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng);
1818

19+
static void secp256k1_ecmult_mult(int points, secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng);
20+
21+
#define MAX_MULTI 64
22+
1923
#endif

src/ecmult_impl.h

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,49 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej_t *prej, s
7777
#endif
7878
}
7979

80+
/** Fill a table 'prej' with a concatenation of precomputed off multiples of the
81+
* points in a. Prej will contain the values
82+
* [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
83+
* needs space for k * n values. zr[0] will contain prej[0].z / a[0].z. The
84+
* other zr[i] values = prej[i].z / prej[i-1].z. */
85+
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) {
86+
int j;
87+
for (j = 0; j < k; j++) {
88+
secp256k1_gej_t aa;
89+
secp256k1_fe_t z2, z3;
90+
if (j != 0) {
91+
/* Make the Z coordinate of each input a known multiple of the
92+
* last prej output of the previous input point. */
93+
secp256k1_fe_sqr(&z2, &prej[n * j - 1].z);
94+
secp256k1_fe_mul(&z3, &z2, &prej[n * j - 1].z);
95+
secp256k1_fe_mul(&aa.x, &a[j].x, &z2);
96+
secp256k1_fe_mul(&aa.y, &a[j].y, &z3);
97+
secp256k1_fe_mul(&aa.z, &a[j].z, &prej[n * j - 1].z);
98+
aa.infinity = 0;
99+
} else {
100+
aa = a[0];
101+
}
102+
secp256k1_ecmult_odd_multiples_table(n, &prej[n * j], &zr[n * j], &aa);
103+
if (j != 0) {
104+
/* Correct the first Z ratio output of this point, by multiplying it
105+
* with the current point's input Z coordinate, chaining them
106+
* together */
107+
secp256k1_fe_mul(zr + n * j, zr + n * j, &a[j].z);
108+
}
109+
}
110+
}
111+
80112
/** Fill a table 'pre' with precomputed odd multiples of a.
81113
*
82114
* There are two versions of this function:
83115
* - secp256k1_ecmult_odd_multiples_table_globalz_windowa which brings its
84116
* resulting point set to a single constant Z denominator, stores the X and Y
85117
* coordinates as ge_storage points in pre, and stores the global Z in rz.
86118
* It only operates on tables sized for WINDOW_A wnaf multiples.
119+
* - secp256k1_ecmult_multi_odd_multiples_table_globalz_windowa which is
120+
* identical to secp256k1_ecmult_odd_multiples_table_globalz_windowa, but
121+
* works on several input points at once, and brings them all to a single
122+
* global Z.
87123
* - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its
88124
* resulting point set to actually affine points, and stores those in pre.
89125
* It operates on tables of any size, but uses heap-allocated temporaries.
@@ -102,6 +138,16 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge_t
102138
secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr);
103139
}
104140

141+
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) {
142+
secp256k1_gej_t prej[MAX_MULTI * ECMULT_TABLE_SIZE(WINDOW_A)];
143+
secp256k1_fe_t zr[MAX_MULTI * ECMULT_TABLE_SIZE(WINDOW_A)];
144+
145+
/* Compute the odd multiples of all inputs in Jacobian form. */
146+
secp256k1_ecmult_multi_odd_multiples_table(k, ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a);
147+
/* Bring them to the same Z denominator. */
148+
secp256k1_ge_globalz_set_table_gej(k * ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr);
149+
}
150+
105151
static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage_t *pre, const secp256k1_gej_t *a) {
106152
secp256k1_gej_t *prej = checked_malloc(sizeof(secp256k1_gej_t) * n);
107153
secp256k1_ge_t *prea = checked_malloc(sizeof(secp256k1_ge_t) * n);
@@ -353,4 +399,122 @@ static void secp256k1_ecmult(secp256k1_gej_t *r, const secp256k1_gej_t *a, const
353399
}
354400
}
355401

402+
static void secp256k1_ecmult_mult(int points, secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng) {
403+
secp256k1_ge_t pre_a[MAX_MULTI][ECMULT_TABLE_SIZE(WINDOW_A)];
404+
secp256k1_ge_t tmpa;
405+
secp256k1_fe_t Z;
406+
const secp256k1_ecmult_consts_t *c = secp256k1_ecmult_consts;
407+
#ifdef USE_ENDOMORPHISM
408+
secp256k1_ge_t pre_a_lam[MAX_MULTI][ECMULT_TABLE_SIZE(WINDOW_A)];
409+
secp256k1_scalar_t na_1[MAX_MULTI], na_lam[MAX_MULTI];
410+
/* Splitted G factors. */
411+
secp256k1_scalar_t ng_1, ng_128;
412+
int wnaf_na_1[MAX_MULTI][130];
413+
int wnaf_na_lam[MAX_MULTI][130];
414+
int bits_na_1[MAX_MULTI];
415+
int bits_na_lam[MAX_MULTI];
416+
int wnaf_ng_1[129];
417+
int bits_ng_1;
418+
int wnaf_ng_128[129];
419+
int bits_ng_128;
420+
#else
421+
int wnaf_na[MAX_MULTI][256];
422+
int bits_na[MAX_MULTI];
423+
int wnaf_ng[257];
424+
int bits_ng;
425+
#endif
426+
int i;
427+
int bits = 0;
428+
int k;
429+
430+
VERIFY_CHECK(points >= 1);
431+
VERIFY_CHECK(points <= MAX_MULTI);
432+
433+
for (k = 0; k < points; k++) {
434+
#ifdef USE_ENDOMORPHISM
435+
/* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
436+
secp256k1_scalar_split_lambda_var(&na_1[k], &na_lam[k], &na[k]);
437+
438+
/* build wnaf representation for na_1 and na_lam. */
439+
bits_na_1[k] = secp256k1_ecmult_wnaf(wnaf_na_1[k], &na_1[k], WINDOW_A);
440+
bits_na_lam[k] = secp256k1_ecmult_wnaf(wnaf_na_lam[k], &na_lam[k], WINDOW_A);
441+
VERIFY_CHECK(bits_na_1[k] <= 130);
442+
VERIFY_CHECK(bits_na_lam[k] <= 130);
443+
if (bits_na_1[k] > bits) bits = bits_na_1[k];
444+
if (bits_na_lam[k] > bits) bits = bits_na_lam[k];
445+
#else
446+
/* build wnaf representation for na. */
447+
bits_na[k] = secp256k1_ecmult_wnaf(wnaf_na[k], &na[k], WINDOW_A);
448+
if (bits_na[k] > bits) bits = bits_na[k];
449+
#endif
450+
}
451+
452+
/* calculate odd multiples of all a's */
453+
secp256k1_ecmult_multi_odd_multiples_table_globalz_windowa(points, &pre_a[0][0], &Z, a);
454+
455+
#ifdef USE_ENDOMORPHISM
456+
for (k = 0; k < points; k++) {
457+
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
458+
secp256k1_ge_mul_lambda(&pre_a_lam[k][i], &pre_a[k][i]);
459+
}
460+
}
461+
#endif
462+
463+
#ifdef USE_ENDOMORPHISM
464+
/* 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) */
465+
secp256k1_scalar_split_128(&ng_1, &ng_128, ng);
466+
467+
/* Build wnaf representation for ng_1 and ng_128 */
468+
bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, &ng_1, WINDOW_G);
469+
bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, &ng_128, WINDOW_G);
470+
if (bits_ng_1 > bits) bits = bits_ng_1;
471+
if (bits_ng_128 > bits) bits = bits_ng_128;
472+
#else
473+
bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, ng, WINDOW_G);
474+
if (bits_ng > bits) bits = bits_ng;
475+
#endif
476+
477+
secp256k1_gej_set_infinity(r);
478+
479+
for (i = bits-1; i >= 0; i--) {
480+
int n;
481+
secp256k1_gej_double_var(r, r, NULL);
482+
#ifdef USE_ENDOMORPHISM
483+
for (k = 0; k < points; k++) {
484+
if (i < bits_na_1[k] && (n = wnaf_na_1[k][i])) {
485+
ECMULT_TABLE_GET_GE(&tmpa, pre_a[k], n, WINDOW_A);
486+
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
487+
}
488+
if (i < bits_na_lam[k] && (n = wnaf_na_lam[k][i])) {
489+
ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam[k], n, WINDOW_A);
490+
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
491+
}
492+
}
493+
if (i < bits_ng_1 && (n = wnaf_ng_1[i])) {
494+
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, c->pre_g, n, WINDOW_G);
495+
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
496+
}
497+
if (i < bits_ng_128 && (n = wnaf_ng_128[i])) {
498+
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, c->pre_g_128, n, WINDOW_G);
499+
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
500+
}
501+
#else
502+
for (k = 0; k < points; k++) {
503+
if (i < bits_na[k] && (n = wnaf_na[k][i])) {
504+
ECMULT_TABLE_GET_GE(&tmpa, pre_a[k], n, WINDOW_A);
505+
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
506+
}
507+
}
508+
if (i < bits_ng && (n = wnaf_ng[i])) {
509+
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, c->pre_g, n, WINDOW_G);
510+
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
511+
}
512+
#endif
513+
}
514+
515+
if (!r->infinity) {
516+
secp256k1_fe_mul(&r->z, &r->z, &Z);
517+
}
518+
}
519+
356520
#endif

src/schnorr.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/***********************************************************************
2+
* Copyright (c) 2015 Pieter Wuille *
3+
* Distributed under the MIT software license, see the accompanying *
4+
* file COPYING or http://www.opensource.org/licenses/mit-license.php. *
5+
***********************************************************************/
6+
7+
#ifndef _SECP256K1_SCHNORR_
8+
#define _SECP256K1_SCHNORR_
9+
10+
#include "scalar.h"
11+
#include "group.h"
12+
13+
typedef void (*secp256k1_schnorr_msghash_t)(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32);
14+
15+
static int secp256k1_schnorr_sig_sign(unsigned char *sig64, const secp256k1_scalar_t *key, const secp256k1_scalar_t *nonce, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32);
16+
static int secp256k1_schnorr_sig_verify(const unsigned char *sig64, const secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32);
17+
static int secp256k1_schnorr_sig_verify_batch(int n, unsigned char (* const sig64)[64], const secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32);
18+
19+
#endif

0 commit comments

Comments
 (0)