Skip to content

Commit 255401b

Browse files
committed
Add support for Schnorr signatures.
1 parent f2991d1 commit 255401b

File tree

7 files changed

+894
-12
lines changed

7 files changed

+894
-12
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: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
9999
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail.
100100
* In: msg32: the 32-byte message hash being verified (will not be NULL)
101101
* key32: pointer to a 32-byte secret key (will not be NULL)
102+
* algo: pointer to a 0-delimited string with the signing algorithm
103+
* name (empty for ECDSA, at most 32 characters).
102104
* attempt: how many iterations we have tried to find a nonce.
103105
* This will almost always be 0, but different attempt values
104106
* are required to result in a different nonce.
@@ -111,6 +113,7 @@ typedef int (*secp256k1_nonce_function_t)(
111113
unsigned char *nonce32,
112114
const unsigned char *msg32,
113115
const unsigned char *key32,
116+
const char *algo,
114117
unsigned int attempt,
115118
const void *data
116119
);
@@ -341,6 +344,194 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
341344
const unsigned char *seed32
342345
) SECP256K1_ARG_NONNULL(1);
343346

347+
/** Create a signature using a custom EC-Schnorr-SHA256 construction. It
348+
* produces non-malleable 64-byte signatures which support public key recovery
349+
* batch validation, and multiparty signing.
350+
* Returns: 1: signature created
351+
* 0: the nonce generation function failed, or the private key was
352+
* invalid.
353+
* In: ctx: pointer to a context object, initialized for signing
354+
* (cannot be NULL)
355+
* msg32: the 32-byte message hash being signed (cannot be NULL)
356+
* seckey: pointer to a 32-byte secret key (cannot be NULL)
357+
* noncefp:pointer to a nonce generation function. If NULL,
358+
* secp256k1_nonce_function_default is used
359+
* ndata: pointer to arbitrary data used by the nonce generation
360+
* function (can be NULL)
361+
* Out: sig64: pointer to a 64-byte array where the signature will be
362+
* placed (cannot be NULL)
363+
*/
364+
int secp256k1_schnorr_sign(
365+
const secp256k1_context_t* ctx,
366+
const unsigned char *msg32,
367+
unsigned char *sig64,
368+
const unsigned char *seckey,
369+
secp256k1_nonce_function_t noncefp,
370+
const void *ndata
371+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
372+
373+
/** Verify a signature created by secp256k1_schnorr_sign.
374+
* Returns: 1: public key and signature correct
375+
* 0: incorrect signature
376+
* -1: invalid public key
377+
* In: ctx: a secp256k1 context object, initialized for verification.
378+
* msg32: the 32-byte message hash being verified (cannot be NULL)
379+
* sig64: the 64-byte signature being verified (cannot be NULL)
380+
* pubkey: the public key to verify with (cannot be NULL)
381+
* pubkeylen: the length of pubkey
382+
*/
383+
SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_verify(
384+
const secp256k1_context_t* ctx,
385+
const unsigned char *msg32,
386+
const unsigned char *sig64,
387+
const unsigned char *pubkey,
388+
int pubkeylen
389+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
390+
391+
/** Recover an EC public key from a Schnorr signature created using
392+
* secp256k1_schnorr_sign.
393+
* Returns: 1: public key successfully recovered (which guarantees a correct
394+
* signature).
395+
* 0: otherwise.
396+
* In: ctx: pointer to a context object, initialized for
397+
* verification (cannot be NULL)
398+
* msg32: the 32-byte message hash assumed to be signed (cannot
399+
* be NULL)
400+
* sig64: signature as 64 byte array (cannot be NULL)
401+
* compressed: whether to recover a compressed or uncompressed pubkey
402+
* Out: pubkey: pointer to a 33 or 65 byte array to put the pubkey
403+
* (cannot be NULL)
404+
* pubkeylen: pointer to an int that will contain the pubkey length
405+
* (cannot be NULL)
406+
*/
407+
int secp256k1_schnorr_recover(
408+
const secp256k1_context_t* ctx,
409+
const unsigned char *msg32,
410+
const unsigned char *sig64,
411+
unsigned char *pubkey,
412+
int *pubkeylen,
413+
int compressed
414+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
415+
416+
/** Generate a nonce pair deterministically for use with
417+
* secp256k1_schnorr_partial_sign.
418+
* Returns: 1: valid nonce pair was generated.
419+
* 0: otherwise (nonce generation function failed)
420+
* In: ctx: pointer to a context object, initialized for signing
421+
* (cannot be NULL)
422+
* msg32: the 32-byte message hash assumed to be signed (cannot
423+
* be NULL)
424+
* sec32: the 32-byte private key (cannot be NULL)
425+
* noncefp: pointer to a nonce generation function. If NULL,
426+
* secp256k1_nonce_function_default is used
427+
* noncedata: pointer to arbitrary data used by the nonce generation
428+
* function (can be NULL)
429+
* Out: pubnonce33: public side of the nonce (represented as a 33-byte
430+
* compressed public key) (cannot be NULL)
431+
* privnonce32:private side of the nonce (32 byte) (cannot be NULL)
432+
*
433+
* Do not use the output as a private/public key pair for signing/validation.
434+
*/
435+
int secp256k1_schnorr_generate_nonce_pair(
436+
const secp256k1_context_t* ctx,
437+
const unsigned char *msg32,
438+
const unsigned char *sec32,
439+
secp256k1_nonce_function_t noncefp,
440+
const void* noncedata,
441+
unsigned char *pubnonce33,
442+
unsigned char *privnonce32
443+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7);
444+
445+
/** Add a number of public keys together.
446+
* Returns: 1: the sum of the public keys is valid.
447+
* 0: the sum of the public keys is not valid.
448+
* -1: one or more of the inputs were invalid.
449+
* In: ctx: pointer to a context object
450+
* out: pointer to 33-byte array for placing the resulting
451+
* public key (in compressed format) (cannot be NULL)
452+
* n: the number of public keys to add together (must be at least 1)
453+
* ins: pointer to array of pointers to public keys in
454+
* compressed format (cannot be NULL)
455+
* Use secp256k1_ec_pubkey_compress and secp256k1_ec_pubkey_decompress if the
456+
* uncompressed format is needed.
457+
*/
458+
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine(
459+
const secp256k1_context_t* ctx,
460+
unsigned char *out,
461+
int n,
462+
const unsigned char **ins
463+
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
464+
465+
/** Produce a partial Schnorr signature, which can be combined using
466+
* secp256k1_schnorr_partial_combine, to end up with a full signature that is
467+
* verifiable using secp256k1_schnorr_verify.
468+
* Returns: 1: signature created succesfully.
469+
* 0: no valid signature exists with this combination of keys, nonces
470+
* and message (chance around 1 in 2^128)
471+
* -1: invalid private key, nonce, or public nonces.
472+
* In: ctx: pointer to context object, initialized for signing (cannot
473+
* be NULL)
474+
* msg32: pointer to 32-byte message to sign
475+
* sec32: pointer to 32-byte private key
476+
* secnonce32: pointer to 32-byte array containing our nonce
477+
* pubnonce33: pointer to 33-byte array containing the sum of the other's
478+
* nonces (see secp256k1_ec_pubkey_combine)
479+
* Out: sig64: pointer to 64-byte array to put partial signature in
480+
*
481+
* The intended procedure for creating a multiparty signature is:
482+
* - Each signer S[i] with private key x[i] and public key Q[i] runs
483+
* secp256k1_schnorr_generate_nonce_pair to produce a pair (k[i],R[i]) of
484+
* private/public nonces.
485+
* - All signers communicate their public nonces to each other (revealing your
486+
* private nonce can lead to discovery of your private key, so it should be
487+
* considered secret).
488+
* - All signers combine all the public nonces they received (excluding their
489+
* own) using secp256k1_ec_pubkey_combine to obtain an
490+
* Rall[i] = sum(R[0..i-1,i+1..n]).
491+
* - All signers produce a partial signature using
492+
* secp256k1_schnorr_partial_sign, passing in their own private key x[i],
493+
* their own private nonce k[i], and the sum of the others' public nonces
494+
* Rall[i].
495+
* - All signers communicate their partial signatures to each other.
496+
* - Someone combines all partial signatures using
497+
* secp256k1_schnorr_partial_combine, to obtain a full signature.
498+
* - The resulting signature is validatable using secp256k1_schnorr_verify, with
499+
* public key equal to the result of secp256k1_ec_pubkey_combine of the
500+
* signers' public keys (sum(Q[0..n])).
501+
*
502+
* Note that secp256k1_schnorr_partial_combine and secp256k1_ec_pubkey_combine
503+
* function take their arguments in any order, and it is possible to
504+
* pre-combine several inputs already with one call, and add more inputs later
505+
* by calling the function again.
506+
*/
507+
SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_sign(
508+
const secp256k1_context_t* ctx,
509+
const unsigned char *msg32,
510+
unsigned char *sig64,
511+
const unsigned char *sec32,
512+
const unsigned char *secnonce32,
513+
const unsigned char *pubnonce33
514+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
515+
516+
/** Combine multiple Schnorr partial signatures.
517+
* Returns: 1: the passed signatures were succesfully combined.
518+
* 0: some signatures cancel eachother out (chance of 1 in 2^128 per
519+
* signature)
520+
* -1: some inputs were invalid, or the signatures were not created
521+
* using the same set of nonces
522+
* In: ctx: pointer to a context object
523+
* sig64: pointer to a 64-byte array to place the combined signature
524+
* (cannot be NULL)
525+
* n: the number of signatures to combine (at least 1)
526+
* Out: sig64sin: pointer to an array of n pointers to 64-byte input
527+
* signatures
528+
*/
529+
SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_combine(
530+
const secp256k1_context_t* ctx,
531+
unsigned char *sig64,
532+
int n,
533+
const unsigned char **sig64sin
534+
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
344535

345536
# ifdef __cplusplus
346537
}

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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/***********************************************************************
2+
* Copyright (c) 2014-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, const secp256k1_ge_t *pubnonce, 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_recover(const secp256k1_ecmult_context_t* ctx, const unsigned char *sig64, secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32);
18+
19+
#endif

0 commit comments

Comments
 (0)