Skip to content

Commit

Permalink
Add schnorrsig module which implements BIP-schnorr [0] compatible sig…
Browse files Browse the repository at this point in the history
…ning, verification and batch verification.

[0] https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki
  • Loading branch information
apoelstra authored and jonasnick committed Nov 1, 2018
1 parent 7bca01d commit bd39a05
Show file tree
Hide file tree
Showing 10 changed files with 1,314 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
bench_inv
bench_ecdh
bench_ecmult
bench_schnorrsig
bench_sign
bench_verify
bench_schnorr_verify
bench_recover
bench_internal
tests
Expand Down
4 changes: 4 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ if ENABLE_MODULE_ECDH
include src/modules/ecdh/Makefile.am.include
endif

if ENABLE_MODULE_SCHNORRSIG
include src/modules/schnorrsig/Makefile.am.include
endif

if ENABLE_MODULE_RECOVERY
include src/modules/recovery/Makefile.am.include
endif
14 changes: 14 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ AC_ARG_ENABLE(module_ecdh,
[enable_module_ecdh=$enableval],
[enable_module_ecdh=no])

AC_ARG_ENABLE(module_schnorrsig,
AS_HELP_STRING([--enable-module-schnorrsig],[enable schnorrsig module (experimental)]),
[enable_module_schnorrsig=$enableval],
[enable_module_schnorrsig=no])

AC_ARG_ENABLE(module_recovery,
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]),
[enable_module_recovery=$enableval],
Expand Down Expand Up @@ -431,6 +436,10 @@ if test x"$enable_module_ecdh" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
fi

if test x"$enable_module_schnorrsig" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module])
fi

if test x"$enable_module_recovery" = x"yes"; then
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
fi
Expand Down Expand Up @@ -458,11 +467,15 @@ if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([WARNING: experimental build])
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
AC_MSG_NOTICE([Building schnorrsig module: $enable_module_schnorrsig])
AC_MSG_NOTICE([******])
else
if test x"$enable_module_ecdh" = x"yes"; then
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_schnorrsig" = x"yes"; then
AC_MSG_ERROR([schnorrsig module is experimental. Use --enable-experimental to allow.])
fi
if test x"$set_asm" = x"arm"; then
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
fi
Expand All @@ -481,6 +494,7 @@ AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"])
AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
Expand Down
118 changes: 118 additions & 0 deletions include/secp256k1_schnorrsig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#ifndef SECP256K1_SCHNORRSIG_H
#define SECP256K1_SCHNORRSIG_H

/** This module implements a variant of Schnorr signatures compliant with
* BIP-schnorr
* (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki).
*/

/** Opaque data structure that holds a parsed Schnorr signature.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
* If you need to convert to a format suitable for storage, transmission, or
* comparison, use the `secp256k1_schnorrsig_serialize` and
* `secp256k1_schnorrsig_parse` functions.
*/
typedef struct {
unsigned char data[64];
} secp256k1_schnorrsig;

