Skip to content

Commit af94dbd

Browse files
committed
Add support for Schnorr signatures.
1 parent 873a453 commit af94dbd

File tree

7 files changed

+463
-1
lines changed

7 files changed

+463
-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: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,61 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
341341
const unsigned char *seed32
342342
) SECP256K1_ARG_NONNULL(1);
343343

344+
/** Create a signature using an experimental custom EC-Schnorr-SHA256 construction. It produces non-malleable 64-byte signatures which are recoverable and batch-validatable.
345+
* Returns: 1: signature created
346+
* 0: the nonce generation function failed, or the private key was invalid.
347+
* In: ctx: pointer to a context object, initialized for signing (cannot be NULL)
348+
* msg32: the 32-byte message hash being signed (cannot be NULL)
349+
* seckey: pointer to a 32-byte secret key (cannot be NULL)
350+
* noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
351+
* ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
352+
* Out: sig64: pointer to a 64-byte array where the signature will be placed (cannot be NULL)
353+
*/
354+
int secp256k1_schnorr_sign(
355+
const secp256k1_context_t* ctx,
356+
const unsigned char *msg32,
357+
unsigned char *sig64,
358+
const unsigned char *seckey,
359+
secp256k1_nonce_function_t noncefp,
360+
const void *ndata
361+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
362+
363+
/** Verify a signature created by secp256k1_schnorr_sign.
364+
* Returns: 1: public key and signature correct
365+
* 0: incorrect signature
366+
* -1: invalid public key
367+
* In: ctx: a secp256k1 context object, initialized for verification.
368+
* msg32: the 32-byte message hash being verified (cannot be NULL)
369+
* sig64: the 64-byte signature being verified (cannot be NULL)
370+
* pubkey: the public key to verify with (cannot be NULL)
371+
* pubkeylen: the length of pubkey
372+
*/
373+
SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_verify(
374+
const secp256k1_context_t* ctx,
375+
const unsigned char *msg32,
376+
const unsigned char *sig64,
377+
const unsigned char *pubkey,
378+
int pubkeylen
379+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
380+
381+
/** Recover an EC public key from a Schnorr signature created using secp256k1_schnorr_sign.
382+
* Returns: 1: public key successfully recovered (which guarantees a correct signature).
383+
* 0: otherwise.
384+
* In: ctx: pointer to a context object, initialized for verification (cannot be NULL)
385+
* msg32: the 32-byte message hash assumed to be signed (cannot be NULL)
386+
* sig64: signature as 64 byte array (cannot be NULL)
387+
* compressed: whether to recover a compressed or uncompressed pubkey
388+
* Out: pubkey: pointer to a 33 or 65 byte array to put the pubkey (cannot be NULL)
389+
* pubkeylen: pointer to an int that will contain the pubkey length (cannot be NULL)
390+
*/
391+
int secp256k1_schnorr_recover(
392+
const secp256k1_context_t* ctx,
393+
const unsigned char *msg32,
394+
const unsigned char *sig64,
395+
unsigned char *pubkey,
396+
int *pubkeylen,
397+
int compressed
398+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
344399

345400
# ifdef __cplusplus
346401
}

src/bench_schnorr_verify.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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+
53+
54+
int main(void) {
55+
benchmark_schnorr_verify_t data;
56+
57+
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
58+
59+
data.numsigs = 1;
60+
run_benchmark("schnorr_verify", benchmark_schnorr_verify, benchmark_schnorr_init, NULL, &data, 10, 20000);
61+
62+
secp256k1_context_destroy(data.ctx);
63+
return 0;
64+
}

src/schnorr.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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, 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+
18+
#endif

src/schnorr_impl.h

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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_IMPL_H_
8+
#define _SECP256K1_SCHNORR_IMPL_H_
9+
10+
#include <string.h>
11+
12+
#include "schnorr.h"
13+
#include "num.h"
14+
#include "field.h"
15+
#include "group.h"
16+
#include "ecmult.h"
17+
#include "ecmult_gen.h"
18+
19+
/**
20+
* Custom Schnorr-based signature scheme:
21+
*
22+
* Signing:
23+
* Inputs: 32-byte message m, 32-byte scalar key x (!=0), 32-byte scalar nonce k (!=0)
24+
*
25+
* Compute point R = k * G. Reject nonce if R's y coordinate is odd (or negate nonce).
26+
* Compute 32-byte r, the serialization of R's x coordinate.
27+
* Compute scalar h = Hash(r || m). Reject nonce if h == 0 or h >= order.
28+
* Compute scalar s = k - h * x.
29+
* The signature is (r, s).
30+
*
31+
*
32+
* Verification:
33+
* Inputs: 32-byte message m, public key point Q, signature: (32-byte r, scalar s)
34+
*
35+
* Signature is invalid if s >= order.
36+
* Signature is invalid if r >= p.
37+
* Compute scalar h = Hash(r || m). Signature is invalid if h == 0 or h >= order.
38+
* Option 1 (faster for single verification):
39+
* Compute point R = h * Q + s * G. Signature is invalid if R is infinity or R's y coordinate is odd.
40+
* Signature is valid if the serialization of R's x coordinate equals r.
41+
* Option 2 (allows batch validation and pubkey recovery):
42+
* Decompress x coordinate r into point R, with odd y coordinate. Fail if R is not on the curve.
43+
* Signature is valid if R + h * Q + s * G == 0.
44+
*/
45+
46+
static int secp256k1_schnorr_sig_sign(const secp256k1_ecmult_gen_context_t* ctx, unsigned char *sig64, const secp256k1_scalar_t *key, secp256k1_scalar_t *nonce, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32) {
47+
secp256k1_gej_t Rj;
48+
secp256k1_ge_t Ra;
49+
unsigned char h32[32];
50+
secp256k1_scalar_t h, s;
51+
int overflow;
52+
53+
if (secp256k1_scalar_is_zero(key) || secp256k1_scalar_is_zero(nonce)) {
54+
return 0;
55+
}
56+
57+
secp256k1_ecmult_gen(ctx, &Rj, nonce);
58+
secp256k1_ge_set_gej(&Ra, &Rj);
59+
secp256k1_fe_normalize(&Ra.y);
60+
if (secp256k1_fe_is_odd(&Ra.y)) {
61+
secp256k1_scalar_negate(nonce, nonce);
62+
}
63+
secp256k1_fe_normalize(&Ra.x);
64+
secp256k1_fe_get_b32(sig64, &Ra.x);
65+
hash(h32, sig64, msg32);
66+
overflow = 0;
67+
secp256k1_scalar_set_b32(&h, h32, &overflow);
68+
if (overflow || secp256k1_scalar_is_zero(&h)) {
69+
return 0;
70+
}
71+
secp256k1_scalar_mul(&s, &h, key);
72+
secp256k1_scalar_negate(&s, &s);
73+
secp256k1_scalar_add(&s, &s, nonce);
74+
secp256k1_scalar_get_b32(sig64 + 32, &s);
75+
return 1;
76+
}
77+
78+
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) {
79+
secp256k1_gej_t Qj, Rj;
80+
secp256k1_ge_t Ra;
81+
secp256k1_fe_t Rx;
82+
secp256k1_scalar_t h, s;
83+
unsigned char hh[32];
84+
int overflow;
85+
86+
if (secp256k1_ge_is_infinity(pubkey)) {
87+
return 0;
88+
}
89+
hash(hh, sig64, msg32);
90+
overflow = 0;
91+
secp256k1_scalar_set_b32(&h, hh, &overflow);
92+
if (overflow || secp256k1_scalar_is_zero(&h)) {
93+
return 0;
94+
}
95+
overflow = 0;
96+
secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
97+
if (overflow) {
98+
return 0;
99+
}
100+
if (!secp256k1_fe_set_b32(&Rx, sig64)) {
101+
return 0;
102+
}
103+
secp256k1_gej_set_ge(&Qj, pubkey);
104+
secp256k1_ecmult(ctx, &Rj, &Qj, &h, &s);
105+
if (secp256k1_gej_is_infinity(&Rj)) {
106+
return 0;
107+
}
108+
secp256k1_ge_set_gej_var(&Ra, &Rj);
109+
secp256k1_fe_normalize_var(&Ra.y);
110+
if (secp256k1_fe_is_odd(&Ra.y)) {
111+
return 0;
112+
}
113+
return secp256k1_fe_equal_var(&Rx, &Ra.x);
114+
}
115+
116+
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) {
117+
secp256k1_gej_t Qj, Rj;
118+
secp256k1_ge_t Ra;
119+
secp256k1_fe_t Rx;
120+
secp256k1_scalar_t h, s;
121+
unsigned char hh[32];
122+
int overflow;
123+
124+
hash(hh, sig64, msg32);
125+
overflow = 0;
126+
secp256k1_scalar_set_b32(&h, hh, &overflow);
127+
if (overflow || secp256k1_scalar_is_zero(&h)) {
128+
return 0;
129+
}
130+
overflow = 0;
131+
secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
132+
if (overflow) {
133+
return 0;
134+
}
135+
if (!secp256k1_fe_set_b32(&Rx, sig64)) {
136+
return 0;
137+
}
138+
if (!secp256k1_ge_set_xo_var(&Ra, &Rx, 0)) {
139+
return 0;
140+
}
141+
secp256k1_gej_set_ge(&Rj, &Ra);
142+
secp256k1_scalar_inverse_var(&h, &h);
143+
secp256k1_scalar_negate(&s, &s);
144+
secp256k1_scalar_mul(&s, &s, &h);
145+
secp256k1_ecmult(ctx, &Qj, &Rj, &h, &s);
146+
if (secp256k1_gej_is_infinity(&Qj)) {
147+
return 0;
148+
}
149+
secp256k1_ge_set_gej(pubkey, &Qj);
150+
return 1;
151+
}
152+
153+
#endif

0 commit comments

Comments
 (0)