Skip to content

Commit b458e1e

Browse files
committed
Add support for Schnorr signatures.
1 parent 17f7148 commit b458e1e

File tree

7 files changed

+870
-1
lines changed

7 files changed

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

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

345528
# ifdef __cplusplus
346529
}

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) 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+
18+
#endif

0 commit comments

Comments
 (0)