/** Serialize a Schnorr signature.
*
* Returns: 1
* Args: ctx: a secp256k1 context object
* Out: out64: pointer to a 64-byte array to store the serialized signature
* In: sig: pointer to the signature
*
* See secp256k1_schnorrsig_parse for details about the encoding.
*/
SECP256K1_API int secp256k1_schnorrsig_serialize(
const secp256k1_context* ctx,
unsigned char *out64,
const secp256k1_schnorrsig* sig
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Parse a Schnorr signature.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
* Args: ctx: a secp256k1 context object
* Out: sig: pointer to a signature object
* In: in64: pointer to the 64-byte signature to be parsed
*
* The signature is serialized in the form R||s, where R is a 32-byte public
* key (x-coordinate only; the y-coordinate is considered to be the unique
* y-coordinate satisfying the curve equation that is a quadratic residue)
* and s is a 32-byte big-endian scalar.
*
* After the call, sig will always be initialized. If parsing failed or the
* encoded numbers are out of range, signature validation with it is
* guaranteed to fail for every message and public key.
*/
SECP256K1_API int secp256k1_schnorrsig_parse(
const secp256k1_context* ctx,
secp256k1_schnorrsig* sig,
const unsigned char *in64
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Create a Schnorr signature.
*
* Returns 1 on success, 0 on failure.
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* Out: sig: pointer to the returned signature (cannot be NULL)
* negated_nonce: a pointer to an integer indicates if signing algorithm negated the
* nonce (can be NULL)
* In: 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_bipschnorr is used
* ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
*/
SECP256K1_API int secp256k1_schnorrsig_sign(
const secp256k1_context* ctx,
secp256k1_schnorrsig *sig,
int *negated_nonce,
const unsigned char *msg32,
const unsigned char *seckey,
secp256k1_nonce_function noncefp,
void *ndata
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

/** Verify a Schnorr signature.
*
* Returns: 1: correct signature
* 0: incorrect or unparseable signature
* Args: ctx: a secp256k1 context object, initialized for verification.
* In: sig: the signature being verified (cannot be NULL)
* msg32: the 32-byte message hash being verified (cannot be NULL)
* pubkey: pointer to a public key to verify with (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
const secp256k1_context* ctx,
const secp256k1_schnorrsig *sig,
const unsigned char *msg32,
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Verifies a set of Schnorr signatures.
*
* Returns 1 if all succeeded, 0 otherwise. In particular, returns 1 if n_sigs is 0.
*
* Args: ctx: a secp256k1 context object, initialized for verification.
* scratch: scratch space used for the multiexponentiation
* In: sig: array of signatures, or NULL if there are no signatures
* msg32: array of messages, or NULL if there are no signatures
* pk: array of public keys, or NULL if there are no signatures
* n_sigs: number of signatures in above arrays. Must be smaller than
* 2^31 and smaller than half the maximum size_t value. Must be 0
* if above arrays are NULL.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch(
const secp256k1_context* ctx,
secp256k1_scratch_space *scratch,
const secp256k1_schnorrsig *const *sig,
const unsigned char *const *msg32,
const secp256k1_pubkey *const *pk,
size_t n_sigs
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
#endif
128 changes: 128 additions & 0 deletions src/bench_schnorrsig.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/**********************************************************************
* Copyright (c) 2018 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/

#include <string.h>
#include <stdlib.h>

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

#define MAX_SIGS (32768)

typedef struct {
secp256k1_context *ctx;
secp256k1_scratch_space *scratch;
size_t n;
const unsigned char **pk;
const secp256k1_schnorrsig **sigs;
const unsigned char **msgs;
} bench_schnorrsig_data;

void bench_schnorrsig_sign(void* arg) {
bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
size_t i;
unsigned char sk[32] = "benchmarkexample secrettemplate";
unsigned char msg[32] = "benchmarkexamplemessagetemplate";
secp256k1_schnorrsig sig;

for (i = 0; i < 1000; i++) {
msg[0] = i;
msg[1] = i >> 8;
sk[0] = i;
sk[1] = i >> 8;
CHECK(secp256k1_schnorrsig_sign(data->ctx, &sig, NULL, msg, sk, NULL, NULL));
}
}

void bench_schnorrsig_verify(void* arg) {
bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
size_t i;

for (i = 0; i < 1000; i++) {
secp256k1_pubkey pk;
CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pk, data->pk[i], 33) == 1);
CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk));
}
}

void bench_schnorrsig_verify_n(void* arg) {
bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
size_t i, j;
const secp256k1_pubkey **pk = (const secp256k1_pubkey **)malloc(data->n * sizeof(*pk));

CHECK(pk != NULL);
for (j = 0; j < MAX_SIGS/data->n; j++) {
for (i = 0; i < data->n; i++) {
secp256k1_pubkey *pk_nonconst = (secp256k1_pubkey *)malloc(sizeof(*pk_nonconst));
CHECK(secp256k1_ec_pubkey_parse(data->ctx, pk_nonconst, data->pk[i], 33) == 1);
pk[i] = pk_nonconst;
}
CHECK(secp256k1_schnorrsig_verify_batch(data->ctx, data->scratch, data->sigs, data->msgs, pk, data->n));
for (i = 0; i < data->n; i++) {
free((void *)pk[i]);
}
}
free(pk);
}

int main(void) {
size_t i;
bench_schnorrsig_data data;

data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
data.scratch = secp256k1_scratch_space_create(data.ctx, 1024 * 1024 * 1024);
data.pk = (const unsigned char **)malloc(MAX_SIGS * sizeof(unsigned char *));
data.msgs = (const unsigned char **)malloc(MAX_SIGS * sizeof(unsigned char *));
data.sigs = (const secp256k1_schnorrsig **)malloc(MAX_SIGS * sizeof(secp256k1_schnorrsig *));

for (i = 0; i < MAX_SIGS; i++) {
unsigned char sk[32];
unsigned char *msg = (unsigned char *)malloc(32);
secp256k1_schnorrsig *sig = (secp256k1_schnorrsig *)malloc(sizeof(*sig));
unsigned char *pk_char = (unsigned char *)malloc(33);
secp256k1_pubkey pk;
size_t pk_len = 33;
msg[0] = sk[0] = i;
msg[1] = sk[1] = i >> 8;
msg[2] = sk[2] = i >> 16;
msg[3] = sk[3] = i >> 24;
memset(&msg[4], 'm', 28);
memset(&sk[4], 's', 28);

data.pk[i] = pk_char;
data.msgs[i] = msg;
data.sigs[i] = sig;

CHECK(secp256k1_ec_pubkey_create(data.ctx, &pk, sk));
CHECK(secp256k1_ec_pubkey_serialize(data.ctx, pk_char, &pk_len, &pk, SECP256K1_EC_COMPRESSED) == 1);
CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, NULL, msg, sk, NULL, NULL));
}

run_benchmark("schnorrsig_sign", bench_schnorrsig_sign, NULL, NULL, (void *) &data, 10, 1000);
run_benchmark("schnorrsig_verify", bench_schnorrsig_verify, NULL, NULL, (void *) &data, 10, 1000);
for (i = 1; i <= MAX_SIGS; i *= 2) {
char name[64];
sprintf(name, "schnorrsig_batch_verify_%d", (int) i);

data.n = i;
run_benchmark(name, bench_schnorrsig_verify_n, NULL, NULL, (void *) &data, 3, MAX_SIGS);
}

for (i = 0; i < MAX_SIGS; i++) {
free((void *)data.pk[i]);
free((void *)data.msgs[i]);
free((void *)data.sigs[i]);
}
free(data.pk);
free(data.msgs);
free(data.sigs);

secp256k1_scratch_space_destroy(data.scratch);
secp256k1_context_destroy(data.ctx);
return 0;
}
8 changes: 8 additions & 0 deletions src/modules/schnorrsig/Makefile.am.include
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
include_HEADERS += include/secp256k1_schnorrsig.h
noinst_HEADERS += src/modules/schnorrsig/main_impl.h
noinst_HEADERS += src/modules/schnorrsig/tests_impl.h
if USE_BENCHMARK
noinst_PROGRAMS += bench_schnorrsig
bench_schnorrsig_SOURCES = src/bench_schnorrsig.c
bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
endif
Loading

0 comments on commit bd39a05

Please sign in to comment.