Skip to content

Commit bd39a05

Browse files
apoelstrajonasnick
authored andcommitted
Add schnorrsig module which implements BIP-schnorr [0] compatible signing, verification and batch verification.
[0] https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki
1 parent 7bca01d commit bd39a05

10 files changed

+1314
-1
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
bench_inv
22
bench_ecdh
33
bench_ecmult
4+
bench_schnorrsig
45
bench_sign
56
bench_verify
6-
bench_schnorr_verify
77
bench_recover
88
bench_internal
99
tests

Makefile.am

+4
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ if ENABLE_MODULE_ECDH
178178
include src/modules/ecdh/Makefile.am.include
179179
endif
180180

181+
if ENABLE_MODULE_SCHNORRSIG
182+
include src/modules/schnorrsig/Makefile.am.include
183+
endif
184+
181185
if ENABLE_MODULE_RECOVERY
182186
include src/modules/recovery/Makefile.am.include
183187
endif

configure.ac

+14
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ AC_ARG_ENABLE(module_ecdh,
129129
[enable_module_ecdh=$enableval],
130130
[enable_module_ecdh=no])
131131

132+
AC_ARG_ENABLE(module_schnorrsig,
133+
AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]),
134+
[enable_module_schnorrsig=$enableval],
135+
[enable_module_schnorrsig=no])
136+
132137
AC_ARG_ENABLE(module_recovery,
133138
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]),
134139
[enable_module_recovery=$enableval],
@@ -431,6 +436,10 @@ if test x"$enable_module_ecdh" = x"yes"; then
431436
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
432437
fi
433438

439+
if test x"$enable_module_schnorrsig" = x"yes"; then
440+
AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module])
441+
fi
442+
434443
if test x"$enable_module_recovery" = x"yes"; then
435444
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
436445
fi
@@ -458,11 +467,15 @@ if test x"$enable_experimental" = x"yes"; then
458467
AC_MSG_NOTICE([WARNING: experimental build])
459468
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
460469
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
470+
AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig])
461471
AC_MSG_NOTICE([******])
462472
else
463473
if test x"$enable_module_ecdh" = x"yes"; then
464474
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
465475
fi
476+
if test x"$enable_module_schnorrsig" = x"yes"; then
477+
AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.])
478+
fi
466479
if test x"$set_asm" = x"arm"; then
467480
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
468481
fi
@@ -481,6 +494,7 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"])
481494
AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
482495
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
483496
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
497+
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
484498
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
485499
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
486500
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])

