Skip to content

Commit 9a04c7c

Browse files
committed
Add support for custom EC-Schnorr-SHA256 signatures
1 parent d84a378 commit 9a04c7c

File tree

13 files changed

+948
-13
lines changed

13 files changed

+948
-13
lines changed

.travis.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ compiler:
88
- gcc
99
env:
1010
global:
11-
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=no ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no
11+
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=no ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=NO
1212
matrix:
1313
- SCALAR=32bit
1414
- SCALAR=32bit FIELD=32bit ECDH=yes
@@ -18,10 +18,10 @@ env:
1818
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes
1919
- FIELD=64bit ASM=x86_64
2020
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
21-
- FIELD=32bit
21+
- FIELD=32bit SCHNORR=yes
2222
- FIELD=32bit ENDOMORPHISM=yes
2323
- BIGNUM=no
24-
- BIGNUM=no ENDOMORPHISM=yes
24+
- BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes
2525
- BIGNUM=no STATICPRECOMPUTATION=no
2626
- BUILD=distcheck
2727
- EXTRAFLAGS=CFLAGS=-DDETERMINISTIC
@@ -58,5 +58,5 @@ before_script: ./autogen.sh
5858
script:
5959
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
6060
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
61-
- ./configure --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
61+
- ./configure --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
6262
os: linux

Makefile.am

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,7 @@ EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h
101101
if ENABLE_MODULE_ECDH
102102
include src/modules/ecdh/Makefile.am.include
103103
endif
104+
105+
if ENABLE_MODULE_SCHNORR
106+
include src/modules/schnorr/Makefile.am.include
107+
endif

configure.ac

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ AC_ARG_ENABLE(module_ecdh,
107107
[enable_module_ecdh=$enableval],
108108
[enable_module_ecdh=no])
109109

110+
AC_ARG_ENABLE(module_schnorr,
111+
AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (default is no)]),
112+
[enable_module_schnorr=$enableval],
113+
[enable_module_schnorr=no])
114+
110115
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
111116
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
112117

@@ -324,6 +329,10 @@ if test x"$enable_module_ecdh" = x"yes"; then
324329
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
325330
fi
326331

332+
if test x"$enable_module_schnorr" = x"yes"; then
333+
AC_DEFINE(ENABLE_MODULE_SCHNORR, 1, [Define this symbol to enable the Schnorr signature module])
334+
fi
335+
327336
AC_C_BIGENDIAN()
328337

329338
AC_MSG_NOTICE([Using assembly optimizations: $set_asm])
@@ -333,6 +342,8 @@ AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
333342
AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
334343
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
335344

345+
AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
346+
336347
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
337348
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
338349
AC_SUBST(SECP_INCLUDES)
@@ -343,6 +354,7 @@ AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
343354
AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
344355
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$use_ecmult_static_precomputation" = x"yes"])
345356
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
357+
AM_CONDITIONAL([ENABLE_MODULE_SCHNORR], [test x"$enable_module_schnorr" = x"yes"])
346358

347359
dnl make sure nothing new is exported so that we don't break the cache
348360
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"

include/secp256k1.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
252252
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail.
253253
* In: msg32: the 32-byte message hash being verified (will not be NULL)
254254
* key32: pointer to a 32-byte secret key (will not be NULL)
255+
* algo16: pointer to a 16-byte array describing the signature
256+
* algorithm (will be NULL for ECDSA for compatibility).
255257
* attempt: how many iterations we have tried to find a nonce.
256258
* This will almost always be 0, but different attempt values
257259
* are required to result in a different nonce.
@@ -264,6 +266,7 @@ typedef int (*secp256k1_nonce_function_t)(
264266
unsigned char *nonce32,
265267
const unsigned char *msg32,
266268
const unsigned char *key32,
269+
const unsigned char *algo16,
267270
unsigned int attempt,
268271
const void *data
269272
);
@@ -425,6 +428,23 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
425428
const unsigned char *seed32
426429
) SECP256K1_ARG_NONNULL(1);
427430

