Skip to content

Commit

Permalink
Add support for Schnorr signatures.
Browse files Browse the repository at this point in the history
  • Loading branch information
sipa committed Jun 25, 2015
1 parent 873a453 commit af94dbd
Show file tree
Hide file tree
Showing 7 changed files with 463 additions and 1 deletion.
7 changes: 6 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ noinst_HEADERS += src/hash_impl.h
noinst_HEADERS += src/field.h
noinst_HEADERS += src/field_impl.h
noinst_HEADERS += src/bench.h
noinst_HEADERS += src/schnorr.h
noinst_HEADERS += src/schnorr_impl.h

pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsecp256k1.pc
Expand All @@ -49,10 +51,13 @@ libsecp256k1_la_LIBADD = $(SECP_LIBS)

noinst_PROGRAMS =
if USE_BENCHMARK
noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal
noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal bench_schnorr_verify
bench_verify_SOURCES = src/bench_verify.c
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
bench_verify_LDFLAGS = -static
bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c
bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
bench_schnorr_verify_LDFLAGS = -static
bench_recover_SOURCES = src/bench_recover.c
bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS)
bench_recover_LDFLAGS = -static
Expand Down
55 changes: 55 additions & 0 deletions include/secp256k1.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,61 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
const unsigned char *seed32
) SECP256K1_ARG_NONNULL(1);

/** Create a signature using an experimental custom EC-Schnorr-SHA256 construction. It produces non-malleable 64-byte signatures which are recoverable and batch-validatable.
* Returns: 1: signature created
* 0: the nonce generation function failed, or the private key was invalid.
* In: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* msg32: the 32-byte message hash being signed (cannot be NULL)
* seckey: pointer to a 32-byte secret key (cannot be NULL)
* noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
* ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
* Out: sig64: pointer to a 64-byte array where the signature will be placed (cannot be NULL)
*/
int secp256k1_schnorr_sign(
const secp256k1_context_t* ctx,
const unsigned char *msg32,
unsigned char *sig64,
const unsigned char *seckey,
secp256k1_nonce_function_t noncefp,
const void *ndata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Verify a signature created by secp256k1_schnorr_sign.
* Returns: 1: public key and signature correct
* 0: incorrect signature
* -1: invalid public key
* In: ctx: a secp256k1 context object, initialized for verification.
* msg32: the 32-byte message hash being verified (cannot be NULL)
* sig64: the 64-byte signature being verified (cannot be NULL)
* pubkey: the public key to verify with (cannot be NULL)
* pubkeylen: the length of pubkey
*/
SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_verify(
const secp256k1_context_t* ctx,
const unsigned char *msg32,
const unsigned char *sig64,
const unsigned char *pubkey,
int pubkeylen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Recover an EC public key from a Schnorr signature created using secp256k1_schnorr_sign.
* Returns: 1: public key successfully recovered (which guarantees a correct signature).
* 0: otherwise.
* In: ctx: pointer to a context object, initialized for verification (cannot be NULL)
* msg32: the 32-byte message hash assumed to be signed (cannot be NULL)
* sig64: signature as 64 byte array (cannot be NULL)
* compressed: whether to recover a compressed or uncompressed pubkey
* Out: pubkey: pointer to a 33 or 65 byte array to put the pubkey (cannot be NULL)
* pubkeylen: pointer to an int that will contain the pubkey length (cannot be NULL)
*/
int secp256k1_schnorr_recover(
const secp256k1_context_t* ctx,
const unsigned char *msg32,
const unsigned char *sig64,
unsigned char *pubkey,
int *pubkeylen,
int compressed
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

# ifdef __cplusplus
}
Expand Down
64 changes: 64 additions & 0 deletions src/bench_schnorr_verify.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**********************************************************************
* Copyright (c) 2014 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/

#include <stdio.h>
#include <string.h>

#include "include/secp256k1.h"
#include "util.h"
#include "bench.h"

typedef struct {
unsigned char key[32];
unsigned char sig[64];
unsigned char pubkey[33];
int pubkeylen;
} benchmark_schnorr_sig_t;

typedef struct {
secp256k1_context_t *ctx;
unsigned char msg[32];
benchmark_schnorr_sig_t sigs[64];
int numsigs;
} benchmark_schnorr_verify_t;

static void benchmark_schnorr_init(void* arg) {
int i, k;
benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg;

for (i = 0; i < 32; i++) data->msg[i] = 1 + i;
for (k = 0; k < data->numsigs; k++) {
for (i = 0; i < 32; i++) data->sigs[k].key[i] = 33 + i + k;
secp256k1_schnorr_sign(data->ctx, data->msg, data->sigs[k].sig, data->sigs[k].key, NULL, NULL);
data->sigs[k].pubkeylen = 33;
CHECK(secp256k1_ec_pubkey_create(data->ctx, data->sigs[k].pubkey, &data->sigs[k].pubkeylen, data->sigs[k].key, 1));
}
}

static void benchmark_schnorr_verify(void* arg) {
int i;
benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg;

for (i = 0; i < 20000 / data->numsigs; i++) {
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
CHECK(secp256k1_schnorr_verify(data->ctx, data->msg, data->sigs[0].sig, data->sigs[0].pubkey, data->sigs[0].pubkeylen) == ((i & 0xFF) == 0));
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
}
}



int main(void) {
benchmark_schnorr_verify_t data;

data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);

data.numsigs = 1;
run_benchmark("schnorr_verify", benchmark_schnorr_verify, benchmark_schnorr_init, NULL, &data, 10, 20000);

secp256k1_context_destroy(data.ctx);
return 0;
}
18 changes: 18 additions & 0 deletions src/schnorr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/***********************************************************************
* Copyright (c) 2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php. *
***********************************************************************/

#ifndef _SECP256K1_SCHNORR_
#define _SECP256K1_SCHNORR_

#include "scalar.h"
#include "group.h"

typedef void (*secp256k1_schnorr_msghash_t)(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32);

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);
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);

