Skip to content

Commit 8fcf74b

Browse files
committed
Add support for Schnorr signatures.
1 parent c2a7efe commit 8fcf74b

File tree

9 files changed

+701
-1
lines changed

9 files changed

+701
-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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,9 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
339339
const unsigned char *seed32
340340
) SECP256K1_ARG_NONNULL(1);
341341

342+
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);
343+
int secp256k1_schnorr_verify(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig64, const unsigned char *pubkey, int pubkeylen);
344+
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);
342345

343346
# ifdef __cplusplus
344347
}

src/bench_schnorr_verify.c

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
secp256k1_context_t *ctx;
23+
unsigned char msg[32];
24+
benchmark_schnorr_sig_t sigs[64];
25+
int numsigs;
26+
} benchmark_schnorr_verify_t;
27+
28+
static void benchmark_schnorr_init(void* arg) {
29+
int i, k;
30+
benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg;
31+
32+
for (i = 0; i < 32; i++) data->msg[i] = 1 + i;
33+
for (k = 0; k < data->numsigs; k++) {
34+
for (i = 0; i < 32; i++) data->sigs[k].key[i] = 33 + i + k;
35+
secp256k1_schnorr_sign(data->ctx, data->msg, data->sigs[k].sig, data->sigs[k].key, NULL, NULL);
36+
data->sigs[k].pubkeylen = 33;
37+
CHECK(secp256k1_ec_pubkey_create(data->ctx, data->sigs[k].pubkey, &data->sigs[k].pubkeylen, data->sigs[k].key, 1));
38+
}
39+
}
40+
41+
static void benchmark_schnorr_verify(void* arg) {
42+
int i;
43+
benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg;
44+
45+
for (i = 0; i < 20000 / data->numsigs; i++) {
46+
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
47+
CHECK(secp256k1_schnorr_verify(data->ctx, data->msg, data->sigs[0].sig, data->sigs[0].pubkey, data->sigs[0].pubkeylen) == ((i & 0xFF) == 0));
48+
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
49+
}
50+
}
51+
52+
static void benchmark_schnorr_verify_batch(void* arg) {
53+
int i, k;
54+
benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg;
55+
56+
const unsigned char *sig_ptr[64];
57+
const unsigned char *pubkey_ptr[64];
58+
int pubkeylen[64];
59+
60+
for (k = 0; k < data->numsigs; k++) {
61+
sig_ptr[k] = &data->sigs[k].sig[0];
62+
pubkey_ptr[k] = &data->sigs[k].pubkey[0];
63+
pubkeylen[k] = data->sigs[k].pubkeylen;
64+
}
65+
66+
for (i = 0; i < 20000 / data->numsigs; i++) {
67+
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
68+
CHECK(secp256k1_schnorr_verify_batch(data->ctx, data->numsigs, data->msg, sig_ptr, pubkey_ptr, pubkeylen) == ((i & 0xFF) == 0));
69+
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
70+
}
71+
}
72+
73+
74+
75+
int main(void) {
76+
benchmark_schnorr_verify_t data;
77+
78+
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
79+
80+
data.numsigs = 1;
81+
run_benchmark("schnorr_verify", benchmark_schnorr_verify, benchmark_schnorr_init, NULL, &data, 10, 20000);
82+
run_benchmark("schnorr_verify_batch1", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
83+
data.numsigs = 2;
84+
run_benchmark("schnorr_verify_batch2", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
85+
data.numsigs = 4;
86+
run_benchmark("schnorr_verify_batch4", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
87+
data.numsigs = 8;
88+
run_benchmark("schnorr_verify_batch8", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
89+
data.numsigs = 16;
90+
run_benchmark("schnorr_verify_batch16", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
91+
data.numsigs = 32;
92+
run_benchmark("schnorr_verify_batch32", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
93+
94+
secp256k1_context_destroy(data.ctx);
95+
return 0;
96+
}

src/ecmult.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,8 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context_t *c
2828
/** Double multiply: R = na*A + ng*G */
2929
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);
3030

31+
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);
32+
33+
#define MAX_MULTI 64
34+
3135
#endif

src/ecmult_impl.h

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,49 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej_t *prej, s
8484
#endif
8585
}
8686

87+
/** Fill a table 'prej' with a concatenation of precomputed off multiples of the
88+
* points in a. Prej will contain the values
89+
* [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
90+
* needs space for k * n values. zr[0] will contain prej[0].z / a[0].z. The
91+
* other zr[i] values = prej[i].z / prej[i-1].z. */
92+
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) {
93+
int j;
94+
for (j = 0; j < k; j++) {
95+
secp256k1_gej_t aa;
96+
secp256k1_fe_t z2, z3;
97+
if (j != 0) {
98+
/* Make the Z coordinate of each input a known multiple of the
99+
* last prej output of the previous input point. */
100+
secp256k1_fe_sqr(&z2, &prej[n * j - 1].z);
101+
secp256k1_fe_mul(&z3, &z2, &prej[n * j - 1].z);
102+
secp256k1_fe_mul(&aa.x, &a[j].x, &z2);
103+
secp256k1_fe_mul(&aa.y, &a[j].y, &z3);
104+
secp256k1_fe_mul(&aa.z, &a[j].z, &prej[n * j - 1].z);
105+
aa.infinity = 0;
106+
} else {
107+
aa = a[0];
108+
}
109+
secp256k1_ecmult_odd_multiples_table(n, &prej[n * j], &zr[n * j], &aa);
110+
if (j != 0) {
111+
/* Correct the first Z ratio output of this point, by multiplying it
112+
* with the current point's input Z coordinate, chaining them
113+
* together */
114+
secp256k1_fe_mul(zr + n * j, zr + n * j, &a[j].z);
115+
}
116+
}
117+
}
118+
87119
/** Fill a table 'pre' with precomputed odd multiples of a.
88120
*
89121
* There are two versions of this function:
90122
* - secp256k1_ecmult_odd_multiples_table_globalz_windowa which brings its
91123
* resulting point set to a single constant Z denominator, stores the X and Y
92124
* coordinates as ge_storage points in pre, and stores the global Z in rz.
93125
* It only operates on tables sized for WINDOW_A wnaf multiples.
126+
* - secp256k1_ecmult_multi_odd_multiples_table_globalz_windowa which is
127+
* identical to secp256k1_ecmult_odd_multiples_table_globalz_windowa, but
128+
* works on several input points at once, and brings them all to a single
129+
* global Z.
94130
* - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its
95131
* resulting point set to actually affine points, and stores those in pre.
96132
* 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
109145
secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr);
110146
}
111147

148+
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) {
149+
secp256k1_gej_t prej[MAX_MULTI * ECMULT_TABLE_SIZE(WINDOW_A)];
150+
secp256k1_fe_t zr[MAX_MULTI * ECMULT_TABLE_SIZE(WINDOW_A)];
151+
152+
/* Compute the odd multiples of all inputs in Jacobian form. */
153+
secp256k1_ecmult_multi_odd_multiples_table(k, ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a);
154+
/* Bring them to the same Z denominator. */
155+
secp256k1_ge_globalz_set_table_gej(k * ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr);
156+
}
157+
112158
static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage_t *pre, const secp256k1_gej_t *a) {
113159
secp256k1_gej_t *prej = checked_malloc(sizeof(secp256k1_gej_t) * n);
114160
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
389435
}
390436
}
391437

