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 Apr 23, 2015
1 parent c2a7efe commit 8fcf74b
Show file tree
Hide file tree
Showing 9 changed files with 701 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
3 changes: 3 additions & 0 deletions include/secp256k1.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,9 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
const unsigned char *seed32
) SECP256K1_ARG_NONNULL(1);

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* noncedata);
int secp256k1_schnorr_verify(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig64, const unsigned char *pubkey, int pubkeylen);
int secp256k1_schnorr_verify_batch(const secp256k1_context_t* ctx, int n, const unsigned char *msg32, const unsigned char **sig64, const unsigned char **pubkey, const int *pubkeylen);

# ifdef __cplusplus
}
Expand Down
96 changes: 96 additions & 0 deletions src/bench_schnorr_verify.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**********************************************************************
* 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);
}
}

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

const unsigned char *sig_ptr[64];
const unsigned char *pubkey_ptr[64];
int pubkeylen[64];

for (k = 0; k < data->numsigs; k++) {
sig_ptr[k] = &data->sigs[k].sig[0];
pubkey_ptr[k] = &data->sigs[k].pubkey[0];
pubkeylen[k] = data->sigs[k].pubkeylen;
}

for (i = 0; i < 20000 / data->numsigs; i++) {
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
CHECK(secp256k1_schnorr_verify_batch(data->ctx, data->numsigs, data->msg, sig_ptr, pubkey_ptr, 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);
run_benchmark("schnorr_verify_batch1", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
data.numsigs = 2;
run_benchmark("schnorr_verify_batch2", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
data.numsigs = 4;
run_benchmark("schnorr_verify_batch4", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
data.numsigs = 8;
run_benchmark("schnorr_verify_batch8", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
data.numsigs = 16;
run_benchmark("schnorr_verify_batch16", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);
data.numsigs = 32;
run_benchmark("schnorr_verify_batch32", benchmark_schnorr_verify_batch, benchmark_schnorr_init, NULL, &data, 10, 20000);

secp256k1_context_destroy(data.ctx);
return 0;
}
4 changes: 4 additions & 0 deletions src/ecmult.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,8 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context_t *c
/** Double multiply: R = na*A + ng*G */
static void secp256k1_ecmult(const secp256k1_ecmult_context_t *ctx, secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng);

static void secp256k1_ecmult_mult(const secp256k1_ecmult_context_t *ctx, int points, secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng);

#define MAX_MULTI 64

#endif
163 changes: 163 additions & 0 deletions src/ecmult_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,49 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej_t *prej, s
#endif
}

/** Fill a table 'prej' with a concatenation of precomputed off multiples of the
* points in a. Prej will contain the values
* [1*a[0],3*a[0],...,(2*n-1)*a[0],1*a[1],3*a[1],...,(2*n-1)*a[k-1]], so it
* needs space for k * n values. zr[0] will contain prej[0].z / a[0].z. The
* other zr[i] values = prej[i].z / prej[i-1].z. */
static void secp256k1_ecmult_multi_odd_multiples_table(int k, int n, secp256k1_gej_t *prej, secp256k1_fe_t *zr, const secp256k1_gej_t *a) {
int j;
for (j = 0; j < k; j++) {
secp256k1_gej_t aa;
secp256k1_fe_t z2, z3;
if (j != 0) {
/* Make the Z coordinate of each input a known multiple of the
* last prej output of the previous input point. */
secp256k1_fe_sqr(&z2, &prej[n * j - 1].z);
secp256k1_fe_mul(&z3, &z2, &prej[n * j - 1].z);
secp256k1_fe_mul(&aa.x, &a[j].x, &z2);
secp256k1_fe_mul(&aa.y, &a[j].y, &z3);
secp256k1_fe_mul(&aa.z, &a[j].z, &prej[n * j - 1].z);
aa.infinity = 0;
} else {
aa = a[0];
}
secp256k1_ecmult_odd_multiples_table(n, &prej[n * j], &zr[n * j], &aa);
if (j != 0) {
/* Correct the first Z ratio output of this point, by multiplying it
* with the current point's input Z coordinate, chaining them
* together */
secp256k1_fe_mul(zr + n * j, zr + n * j, &a[j].z);
}
}
}

/** Fill a table 'pre' with precomputed odd multiples of a.
*
* There are two versions of this function:
* - secp256k1_ecmult_odd_multiples_table_globalz_windowa which brings its
* resulting point set to a single constant Z denominator, stores the X and Y
* coordinates as ge_storage points in pre, and stores the global Z in rz.
* It only operates on tables sized for WINDOW_A wnaf multiples.
* - secp256k1_ecmult_multi_odd_multiples_table_globalz_windowa which is
* identical to secp256k1_ecmult_odd_multiples_table_globalz_windowa, but
* works on several input points at once, and brings them all to a single
* global Z.
* - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its
* resulting point set to actually affine points, and stores those in pre.
* It operates on tables of any size, but uses heap-allocated temporaries.
Expand All @@ -109,6 +145,16 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge_t
secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr);
}

static void secp256k1_ecmult_multi_odd_multiples_table_globalz_windowa(int k, secp256k1_ge_t *pre, secp256k1_fe_t *globalz, const secp256k1_gej_t *a) {
secp256k1_gej_t prej[MAX_MULTI * ECMULT_TABLE_SIZE(WINDOW_A)];
secp256k1_fe_t zr[MAX_MULTI * ECMULT_TABLE_SIZE(WINDOW_A)];

/* Compute the odd multiples of all inputs in Jacobian form. */
secp256k1_ecmult_multi_odd_multiples_table(k, ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a);
/* Bring them to the same Z denominator. */
secp256k1_ge_globalz_set_table_gej(k * ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr);
}

static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage_t *pre, const secp256k1_gej_t *a) {
secp256k1_gej_t *prej = checked_malloc(sizeof(secp256k1_gej_t) * n);
secp256k1_ge_t *prea = checked_malloc(sizeof(secp256k1_ge_t) * n);
Expand Down Expand Up @@ -389,4 +435,121 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context_t *ctx, secp256k1_ge
}
}

static void secp256k1_ecmult_mult(const secp256k1_ecmult_context_t *ctx, int points, secp256k1_gej_t *r, const secp256k1_gej_t *a, const secp256k1_scalar_t *na, const secp256k1_scalar_t *ng) {
secp256k1_ge_t pre_a[MAX_MULTI][ECMULT_TABLE_SIZE(WINDOW_A)];
secp256k1_ge_t tmpa;
secp256k1_fe_t Z;
#ifdef USE_ENDOMORPHISM
secp256k1_ge_t pre_a_lam[MAX_MULTI][ECMULT_TABLE_SIZE(WINDOW_A)];
secp256k1_scalar_t na_1[MAX_MULTI], na_lam[MAX_MULTI];
/* Splitted G factors. */
secp256k1_scalar_t ng_1, ng_128;
int wnaf_na_1[MAX_MULTI][130];
int wnaf_na_lam[MAX_MULTI][130];
int bits_na_1[MAX_MULTI];
int bits_na_lam[MAX_MULTI];
int wnaf_ng_1[129];
int bits_ng_1;
int wnaf_ng_128[129];
int bits_ng_128;
#else
int wnaf_na[MAX_MULTI][256];
int bits_na[MAX_MULTI];
int wnaf_ng[257];
int bits_ng;
#endif
int i;
int bits = 0;
int k;

VERIFY_CHECK(points >= 1);
VERIFY_CHECK(points <= MAX_MULTI);

for (k = 0; k < points; k++) {
#ifdef USE_ENDOMORPHISM
/* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
secp256k1_scalar_split_lambda_var(&na_1[k], &na_lam[k], &na[k]);

/* build wnaf representation for na_1 and na_lam. */
bits_na_1[k] = secp256k1_ecmult_wnaf(wnaf_na_1[k], &na_1[k], WINDOW_A);
bits_na_lam[k] = secp256k1_ecmult_wnaf(wnaf_na_lam[k], &na_lam[k], WINDOW_A);
VERIFY_CHECK(bits_na_1[k] <= 130);
VERIFY_CHECK(bits_na_lam[k] <= 130);
if (bits_na_1[k] > bits) bits = bits_na_1[k];
if (bits_na_lam[k] > bits) bits = bits_na_lam[k];
#else
/* build wnaf representation for na. */
bits_na[k] = secp256k1_ecmult_wnaf(wnaf_na[k], &na[k], WINDOW_A);
if (bits_na[k] > bits) bits = bits_na[k];
#endif
}

/* calculate odd multiples of all a's */
secp256k1_ecmult_multi_odd_multiples_table_globalz_windowa(points, &pre_a[0][0], &Z, a);

#ifdef USE_ENDOMORPHISM
for (k = 0; k < points; k++) {
for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
secp256k1_ge_mul_lambda(&pre_a_lam[k][i], &pre_a[k][i]);
}
}
#endif

#ifdef USE_ENDOMORPHISM
/* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */
secp256k1_scalar_split_128(&ng_1, &ng_128, ng);

/* Build wnaf representation for ng_1 and ng_128 */
bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, &ng_1, WINDOW_G);
bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, &ng_128, WINDOW_G);
if (bits_ng_1 > bits) bits = bits_ng_1;
if (bits_ng_128 > bits) bits = bits_ng_128;
#else
bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, ng, WINDOW_G);
if (bits_ng > bits) bits = bits_ng;
#endif