include/secp256k1_schnorrsig.h

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#ifndef SECP256K1_SCHNORRSIG_H
2+
#define SECP256K1_SCHNORRSIG_H
3+
4+
/** This module implements a variant of Schnorr signatures compliant with
5+
* BIP-schnorr
6+
* (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki).
7+
*/
8+
9+
/** Opaque data structure that holds a parsed Schnorr signature.
10+
*
11+
* The exact representation of data inside is implementation defined and not
12+
* guaranteed to be portable between different platforms or versions. It is
13+
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
14+
* If you need to convert to a format suitable for storage, transmission, or
15+
* comparison, use the `secp256k1_schnorrsig_serialize` and
16+
* `secp256k1_schnorrsig_parse` functions.
17+
*/
18+
typedef struct {
19+
unsigned char data[64];
20+
} secp256k1_schnorrsig;
21+
22+
/** Serialize a Schnorr signature.
23+
*
24+
* Returns: 1
25+
* Args: ctx: a secp256k1 context object
26+
* Out: out64: pointer to a 64-byte array to store the serialized signature
27+
* In: sig: pointer to the signature
28+
*
29+
* See secp256k1_schnorrsig_parse for details about the encoding.
30+
*/
31+
SECP256K1_API int secp256k1_schnorrsig_serialize(
32+
const secp256k1_context* ctx,
33+
unsigned char *out64,
34+
const secp256k1_schnorrsig* sig
35+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
36+
37+
/** Parse a Schnorr signature.
38+
*
39+
* Returns: 1 when the signature could be parsed, 0 otherwise.
40+
* Args: ctx: a secp256k1 context object
41+
* Out: sig: pointer to a signature object
42+
* In: in64: pointer to the 64-byte signature to be parsed
43+
*
44+
* The signature is serialized in the form R||s, where R is a 32-byte public
45+
* key (x-coordinate only; the y-coordinate is considered to be the unique
46+
* y-coordinate satisfying the curve equation that is a quadratic residue)
47+
* and s is a 32-byte big-endian scalar.
48+
*
49+
* After the call, sig will always be initialized. If parsing failed or the
50+
* encoded numbers are out of range, signature validation with it is
51+
* guaranteed to fail for every message and public key.
52+
*/
53+
SECP256K1_API int secp256k1_schnorrsig_parse(
54+
const secp256k1_context* ctx,
55+
secp256k1_schnorrsig* sig,
56+
const unsigned char *in64
57+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
58+
59+
/** Create a Schnorr signature.
60+
*
61+
* Returns 1 on success, 0 on failure.
62+
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
63+
* Out: sig: pointer to the returned signature (cannot be NULL)
64+
* negated_nonce: a pointer to an integer indicates if signing algorithm negated the
65+
* nonce (can be NULL)
66+
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
67+
* seckey: pointer to a 32-byte secret key (cannot be NULL)
68+
* noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bipschnorr is used
69+
* ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
70+
*/
71+
SECP256K1_API int secp256k1_schnorrsig_sign(
72+
const secp256k1_context* ctx,
73+
secp256k1_schnorrsig *sig,
74+
int *negated_nonce,
75+
const unsigned char *msg32,
76+
const unsigned char *seckey,
77+
secp256k1_nonce_function noncefp,
78+
void *ndata
79+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
80+
81+
/** Verify a Schnorr signature.
82+
*
83+
* Returns: 1: correct signature
84+
* 0: incorrect or unparseable signature
85+
* Args: ctx: a secp256k1 context object, initialized for verification.
86+
* In: sig: the signature being verified (cannot be NULL)
87+
* msg32: the 32-byte message hash being verified (cannot be NULL)
88+
* pubkey: pointer to a public key to verify with (cannot be NULL)
89+
*/
90+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
91+
const secp256k1_context* ctx,
92+
const secp256k1_schnorrsig *sig,
93+
const unsigned char *msg32,
94+
const secp256k1_pubkey *pubkey
95+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
96+
97+
/** Verifies a set of Schnorr signatures.
98+
*
99+
* Returns 1 if all succeeded, 0 otherwise. In particular, returns 1 if n_sigs is 0.
100+
*
101+
* Args: ctx: a secp256k1 context object, initialized for verification.
102+
* scratch: scratch space used for the multiexponentiation
103+
* In: sig: array of signatures, or NULL if there are no signatures
104+
* msg32: array of messages, or NULL if there are no signatures
105+
* pk: array of public keys, or NULL if there are no signatures
106+
* n_sigs: number of signatures in above arrays. Must be smaller than
107+
* 2^31 and smaller than half the maximum size_t value. Must be 0
108+
* if above arrays are NULL.
109+
*/
110+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch(
111+
const secp256k1_context* ctx,
112+
secp256k1_scratch_space *scratch,
113+
const secp256k1_schnorrsig *const *sig,
114+
const unsigned char *const *msg32,
115+
const secp256k1_pubkey *const *pk,
116+
size_t n_sigs
117+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
118+
#endif

src/bench_schnorrsig.c

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/**********************************************************************
2+
* Copyright (c) 2018 Andrew Poelstra *
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 <string.h>
8+
#include <stdlib.h>
9+
10+
#include "include/secp256k1.h"
11+
#include "include/secp256k1_schnorrsig.h"
12+
#include "util.h"
13+
#include "bench.h"
14+
15+
#define MAX_SIGS (32768)
16+
17+
typedef struct {
18+
secp256k1_context *ctx;
19+
secp256k1_scratch_space *scratch;
20+
size_t n;
21+
const unsigned char **pk;
22+
const secp256k1_schnorrsig **sigs;
23+
const unsigned char **msgs;
24+
} bench_schnorrsig_data;
25+
26+
void bench_schnorrsig_sign(void* arg) {
27+
bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
28+
size_t i;
29+
unsigned char sk[32] = "benchmarkexample secrettemplate";
30+
unsigned char msg[32] = "benchmarkexamplemessagetemplate";
31+
secp256k1_schnorrsig sig;
32+
33+
for (i = 0; i < 1000; i++) {
34+
msg[0] = i;
35+
msg[1] = i >> 8;
36+
sk[0] = i;
37+
sk[1] = i >> 8;
38+
CHECK(secp256k1_schnorrsig_sign(data->ctx, &sig, NULL, msg, sk, NULL, NULL));
39+
}
40+
}
41+
42+
void bench_schnorrsig_verify(void* arg) {
43+
bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
44+
size_t i;
45+
46+
for (i = 0; i < 1000; i++) {
47+
secp256k1_pubkey pk;
48+
CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pk, data->pk[i], 33) == 1);
49+
CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk));
50+
}
51+
}
52+
53+
void bench_schnorrsig_verify_n(void* arg) {
54+
bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
55+
size_t i, j;
56+
const secp256k1_pubkey **pk = (const secp256k1_pubkey **)malloc(data->n * sizeof(*pk));
57+
58+
CHECK(pk != NULL);
59+
for (j = 0; j < MAX_SIGS/data->n; j++) {
60+
for (i = 0; i < data->n; i++) {
61+
secp256k1_pubkey *pk_nonconst = (secp256k1_pubkey *)malloc(sizeof(*pk_nonconst));
62+
CHECK(secp256k1_ec_pubkey_parse(data->ctx, pk_nonconst, data->pk[i], 33) == 1);
63+
pk[i] = pk_nonconst;
64+
}
65+
CHECK(secp256k1_schnorrsig_verify_batch(data->ctx, data->scratch, data->sigs, data->msgs, pk, data->n));
66+
for (i = 0; i < data->n; i++) {
67+
free((void *)pk[i]);
68+
}
69+
}
70+
free(pk);
71+
}
72+
73+
int main(void) {
74+
size_t i;
75+
bench_schnorrsig_data data;
76+
77+
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
78+
data.scratch = secp256k1_scratch_space_create(data.ctx, 1024 * 1024 * 1024);
79+
data.pk = (const unsigned char **)malloc(MAX_SIGS * sizeof(unsigned char *));
80+
data.msgs = (const unsigned char **)malloc(MAX_SIGS * sizeof(unsigned char *));
81+
data.sigs = (const secp256k1_schnorrsig **)malloc(MAX_SIGS * sizeof(secp256k1_schnorrsig *));
82+
83+
for (i = 0; i < MAX_SIGS; i++) {
84+
unsigned char sk[32];
85+
unsigned char *msg = (unsigned char *)malloc(32);
86+
secp256k1_schnorrsig *sig = (secp256k1_schnorrsig *)malloc(sizeof(*sig));
87+
unsigned char *pk_char = (unsigned char *)malloc(33);
88+
secp256k1_pubkey pk;
89+
size_t pk_len = 33;
90+
msg[0] = sk[0] = i;
91+
msg[1] = sk[1] = i >> 8;
92+
msg[2] = sk[2] = i >> 16;
93+
msg[3] = sk[3] = i >> 24;
94+
memset(&msg[4], 'm', 28);
95+
memset(&sk[4], 's', 28);
96+
97+
data.pk[i] = pk_char;
98+
data.msgs[i] = msg;
99+
data.sigs[i] = sig;
100+
101+
CHECK(secp256k1_ec_pubkey_create(data.ctx, &pk, sk));
102+
CHECK(secp256k1_ec_pubkey_serialize(data.ctx, pk_char, &pk_len, &pk, SECP256K1_EC_COMPRESSED) == 1);
103+
CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, NULL, msg, sk, NULL, NULL));
104+
}
105+
106+
run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, 1000);
107+
run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, 1000);
108+
for (i = 1; i <= MAX_SIGS; i *= 2) {
109+
char name[64];
110+
sprintf(name, "schnorrsig_batch_verify_%d", (int) i);
111+
112+
data.n = i;
113+
run_benchmark(name, bench_schnorrsig_verify_n, NULL, NULL, (void *) &data, 3, MAX_SIGS);
114+
}
115+
116+
for (i = 0; i < MAX_SIGS; i++) {
117+
free((void *)data.pk[i]);
118+
free((void *)data.msgs[i]);
119+
free((void *)data.sigs[i]);
120+
}
121+
free(data.pk);
122+
free(data.msgs);
123+
free(data.sigs);
124+
125+
secp256k1_scratch_space_destroy(data.scratch);
126+
secp256k1_context_destroy(data.ctx);
127+
return 0;
128+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
include_HEADERS += include/secp256k1_schnorrsig.h
2+
noinst_HEADERS += src/modules/schnorrsig/main_impl.h
3+
noinst_HEADERS += src/modules/schnorrsig/tests_impl.h
4+
if USE_BENCHMARK
5+
noinst_PROGRAMS += bench_schnorrsig
6+
bench_schnorrsig_SOURCES = src/bench_schnorrsig.c
7+
bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
8+
endif

0 commit comments

Comments
 (0)