-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add schnorrsig module which implements BIP-schnorr [0] compatible sig…
…ning, verification and batch verification. [0] https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki
- Loading branch information
Showing
10 changed files
with
1,314 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.