431+
/** Add a number of public keys together.
432+
* Returns: 1: the sum of the public keys is valid.
433+
* 0: the sum of the public keys is not valid.
434+
* In: ctx: pointer to a context object
435+
* out: pointer to pubkey for placing the resulting public key
436+
* (cannot be NULL)
437+
* n: the number of public keys to add together (must be at least 1)
438+
* ins: pointer to array of pointers to public keys (cannot be NULL)
439+
* Use secp256k1_ec_pubkey_compress and secp256k1_ec_pubkey_decompress if the
440+
* uncompressed format is needed.
441+
*/
442+
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine(
443+
const secp256k1_context_t* ctx,
444+
secp256k1_pubkey_t *out,
445+
int n,
446+
const secp256k1_pubkey_t * const * ins
447+
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
428448

429449
# ifdef __cplusplus
430450
}

include/secp256k1_schnorr.h

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#ifndef _SECP256K1_SCHNORR_
2+
# define _SECP256K1_SCHNORR_
3+
4+
# include "secp256k1.h"
5+
6+
# ifdef __cplusplus
7+
extern "C" {
8+
# endif
9+
10+
/** Create a signature using a custom EC-Schnorr-SHA256 construction. It
11+
* produces non-malleable 64-byte signatures which support public key recovery
12+
* batch validation, and multiparty signing.
13+
* Returns: 1: signature created
14+
* 0: the nonce generation function failed, or the private key was
15+
* invalid.
16+
* In: ctx: pointer to a context object, initialized for signing
17+
* (cannot be NULL)
18+
* msg32: the 32-byte message hash being signed (cannot be NULL)
19+
* seckey: pointer to a 32-byte secret key (cannot be NULL)
20+
* noncefp:pointer to a nonce generation function. If NULL,
21+
* secp256k1_nonce_function_default is used
22+
* ndata: pointer to arbitrary data used by the nonce generation
23+
* function (can be NULL)
24+
* Out: sig64: pointer to a 64-byte array where the signature will be
25+
* placed (cannot be NULL)
26+
*/
27+
int secp256k1_schnorr_sign(
28+
const secp256k1_context_t* ctx,
29+
const unsigned char *msg32,
30+
unsigned char *sig64,
31+
const unsigned char *seckey,
32+
secp256k1_nonce_function_t noncefp,
33+
const void *ndata
34+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
35+
36+
/** Verify a signature created by secp256k1_schnorr_sign.
37+
* Returns: 1: correct signature
38+
* 0: incorrect signature
39+
* In: ctx: a secp256k1 context object, initialized for verification.
40+
* msg32: the 32-byte message hash being verified (cannot be NULL)
41+
* sig64: the 64-byte signature being verified (cannot be NULL)
42+
* pubkey: the public key to verify with (cannot be NULL)
43+
*/
44+
SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_verify(
45+
const secp256k1_context_t* ctx,
46+
const unsigned char *msg32,
47+
const unsigned char *sig64,
48+
const secp256k1_pubkey_t *pubkey
49+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
50+
51+
/** Recover an EC public key from a Schnorr signature created using
52+
* secp256k1_schnorr_sign.
53+
* Returns: 1: public key successfully recovered (which guarantees a correct
54+
* signature).
55+
* 0: otherwise.
56+
* In: ctx: pointer to a context object, initialized for
57+
* verification (cannot be NULL)
58+
* msg32: the 32-byte message hash assumed to be signed (cannot
59+
* be NULL)
60+
* sig64: signature as 64 byte array (cannot be NULL)
61+
* Out: pubkey: pointer to a pubkey to set to the recovered public key
62+
* (cannot be NULL).
63+
*/
64+
int secp256k1_schnorr_recover(
65+
const secp256k1_context_t* ctx,
66+
const unsigned char *msg32,
67+
const unsigned char *sig64,
68+
secp256k1_pubkey_t *pubkey
69+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
70+
71+
/** Generate a nonce pair deterministically for use with
72+
* secp256k1_schnorr_partial_sign.
73+
* Returns: 1: valid nonce pair was generated.
74+
* 0: otherwise (nonce generation function failed)
75+
* In: ctx: pointer to a context object, initialized for signing
76+
* (cannot be NULL)
77+
* msg32: the 32-byte message hash assumed to be signed (cannot
78+
* be NULL)
79+
* sec32: the 32-byte private key (cannot be NULL)
80+
* noncefp: pointer to a nonce generation function. If NULL,
81+
* secp256k1_nonce_function_default is used
82+
* noncedata: pointer to arbitrary data used by the nonce generation
83+
* function (can be NULL)
84+
* Out: pubnonce: public side of the nonce (cannot be NULL)
85+
* privnonce32: private side of the nonce (32 byte) (cannot be NULL)
86+
*
87+
* Do not use the output as a private/public key pair for signing/validation.
88+
*/
89+
int secp256k1_schnorr_generate_nonce_pair(
90+
const secp256k1_context_t* ctx,
91+
const unsigned char *msg32,
92+
const unsigned char *sec32,
93+
secp256k1_nonce_function_t noncefp,
94+
const void* noncedata,
95+
secp256k1_pubkey_t *pubnonce,
96+
unsigned char *privnonce32
97+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7);
98+
99+
/** Produce a partial Schnorr signature, which can be combined using
100+
* secp256k1_schnorr_partial_combine, to end up with a full signature that is
101+
* verifiable using secp256k1_schnorr_verify.
102+
* Returns: 1: signature created succesfully.
103+
* 0: no valid signature exists with this combination of keys, nonces
104+
* and message (chance around 1 in 2^128)
105+
* -1: invalid private key, nonce, or public nonces.
106+
* In: ctx: pointer to context object, initialized for signing (cannot
107+
* be NULL)
108+
* msg32: pointer to 32-byte message to sign
109+
* sec32: pointer to 32-byte private key
110+
* secnonce32: pointer to 32-byte array containing our nonce
111+
* pubnonce_others: pointer to pubkey containing the sum of the other's
112+
* nonces (see secp256k1_ec_pubkey_combine)
113+
* Out: sig64: pointer to 64-byte array to put partial signature in
114+
*
115+
* The intended procedure for creating a multiparty signature is:
116+
* - Each signer S[i] with private key x[i] and public key Q[i] runs
117+
* secp256k1_schnorr_generate_nonce_pair to produce a pair (k[i],R[i]) of
118+
* private/public nonces.
119+
* - All signers communicate their public nonces to each other (revealing your
120+
* private nonce can lead to discovery of your private key, so it should be
121+
* considered secret).
122+
* - All signers combine all the public nonces they received (excluding their
123+
* own) using secp256k1_ec_pubkey_combine to obtain an
124+
* Rall[i] = sum(R[0..i-1,i+1..n]).
125+
* - All signers produce a partial signature using
126+
* secp256k1_schnorr_partial_sign, passing in their own private key x[i],
127+
* their own private nonce k[i], and the sum of the others' public nonces
128+
* Rall[i].
129+
* - All signers communicate their partial signatures to each other.
130+
* - Someone combines all partial signatures using
131+
* secp256k1_schnorr_partial_combine, to obtain a full signature.
132+
* - The resulting signature is validatable using secp256k1_schnorr_verify, with
133+
* public key equal to the result of secp256k1_ec_pubkey_combine of the
134+
* signers' public keys (sum(Q[0..n])).
135+
*
136+
* Note that secp256k1_schnorr_partial_combine and secp256k1_ec_pubkey_combine
137+
* function take their arguments in any order, and it is possible to
138+
* pre-combine several inputs already with one call, and add more inputs later
139+
* by calling the function again (they are commutative and associative).
140+
*/
141+
SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_sign(
142+
const secp256k1_context_t* ctx,
143+
const unsigned char *msg32,
144+
unsigned char *sig64,
145+
const unsigned char *sec32,
146+
const unsigned char *secnonce32,
147+
const secp256k1_pubkey_t *pubnonce_others
148+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
149+
150+
/** Combine multiple Schnorr partial signatures.
151+
* Returns: 1: the passed signatures were succesfully combined.
152+
* 0: the resulting signature is not valid (chance of 1 in 2^256)
153+
* -1: some inputs were invalid, or the signatures were not created
154+
* using the same set of nonces
155+
* In: ctx: pointer to a context object
156+
* sig64: pointer to a 64-byte array to place the combined signature
157+
* (cannot be NULL)
158+
* n: the number of signatures to combine (at least 1)
159+
* Out: sig64sin: pointer to an array of n pointers to 64-byte input
160+
* signatures
161+
*/
162+
SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_combine(
163+
const secp256k1_context_t* ctx,
164+
unsigned char *sig64,
165+
int n,
166+
const unsigned char * const * sig64sin
167+
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
168+
169+
# ifdef __cplusplus
170+
}
171+
# endif
172+
173+
#endif

src/bench_schnorr_verify.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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 "include/secp256k1_schnorr.h"
12+
#include "util.h"
13+
#include "bench.h"
14+
15+
typedef struct {
16+
unsigned char key[32];
17+
unsigned char sig[64];
18+
unsigned char pubkey[33];
19+
int pubkeylen;
20+
} benchmark_schnorr_sig_t;
21+
22+
typedef struct {
23+
secp256k1_context_t *ctx;
24+
unsigned char msg[32];
25+
benchmark_schnorr_sig_t sigs[64];
26+
int numsigs;
27+
} benchmark_schnorr_verify_t;
28+
29+
static void benchmark_schnorr_init(void* arg) {
30+
int i, k;
31+
benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg;
32+
33+
for (i = 0; i < 32; i++) data->msg[i] = 1 + i;
34+
for (k = 0; k < data->numsigs; k++) {
35+
secp256k1_pubkey_t pubkey;
36+
for (i = 0; i < 32; i++) data->sigs[k].key[i] = 33 + i + k;
37+
secp256k1_schnorr_sign(data->ctx, data->msg, data->sigs[k].sig, data->sigs[k].key, NULL, NULL);
38+
data->sigs[k].pubkeylen = 33;
39+
CHECK(secp256k1_ec_pubkey_create(data->ctx, &pubkey, data->sigs[k].key));
40+
CHECK(secp256k1_ec_pubkey_serialize(data->ctx, data->sigs[k].pubkey, &data->sigs[k].pubkeylen, &pubkey, 1));
41+
}
42+
}
43+
44+
static void benchmark_schnorr_verify(void* arg) {
45+
int i;
46+
benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg;
47+
48+
for (i = 0; i < 20000 / data->numsigs; i++) {
49+
secp256k1_pubkey_t pubkey;
50+
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
51+
CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->sigs[0].pubkey, data->sigs[0].pubkeylen));
52+
CHECK(secp256k1_schnorr_verify(data->ctx, data->msg, data->sigs[0].sig, &pubkey) == ((i & 0xFF) == 0));
53+
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
54+
}
55+
}
56+
57+
58+
59+
int main(void) {
60+
benchmark_schnorr_verify_t data;
61+
62+
data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
63+
64+
data.numsigs = 1;
65+
run_benchmark("schnorr_verify", benchmark_schnorr_verify, benchmark_schnorr_init, NULL, &data, 10, 20000);
66+
67+
secp256k1_context_destroy(data.ctx);
68+
return 0;
69+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
include_HEADERS += include/secp256k1_schnorr.h
2+
noinst_HEADERS += src/modules/schnorr/main_impl.h
3+
noinst_HEADERS += src/modules/schnorr/schnorr.h
4+
noinst_HEADERS += src/modules/schnorr/schnorr_impl.h
5+
noinst_HEADERS += src/modules/schnorr/tests_impl.h
6+
if USE_BENCHMARK
7+
noinst_PROGRAMS += bench_schnorr_verify
8+
bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c
9+
bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
10+
bench_schnorr_verify_LDFLAGS = -static
11+
endif

0 commit comments

Comments
 (0)