438+
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) {
439+
secp256k1_ge_t pre_a[MAX_MULTI][ECMULT_TABLE_SIZE(WINDOW_A)];
440+
secp256k1_ge_t tmpa;
441+
secp256k1_fe_t Z;
442+
#ifdef USE_ENDOMORPHISM
443+
secp256k1_ge_t pre_a_lam[MAX_MULTI][ECMULT_TABLE_SIZE(WINDOW_A)];
444+
secp256k1_scalar_t na_1[MAX_MULTI], na_lam[MAX_MULTI];
445+
/* Splitted G factors. */
446+
secp256k1_scalar_t ng_1, ng_128;
447+
int wnaf_na_1[MAX_MULTI][130];
448+
int wnaf_na_lam[MAX_MULTI][130];
449+
int bits_na_1[MAX_MULTI];
450+
int bits_na_lam[MAX_MULTI];
451+
int wnaf_ng_1[129];
452+
int bits_ng_1;
453+
int wnaf_ng_128[129];
454+
int bits_ng_128;
455+
#else
456+
int wnaf_na[MAX_MULTI][256];
457+
int bits_na[MAX_MULTI];
458+
int wnaf_ng[257];
459+
int bits_ng;
460+
#endif
461+
int i;
462+
int bits = 0;
463+
int k;
464+
465+
VERIFY_CHECK(points >= 1);
466+
VERIFY_CHECK(points <= MAX_MULTI);
467+
468+
for (k = 0; k < points; k++) {
469+
#ifdef USE_ENDOMORPHISM
470+
/* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
471+
secp256k1_scalar_split_lambda_var(&na_1[k], &na_lam[k], &na[k]);
472+
473+
/* build wnaf representation for na_1 and na_lam. */
474+
bits_na_1[k] = secp256k1_ecmult_wnaf(wnaf_na_1[k], &na_1[k], WINDOW_A);
475+
bits_na_lam[k] = secp256k1_ecmult_wnaf(wnaf_na_lam[k], &na_lam[k], WINDOW_A);
476+
VERIFY_CHECK(bits_na_1[k] <= 130);
477+
VERIFY_CHECK(bits_na_lam[k] <= 130);
478+
if (bits_na_1[k] > bits) bits = bits_na_1[k];
479+
if (bits_na_lam[k] > bits) bits = bits_na_lam[k];
480+
#else
481+
/* build wnaf representation for na. */
482+
bits_na[k] = secp256k1_ecmult_wnaf(wnaf_na[k], &na[k], WINDOW_A);
483+
if (bits_na[k] > bits) bits = bits_na[k];
484+
#endif
485+
}
486+
487+
/* calculate odd multiples of all a's */
488+
secp256k1_ecmult_multi_odd_multiples_table_globalz_windowa(points, &pre_a[0][0], &Z, a);
489+
490+
#ifdef USE_ENDOMORPHISM
491+
for (k = 0; k < points; k++) {
492+
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
493+
secp256k1_ge_mul_lambda(&pre_a_lam[k][i], &pre_a[k][i]);
494+
}
495+
}
496+
#endif
497+
498+
#ifdef USE_ENDOMORPHISM
499+
/* 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) */
500+
secp256k1_scalar_split_128(&ng_1, &ng_128, ng);
501+
502+
/* Build wnaf representation for ng_1 and ng_128 */
503+
bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, &ng_1, WINDOW_G);
504+
bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, &ng_128, WINDOW_G);
505+
if (bits_ng_1 > bits) bits = bits_ng_1;
506+
if (bits_ng_128 > bits) bits = bits_ng_128;
507+
#else
508+
bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, ng, WINDOW_G);
509+
if (bits_ng > bits) bits = bits_ng;
510+
#endif
511+
512+
secp256k1_gej_set_infinity(r);
513+
514+
for (i = bits-1; i >= 0; i--) {
515+
int n;
516+
secp256k1_gej_double_var(r, r, NULL);
517+
#ifdef USE_ENDOMORPHISM
518+
for (k = 0; k < points; k++) {
519+
if (i < bits_na_1[k] && (n = wnaf_na_1[k][i])) {
520+
ECMULT_TABLE_GET_GE(&tmpa, pre_a[k], n, WINDOW_A);
521+
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
522+
}
523+
if (i < bits_na_lam[k] && (n = wnaf_na_lam[k][i])) {
524+
ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam[k], n, WINDOW_A);
525+
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
526+
}
527+
}
528+
if (i < bits_ng_1 && (n = wnaf_ng_1[i])) {
529+
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G);
530+
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
531+
}
532+
if (i < bits_ng_128 && (n = wnaf_ng_128[i])) {
533+
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g_128, n, WINDOW_G);
534+
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
535+
}
536+
#else
537+
for (k = 0; k < points; k++) {
538+
if (i < bits_na[k] && (n = wnaf_na[k][i])) {
539+
ECMULT_TABLE_GET_GE(&tmpa, pre_a[k], n, WINDOW_A);
540+
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
541+
}
542+
}
543+
if (i < bits_ng && (n = wnaf_ng[i])) {
544+
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G);
545+
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
546+
}
547+
#endif
548+
}
549+
550+
if (!r->infinity) {
551+
secp256k1_fe_mul(&r->z, &r->z, &Z);
552+
}
553+
}
554+
392555
#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(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);
16+
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);
17+
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);
18+
19+
#endif

0 commit comments

Comments
 (0)