secp256k1_gej_set_infinity(r);

for (i = bits-1; i >= 0; i--) {
int n;
secp256k1_gej_double_var(r, r, NULL);
#ifdef USE_ENDOMORPHISM
for (k = 0; k < points; k++) {
if (i < bits_na_1[k] && (n = wnaf_na_1[k][i])) {
ECMULT_TABLE_GET_GE(&tmpa, pre_a[k], n, WINDOW_A);
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
}
if (i < bits_na_lam[k] && (n = wnaf_na_lam[k][i])) {
ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam[k], n, WINDOW_A);
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
}
}
if (i < bits_ng_1 && (n = wnaf_ng_1[i])) {
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G);
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
}
if (i < bits_ng_128 && (n = wnaf_ng_128[i])) {
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g_128, n, WINDOW_G);
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
}
#else
for (k = 0; k < points; k++) {
if (i < bits_na[k] && (n = wnaf_na[k][i])) {
ECMULT_TABLE_GET_GE(&tmpa, pre_a[k], n, WINDOW_A);
secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
}
}
if (i < bits_ng && (n = wnaf_ng[i])) {
ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G);
secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
}
#endif
}

if (!r->infinity) {
secp256k1_fe_mul(&r->z, &r->z, &Z);
}
}

#endif
19 changes: 19 additions & 0 deletions src/schnorr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/***********************************************************************
* 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, const 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);
static int secp256k1_schnorr_sig_verify_batch(const secp256k1_ecmult_context_t* ctx, int n, unsigned char (* const sig64)[64], const secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32);

#endif
Loading

0 comments on commit 8fcf74b

Please sign in to comment.