#endif
153 changes: 153 additions & 0 deletions src/schnorr_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/***********************************************************************
* Copyright (c) 2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php. *
***********************************************************************/

#ifndef _SECP256K1_SCHNORR_IMPL_H_
#define _SECP256K1_SCHNORR_IMPL_H_

#include <string.h>

#include "schnorr.h"
#include "num.h"
#include "field.h"
#include "group.h"
#include "ecmult.h"
#include "ecmult_gen.h"

/**
* Custom Schnorr-based signature scheme:
*
* Signing:
* Inputs: 32-byte message m, 32-byte scalar key x (!=0), 32-byte scalar nonce k (!=0)
*
* Compute point R = k * G. Reject nonce if R's y coordinate is odd (or negate nonce).
* Compute 32-byte r, the serialization of R's x coordinate.
* Compute scalar h = Hash(r || m). Reject nonce if h == 0 or h >= order.
* Compute scalar s = k - h * x.
* The signature is (r, s).
*
*
* Verification:
* Inputs: 32-byte message m, public key point Q, signature: (32-byte r, scalar s)
*
* Signature is invalid if s >= order.
* Signature is invalid if r >= p.
* Compute scalar h = Hash(r || m). Signature is invalid if h == 0 or h >= order.
* Option 1 (faster for single verification):
* Compute point R = h * Q + s * G. Signature is invalid if R is infinity or R's y coordinate is odd.
* Signature is valid if the serialization of R's x coordinate equals r.
* Option 2 (allows batch validation and pubkey recovery):
* Decompress x coordinate r into point R, with odd y coordinate. Fail if R is not on the curve.
* Signature is valid if R + h * Q + s * G == 0.
*/

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) {
secp256k1_gej_t Rj;
secp256k1_ge_t Ra;
unsigned char h32[32];
secp256k1_scalar_t h, s;
int overflow;

if (secp256k1_scalar_is_zero(key) || secp256k1_scalar_is_zero(nonce)) {
return 0;
}

secp256k1_ecmult_gen(ctx, &Rj, nonce);
secp256k1_ge_set_gej(&Ra, &Rj);
secp256k1_fe_normalize(&Ra.y);
if (secp256k1_fe_is_odd(&Ra.y)) {
secp256k1_scalar_negate(nonce, nonce);
}
secp256k1_fe_normalize(&Ra.x);
secp256k1_fe_get_b32(sig64, &Ra.x);
hash(h32, sig64, msg32);
overflow = 0;
secp256k1_scalar_set_b32(&h, h32, &overflow);
if (overflow || secp256k1_scalar_is_zero(&h)) {
return 0;
}
secp256k1_scalar_mul(&s, &h, key);
secp256k1_scalar_negate(&s, &s);
secp256k1_scalar_add(&s, &s, nonce);
secp256k1_scalar_get_b32(sig64 + 32, &s);
return 1;
}

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) {
secp256k1_gej_t Qj, Rj;
secp256k1_ge_t Ra;
secp256k1_fe_t Rx;
secp256k1_scalar_t h, s;
unsigned char hh[32];
int overflow;

if (secp256k1_ge_is_infinity(pubkey)) {
return 0;
}
hash(hh, sig64, msg32);
overflow = 0;
secp256k1_scalar_set_b32(&h, hh, &overflow);
if (overflow || secp256k1_scalar_is_zero(&h)) {
return 0;
}
overflow = 0;
secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
if (overflow) {
return 0;
}
if (!secp256k1_fe_set_b32(&Rx, sig64)) {
return 0;
}
secp256k1_gej_set_ge(&Qj, pubkey);
secp256k1_ecmult(ctx, &Rj, &Qj, &h, &s);
if (secp256k1_gej_is_infinity(&Rj)) {
return 0;
}
secp256k1_ge_set_gej_var(&Ra, &Rj);
secp256k1_fe_normalize_var(&Ra.y);
if (secp256k1_fe_is_odd(&Ra.y)) {
return 0;
}
return secp256k1_fe_equal_var(&Rx, &Ra.x);
}

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) {
secp256k1_gej_t Qj, Rj;
secp256k1_ge_t Ra;
secp256k1_fe_t Rx;
secp256k1_scalar_t h, s;
unsigned char hh[32];
int overflow;

hash(hh, sig64, msg32);
overflow = 0;
secp256k1_scalar_set_b32(&h, hh, &overflow);
if (overflow || secp256k1_scalar_is_zero(&h)) {
return 0;
}
overflow = 0;
secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
if (overflow) {
return 0;
}
if (!secp256k1_fe_set_b32(&Rx, sig64)) {
return 0;
}
if (!secp256k1_ge_set_xo_var(&Ra, &Rx, 0)) {
return 0;
}
secp256k1_gej_set_ge(&Rj, &Ra);
secp256k1_scalar_inverse_var(&h, &h);
secp256k1_scalar_negate(&s, &s);
secp256k1_scalar_mul(&s, &s, &h);
secp256k1_ecmult(ctx, &Qj, &Rj, &h, &s);
if (secp256k1_gej_is_infinity(&Qj)) {
return 0;
}
secp256k1_ge_set_gej(pubkey, &Qj);
return 1;
}

#endif
Loading

0 comments on commit af94dbd

Please sign in to comment.