Add support for custom EC-Schnorr-SHA256 signatures
This commit is contained in:
parent
d84a3784f4
commit
a5a66c70b6
|
@ -2,6 +2,7 @@ bench_inv
|
|||
bench_ecdh
|
||||
bench_sign
|
||||
bench_verify
|
||||
bench_verify_schnorr
|
||||
bench_recover
|
||||
bench_internal
|
||||
tests
|
||||
|
|
|
@ -8,7 +8,7 @@ compiler:
|
|||
- gcc
|
||||
env:
|
||||
global:
|
||||
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=no ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no
|
||||
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=no ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=NO
|
||||
matrix:
|
||||
- SCALAR=32bit
|
||||
- SCALAR=32bit FIELD=32bit ECDH=yes
|
||||
|
@ -18,10 +18,10 @@ env:
|
|||
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes
|
||||
- FIELD=64bit ASM=x86_64
|
||||
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
|
||||
- FIELD=32bit
|
||||
- FIELD=32bit SCHNORR=yes
|
||||
- FIELD=32bit ENDOMORPHISM=yes
|
||||
- BIGNUM=no
|
||||
- BIGNUM=no ENDOMORPHISM=yes
|
||||
- BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes
|
||||
- BIGNUM=no STATICPRECOMPUTATION=no
|
||||
- BUILD=distcheck
|
||||
- EXTRAFLAGS=CFLAGS=-DDETERMINISTIC
|
||||
|
@ -58,5 +58,5 @@ before_script: ./autogen.sh
|
|||
script:
|
||||
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
|
||||
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
|
||||
- ./configure --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
|
||||
- ./configure --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
|
||||
os: linux
|
||||
|
|
|
@ -101,3 +101,7 @@ EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h
|
|||
if ENABLE_MODULE_ECDH
|
||||
include src/modules/ecdh/Makefile.am.include
|
||||
endif
|
||||
|
||||
if ENABLE_MODULE_SCHNORR
|
||||
include src/modules/schnorr/Makefile.am.include
|
||||
endif
|
||||
|
|
12
configure.ac
12
configure.ac
|
@ -107,6 +107,11 @@ AC_ARG_ENABLE(module_ecdh,
|
|||
[enable_module_ecdh=$enableval],
|
||||
[enable_module_ecdh=no])
|
||||
|
||||
AC_ARG_ENABLE(module_schnorr,
|
||||
AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (default is no)]),
|
||||
[enable_module_schnorr=$enableval],
|
||||
[enable_module_schnorr=no])
|
||||
|
||||
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
|
||||
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
|
||||
|
||||
|
@ -324,6 +329,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_schnorr" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_MODULE_SCHNORR, 1, [Define this symbol to enable the Schnorr signature module])
|
||||
fi
|
||||
|
||||
AC_C_BIGENDIAN()
|
||||
|
||||
AC_MSG_NOTICE([Using assembly optimizations: $set_asm])
|
||||
|
@ -333,6 +342,8 @@ AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
|
|||
AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
|
||||
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
|
||||
|
||||
AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
|
||||
|
||||
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
|
||||
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
|
||||
AC_SUBST(SECP_INCLUDES)
|
||||
|
@ -343,6 +354,7 @@ AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
|
|||
AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
|
||||
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$use_ecmult_static_precomputation" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_SCHNORR], [test x"$enable_module_schnorr" = x"yes"])
|
||||
|
||||
dnl make sure nothing new is exported so that we don't break the cache
|
||||
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"
|
||||
|
|
|
@ -252,6 +252,8 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
|
|||
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail.
|
||||
* In: msg32: the 32-byte message hash being verified (will not be NULL)
|
||||
* key32: pointer to a 32-byte secret key (will not be NULL)
|
||||
* algo16: pointer to a 16-byte array describing the signature
|
||||
* algorithm (will be NULL for ECDSA for compatibility).
|
||||
* attempt: how many iterations we have tried to find a nonce.
|
||||
* This will almost always be 0, but different attempt values
|
||||
* are required to result in a different nonce.
|
||||
|
@ -264,6 +266,7 @@ typedef int (*secp256k1_nonce_function_t)(
|
|||
unsigned char *nonce32,
|
||||
const unsigned char *msg32,
|
||||
const unsigned char *key32,
|
||||
const unsigned char *algo16,
|
||||
unsigned int attempt,
|
||||
const void *data
|
||||
);
|
||||
|
@ -425,6 +428,23 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
|
|||
const unsigned char *seed32
|
||||
) SECP256K1_ARG_NONNULL(1);
|
||||
|
||||
/** Add a number of public keys together.
|
||||
* Returns: 1: the sum of the public keys is valid.
|
||||
* 0: the sum of the public keys is not valid.
|
||||
* In: ctx: pointer to a context object
|
||||
* out: pointer to pubkey for placing the resulting public key
|
||||
* (cannot be NULL)
|
||||
* n: the number of public keys to add together (must be at least 1)
|
||||
* ins: pointer to array of pointers to public keys (cannot be NULL)
|
||||
* Use secp256k1_ec_pubkey_compress and secp256k1_ec_pubkey_decompress if the
|
||||
* uncompressed format is needed.
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine(
|
||||
const secp256k1_context_t* ctx,
|
||||
secp256k1_pubkey_t *out,
|
||||
int n,
|
||||
const secp256k1_pubkey_t * const * ins
|
||||
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
#ifndef _SECP256K1_SCHNORR_
|
||||
# define _SECP256K1_SCHNORR_
|
||||
|
||||
# include "secp256k1.h"
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
/** Create a signature using a custom EC-Schnorr-SHA256 construction. It
|
||||
* produces non-malleable 64-byte signatures which support public key recovery
|
||||
* batch validation, and multiparty signing.
|
||||
* 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: correct signature
|
||||
* 0: incorrect signature
|
||||
* 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)
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_verify(
|
||||
const secp256k1_context_t* ctx,
|
||||
const unsigned char *msg32,
|
||||
const unsigned char *sig64,
|
||||
const secp256k1_pubkey_t *pubkey
|
||||
) 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)
|
||||
* Out: pubkey: pointer to a pubkey to set to the recovered public key
|
||||
* (cannot be NULL).
|
||||
*/
|
||||
int secp256k1_schnorr_recover(
|
||||
const secp256k1_context_t* ctx,
|
||||
const unsigned char *msg32,
|
||||
const unsigned char *sig64,
|
||||
secp256k1_pubkey_t *pubkey
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Generate a nonce pair deterministically for use with
|
||||
* secp256k1_schnorr_partial_sign.
|
||||
* Returns: 1: valid nonce pair was generated.
|
||||
* 0: otherwise (nonce generation function failed)
|
||||
* In: ctx: pointer to a context object, initialized for signing
|
||||
* (cannot be NULL)
|
||||
* msg32: the 32-byte message hash assumed to be signed (cannot
|
||||
* be NULL)
|
||||
* sec32: the 32-byte private key (cannot be NULL)
|
||||
* noncefp: pointer to a nonce generation function. If NULL,
|
||||
* secp256k1_nonce_function_default is used
|
||||
* noncedata: pointer to arbitrary data used by the nonce generation
|
||||
* function (can be NULL)
|
||||
* Out: pubnonce: public side of the nonce (cannot be NULL)
|
||||
* privnonce32: private side of the nonce (32 byte) (cannot be NULL)
|
||||
*
|
||||
* Do not use the output as a private/public key pair for signing/validation.
|
||||
*/
|
||||
int secp256k1_schnorr_generate_nonce_pair(
|
||||
const secp256k1_context_t* ctx,
|
||||
const unsigned char *msg32,
|
||||
const unsigned char *sec32,
|
||||
secp256k1_nonce_function_t noncefp,
|
||||
const void* noncedata,
|
||||
secp256k1_pubkey_t *pubnonce,
|
||||
unsigned char *privnonce32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7);
|
||||
|
||||
/** Produce a partial Schnorr signature, which can be combined using
|
||||
* secp256k1_schnorr_partial_combine, to end up with a full signature that is
|
||||
* verifiable using secp256k1_schnorr_verify.
|
||||
* Returns: 1: signature created succesfully.
|
||||
* 0: no valid signature exists with this combination of keys, nonces
|
||||
* and message (chance around 1 in 2^128)
|
||||
* -1: invalid private key, nonce, or public nonces.
|
||||
* In: ctx: pointer to context object, initialized for signing (cannot
|
||||
* be NULL)
|
||||
* msg32: pointer to 32-byte message to sign
|
||||
* sec32: pointer to 32-byte private key
|
||||
* secnonce32: pointer to 32-byte array containing our nonce
|
||||
* pubnonce_others: pointer to pubkey containing the sum of the other's
|
||||
* nonces (see secp256k1_ec_pubkey_combine)
|
||||
* Out: sig64: pointer to 64-byte array to put partial signature in
|
||||
*
|
||||
* The intended procedure for creating a multiparty signature is:
|
||||
* - Each signer S[i] with private key x[i] and public key Q[i] runs
|
||||
* secp256k1_schnorr_generate_nonce_pair to produce a pair (k[i],R[i]) of
|
||||
* private/public nonces.
|
||||
* - All signers communicate their public nonces to each other (revealing your
|
||||
* private nonce can lead to discovery of your private key, so it should be
|
||||
* considered secret).
|
||||
* - All signers combine all the public nonces they received (excluding their
|
||||
* own) using secp256k1_ec_pubkey_combine to obtain an
|
||||
* Rall[i] = sum(R[0..i-1,i+1..n]).
|
||||
* - All signers produce a partial signature using
|
||||
* secp256k1_schnorr_partial_sign, passing in their own private key x[i],
|
||||
* their own private nonce k[i], and the sum of the others' public nonces
|
||||
* Rall[i].
|
||||
* - All signers communicate their partial signatures to each other.
|
||||
* - Someone combines all partial signatures using
|
||||
* secp256k1_schnorr_partial_combine, to obtain a full signature.
|
||||
* - The resulting signature is validatable using secp256k1_schnorr_verify, with
|
||||
* public key equal to the result of secp256k1_ec_pubkey_combine of the
|
||||
* signers' public keys (sum(Q[0..n])).
|
||||
*
|
||||
* Note that secp256k1_schnorr_partial_combine and secp256k1_ec_pubkey_combine
|
||||
* function take their arguments in any order, and it is possible to
|
||||
* pre-combine several inputs already with one call, and add more inputs later
|
||||
* by calling the function again (they are commutative and associative).
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_sign(
|
||||
const secp256k1_context_t* ctx,
|
||||
const unsigned char *msg32,
|
||||
unsigned char *sig64,
|
||||
const unsigned char *sec32,
|
||||
const unsigned char *secnonce32,
|
||||
const secp256k1_pubkey_t *pubnonce_others
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
|
||||
|
||||
/** Combine multiple Schnorr partial signatures.
|
||||
* Returns: 1: the passed signatures were succesfully combined.
|
||||
* 0: the resulting signature is not valid (chance of 1 in 2^256)
|
||||
* -1: some inputs were invalid, or the signatures were not created
|
||||
* using the same set of nonces
|
||||
* In: ctx: pointer to a context object
|
||||
* sig64: pointer to a 64-byte array to place the combined signature
|
||||
* (cannot be NULL)
|
||||
* n: the number of signatures to combine (at least 1)
|
||||
* Out: sig64sin: pointer to an array of n pointers to 64-byte input
|
||||
* signatures
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_combine(
|
||||
const secp256k1_context_t* ctx,
|
||||
unsigned char *sig64,
|
||||
int n,
|
||||
const unsigned char * const * sig64sin
|
||||
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,69 @@
|
|||
/**********************************************************************
|
||||
* 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 "include/secp256k1_schnorr.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++) {
|
||||
secp256k1_pubkey_t pubkey;
|
||||
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, &pubkey, data->sigs[k].key));
|
||||
CHECK(secp256k1_ec_pubkey_serialize(data->ctx, data->sigs[k].pubkey, &data->sigs[k].pubkeylen, &pubkey, 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++) {
|
||||
secp256k1_pubkey_t pubkey;
|
||||
data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF);
|
||||
CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->sigs[0].pubkey, data->sigs[0].pubkeylen));
|
||||
CHECK(secp256k1_schnorr_verify(data->ctx, data->msg, data->sigs[0].sig, &pubkey) == ((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;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
include_HEADERS += include/secp256k1_schnorr.h
|
||||
noinst_HEADERS += src/modules/schnorr/main_impl.h
|
||||
noinst_HEADERS += src/modules/schnorr/schnorr.h
|
||||
noinst_HEADERS += src/modules/schnorr/schnorr_impl.h
|
||||
noinst_HEADERS += src/modules/schnorr/tests_impl.h
|
||||
if USE_BENCHMARK
|
||||
noinst_PROGRAMS += bench_schnorr_verify
|
||||
bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c
|
||||
bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
|
||||
bench_schnorr_verify_LDFLAGS = -static
|
||||
endif
|
|
@ -0,0 +1,163 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2014-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_MODULE_SCHNORR_MAIN
|
||||
#define SECP256K1_MODULE_SCHNORR_MAIN
|
||||
|
||||
#include "modules/schnorr/schnorr_impl.h"
|
||||
|
||||
static void secp256k1_schnorr_msghash_sha256(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) {
|
||||
secp256k1_sha256_t sha;
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, r32, 32);
|
||||
secp256k1_sha256_write(&sha, msg32, 32);
|
||||
secp256k1_sha256_finalize(&sha, h32);
|
||||
}
|
||||
|
||||
static const unsigned char secp256k1_schnorr_algo16[16] = "Schnorr+SHA256 ";
|
||||
|
||||
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) {
|
||||
secp256k1_scalar_t sec, non;
|
||||
int ret = 0;
|
||||
int overflow = 0;
|
||||
unsigned int count = 0;
|
||||
ARG_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(seckey != NULL);
|
||||
if (noncefp == NULL) {
|
||||
noncefp = secp256k1_nonce_function_default;
|
||||
}
|
||||
|
||||
secp256k1_scalar_set_b32(&sec, seckey, NULL);
|
||||
while (1) {
|
||||
unsigned char nonce32[32];
|
||||
ret = noncefp(nonce32, msg32, seckey, secp256k1_schnorr_algo16, count, noncedata);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
|
||||
memset(nonce32, 0, 32);
|
||||
if (!secp256k1_scalar_is_zero(&non) && !overflow) {
|
||||
if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, NULL, secp256k1_schnorr_msghash_sha256, msg32)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if (!ret) {
|
||||
memset(sig64, 0, 64);
|
||||
}
|
||||
secp256k1_scalar_clear(&non);
|
||||
secp256k1_scalar_clear(&sec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_schnorr_verify(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig64, const secp256k1_pubkey_t *pubkey) {
|
||||
secp256k1_ge_t q;
|
||||
ARG_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
|
||||
secp256k1_pubkey_load(ctx, &q, pubkey);
|
||||
return secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32);
|
||||
}
|
||||
|
||||
int secp256k1_schnorr_recover(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sig64, secp256k1_pubkey_t *pubkey) {
|
||||
secp256k1_ge_t q;
|
||||
|
||||
ARG_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
|
||||
if (secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32)) {
|
||||
secp256k1_pubkey_save(pubkey, &q);
|
||||
return 1;
|
||||
} else {
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int secp256k1_schnorr_generate_nonce_pair(const secp256k1_context_t* ctx, const unsigned char *msg32, const unsigned char *sec32, secp256k1_nonce_function_t noncefp, const void* noncedata, secp256k1_pubkey_t *pubnonce, unsigned char *privnonce32) {
|
||||
int count = 0;
|
||||
int ret = 1;
|
||||
secp256k1_gej_t Qj;
|
||||
secp256k1_ge_t Q;
|
||||
secp256k1_scalar_t sec;
|
||||
|
||||
ARG_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(sec32 != NULL);
|
||||
ARG_CHECK(pubnonce != NULL);
|
||||
ARG_CHECK(privnonce32 != NULL);
|
||||
|
||||
if (noncefp == NULL) {
|
||||
noncefp = secp256k1_nonce_function_default;
|
||||
}
|
||||
|
||||
do {
|
||||
int overflow;
|
||||
ret = noncefp(privnonce32, msg32, sec32, secp256k1_schnorr_algo16, count++, noncedata);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&sec, privnonce32, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&sec)) {
|
||||
continue;
|
||||
}
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sec);
|
||||
secp256k1_ge_set_gej(&Q, &Qj);
|
||||
|
||||
secp256k1_pubkey_save(pubnonce, &Q);
|
||||
break;
|
||||
} while(1);
|
||||
|
||||
secp256k1_scalar_clear(&sec);
|
||||
if (!ret) {
|
||||
memset(pubnonce, 0, sizeof(*pubnonce));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_schnorr_partial_sign(const secp256k1_context_t* ctx, const unsigned char *msg32, unsigned char *sig64, const unsigned char *sec32, const unsigned char *secnonce32, const secp256k1_pubkey_t *pubnonce_others) {
|
||||
int overflow = 0;
|
||||
secp256k1_scalar_t sec, non;
|
||||
secp256k1_ge_t pubnon;
|
||||
ARG_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(sec32 != NULL);
|
||||
ARG_CHECK(secnonce32 != NULL);
|
||||
ARG_CHECK(pubnonce_others != NULL);
|
||||
|
||||
secp256k1_scalar_set_b32(&sec, sec32, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&sec)) {
|
||||
return -1;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&non, secnonce32, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&non)) {
|
||||
return -1;
|
||||
}
|
||||
secp256k1_pubkey_load(ctx, &pubnon, pubnonce_others);
|
||||
return secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, &pubnon, secp256k1_schnorr_msghash_sha256, msg32);
|
||||
}
|
||||
|
||||
int secp256k1_schnorr_partial_combine(const secp256k1_context_t* ctx, unsigned char *sig64, int n, const unsigned char * const *sig64sin) {
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(n >= 1);
|
||||
ARG_CHECK(sig64sin != NULL);
|
||||
return secp256k1_schnorr_sig_combine(sig64, n, sig64sin);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
/***********************************************************************
|
||||
* Copyright (c) 2014-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, const secp256k1_ge_t *pubnonce, 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_recover(const secp256k1_ecmult_context_t* ctx, const unsigned char *sig64, secp256k1_ge_t *pubkey, secp256k1_schnorr_msghash_t hash, const unsigned char *msg32);
|
||||
static int secp256k1_schnorr_sig_combine(unsigned char *sig64, int n, const unsigned char * const *sig64ins);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,207 @@
|
|||
/***********************************************************************
|
||||
* Copyright (c) 2014-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. They support multiparty signing, public key
|
||||
* recovery and batch validation.
|
||||
*
|
||||
* Rationale for verifying R's y coordinate:
|
||||
* In order to support batch validation and public key recovery, the full R point must
|
||||
* be known to verifiers, rather than just its x coordinate. In order to not risk
|
||||
* being more strict in batch validation than normal validation, validators must be
|
||||
* required to reject signatures with incorrect y coordinate. This is only possible
|
||||
* by including a (relatively slow) field inverse, or a field square root. However,
|
||||
* batch validation offers potentially much higher benefits than this cost.
|
||||
*
|
||||
* Rationale for having an implicit y coordinate oddness:
|
||||
* If we commit to having the full R point known to verifiers, there are two mechanism.
|
||||
* Either include its oddness in the signature, or give it an implicit fixed value.
|
||||
* As the R y coordinate can be flipped by a simple negation of the nonce, we choose the
|
||||
* latter, as it comes with nearly zero impact on signing or validation performance, and
|
||||
* saves a byte in the signature.
|
||||
*
|
||||
* 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, const secp256k1_scalar_t *nonce, const secp256k1_ge_t *pubnonce, 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;
|
||||
secp256k1_scalar_t n;
|
||||
|
||||
if (secp256k1_scalar_is_zero(key) || secp256k1_scalar_is_zero(nonce)) {
|
||||
return 0;
|
||||
}
|
||||
n = *nonce;
|
||||
|
||||
secp256k1_ecmult_gen(ctx, &Rj, &n);
|
||||
if (pubnonce) {
|
||||
secp256k1_gej_add_ge(&Rj, &Rj, pubnonce);
|
||||
}
|
||||
secp256k1_ge_set_gej(&Ra, &Rj);
|
||||
secp256k1_fe_normalize(&Ra.y);
|
||||
if (secp256k1_fe_is_odd(&Ra.y)) {
|
||||
/* R's y coordinate is odd, which is not allowed (see rationale above).
|
||||
Force it to be even by negating the nonce. Note that this even works
|
||||
for multiparty signing, as the R point is known to all participants,
|
||||
which can all decide to flip the sign in unison, resulting in the
|
||||
overall R point to be negated too. */
|
||||
secp256k1_scalar_negate(&n, &n);
|
||||
}
|
||||
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)) {
|
||||
secp256k1_scalar_clear(&n);
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_mul(&s, &h, key);
|
||||
secp256k1_scalar_negate(&s, &s);
|
||||
secp256k1_scalar_add(&s, &s, &n);
|
||||
secp256k1_scalar_clear(&n);
|
||||
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;
|
||||
}
|
||||
|
||||
static int secp256k1_schnorr_sig_combine(unsigned char *sig64, int n, const unsigned char * const *sig64ins) {
|
||||
secp256k1_scalar_t s = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
int i;
|
||||
for (i = 0; i < n; i++) {
|
||||
secp256k1_scalar_t si;
|
||||
int overflow;
|
||||
secp256k1_scalar_set_b32(&si, sig64ins[i] + 32, &overflow);
|
||||
if (overflow) {
|
||||
return -1;
|
||||
}
|
||||
if (i) {
|
||||
if (memcmp(sig64ins[i - 1], sig64ins[i], 32) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
secp256k1_scalar_add(&s, &s, &si);
|
||||
}
|
||||
if (secp256k1_scalar_is_zero(&s)) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(sig64, sig64ins[0], 32);
|
||||
secp256k1_scalar_get_b32(sig64 + 32, &s);
|
||||
secp256k1_scalar_clear(&s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,173 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2014-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_MODULE_SCHNORR_TESTS
|
||||
#define SECP256K1_MODULE_SCHNORR_TESTS
|
||||
|
||||
void test_schnorr_end_to_end(void) {
|
||||
unsigned char privkey[32];
|
||||
unsigned char message[32];
|
||||
unsigned char schnorr_signature[64];
|
||||
secp256k1_pubkey_t pubkey, recpubkey;
|
||||
|
||||
/* Generate a random key and message. */
|
||||
{
|
||||
secp256k1_scalar_t key;
|
||||
random_scalar_order_test(&key);
|
||||
secp256k1_scalar_get_b32(privkey, &key);
|
||||
secp256k1_rand256_test(message);
|
||||
}
|
||||
|
||||
/* Construct and verify corresponding public key. */
|
||||
CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1);
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1);
|
||||
|
||||
/* Schnorr sign. */
|
||||
CHECK(secp256k1_schnorr_sign(ctx, message, schnorr_signature, privkey, NULL, NULL) == 1);
|
||||
CHECK(secp256k1_schnorr_verify(ctx, message, schnorr_signature, &pubkey) == 1);
|
||||
CHECK(secp256k1_schnorr_recover(ctx, message, schnorr_signature, &recpubkey) == 1);
|
||||
CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0);
|
||||
/* Destroy signature and verify again. */
|
||||
schnorr_signature[secp256k1_rand32() % 64] += 1 + (secp256k1_rand32() % 255);
|
||||
CHECK(secp256k1_schnorr_verify(ctx, message, schnorr_signature, &pubkey) == 0);
|
||||
CHECK(secp256k1_schnorr_recover(ctx, message, schnorr_signature, &recpubkey) != 1 ||
|
||||
memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0);
|
||||
}
|
||||
|
||||
/** Horribly broken hash function. Do not use for anything but tests. */
|
||||
void test_schnorr_hash(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) {
|
||||
int i;
|
||||
for (i = 0; i < 32; i++) {
|
||||
h32[i] = r32[i] ^ msg32[i];
|
||||
}
|
||||
}
|
||||
|
||||
void test_schnorr_sign_verify(void) {
|
||||
unsigned char msg32[32];
|
||||
unsigned char sig64[3][64];
|
||||
secp256k1_gej_t pubkeyj[3];
|
||||
secp256k1_ge_t pubkey[3];
|
||||
secp256k1_scalar_t nonce[3], key[3];
|
||||
int i = 0;
|
||||
int k;
|
||||
|
||||
secp256k1_rand256_test(msg32);
|
||||
|
||||
for (k = 0; k < 3; k++) {
|
||||
random_scalar_order_test(&key[k]);
|
||||
|
||||
do {
|
||||
random_scalar_order_test(&nonce[k]);
|
||||
if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64[k], &key[k], &nonce[k], NULL, &test_schnorr_hash, msg32)) {
|
||||
break;
|
||||
}
|
||||
} while(1);
|
||||
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj[k], &key[k]);
|
||||
secp256k1_ge_set_gej_var(&pubkey[k], &pubkeyj[k]);
|
||||
CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32));
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
int pos = secp256k1_rand32() % 64;
|
||||
int mod = 1 + (secp256k1_rand32() % 255);
|
||||
sig64[k][pos] ^= mod;
|
||||
CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32) == 0);
|
||||
sig64[k][pos] ^= mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_schnorr_threshold(void) {
|
||||
unsigned char msg[32];
|
||||
unsigned char sec[5][32];
|
||||
secp256k1_pubkey_t pub[5];
|
||||
unsigned char nonce[5][32];
|
||||
secp256k1_pubkey_t pubnonce[5];
|
||||
unsigned char sig[5][64];
|
||||
const unsigned char* sigs[5];
|
||||
unsigned char allsig[64];
|
||||
const secp256k1_pubkey_t* pubs[5];
|
||||
secp256k1_pubkey_t allpub;
|
||||
int n, i;
|
||||
int damage;
|
||||
int ret = 0;
|
||||
|
||||
damage = (secp256k1_rand32() % 2) ? (1 + (secp256k1_rand32() % 4)) : 0;
|
||||
secp256k1_rand256_test(msg);
|
||||
n = 2 + (secp256k1_rand32() % 4);
|
||||
for (i = 0; i < n; i++) {
|
||||
do {
|
||||
secp256k1_rand256_test(sec[i]);
|
||||
} while (!secp256k1_ec_seckey_verify(ctx, sec[i]));
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pub[i], sec[i]));
|
||||
CHECK(secp256k1_schnorr_generate_nonce_pair(ctx, msg, sec[i], NULL, NULL, &pubnonce[i], nonce[i]));
|
||||
pubs[i] = &pub[i];
|
||||
}
|
||||
if (damage == 1) {
|
||||
nonce[secp256k1_rand32() % n][secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255);
|
||||
} else if (damage == 2) {
|
||||
sec[secp256k1_rand32() % n][secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255);
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
secp256k1_pubkey_t allpubnonce;
|
||||
const secp256k1_pubkey_t *pubnonces[4];
|
||||
int j;
|
||||
for (j = 0; j < i; j++) {
|
||||
pubnonces[j] = &pubnonce[j];
|
||||
}
|
||||
for (j = i + 1; j < n; j++) {
|
||||
pubnonces[j - 1] = &pubnonce[j];
|
||||
}
|
||||
CHECK(secp256k1_ec_pubkey_combine(ctx, &allpubnonce, n - 1, pubnonces));
|
||||
ret |= (secp256k1_schnorr_partial_sign(ctx, msg, sig[i], sec[i], nonce[i], &allpubnonce) != 1) * 1;
|
||||
sigs[i] = sig[i];
|
||||
}
|
||||
if (damage == 3) {
|
||||
sig[secp256k1_rand32() % n][secp256k1_rand32() % 64] ^= 1 + (secp256k1_rand32() % 255);
|
||||
}
|
||||
ret |= (secp256k1_ec_pubkey_combine(ctx, &allpub, n, pubs) != 1) * 2;
|
||||
if ((ret & 1) == 0) {
|
||||
ret |= (secp256k1_schnorr_partial_combine(ctx, allsig, n, sigs) != 1) * 4;
|
||||
}
|
||||
if (damage == 4) {
|
||||
allsig[secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255);
|
||||
}
|
||||
if ((ret & 7) == 0) {
|
||||
ret |= (secp256k1_schnorr_verify(ctx, msg, allsig, &allpub) != 1) * 8;
|
||||
}
|
||||
CHECK((ret == 0) == (damage == 0));
|
||||
}
|
||||
|
||||
void test_schnorr_recovery(void) {
|
||||
unsigned char msg32[32];
|
||||
unsigned char sig64[64];
|
||||
secp256k1_ge_t Q;
|
||||
|
||||
secp256k1_rand256_test(msg32);
|
||||
secp256k1_rand256_test(sig64);
|
||||
secp256k1_rand256_test(sig64 + 32);
|
||||
if (secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1) {
|
||||
CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
void run_schnorr_tests(void) {
|
||||
int i;
|
||||
for (i = 0; i < 32*count; i++) {
|
||||
test_schnorr_end_to_end();
|
||||
}
|
||||
for (i = 0; i < 32 * count; i++) {
|
||||
test_schnorr_sign_verify();
|
||||
}
|
||||
for (i = 0; i < 16 * count; i++) {
|
||||
test_schnorr_recovery();
|
||||
}
|
||||
for (i = 0; i < 10 * count; i++) {
|
||||
test_schnorr_threshold();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -267,20 +267,29 @@ int secp256k1_ecdsa_verify(const secp256k1_context_t* ctx, const unsigned char *
|
|||
secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m));
|
||||
}
|
||||
|
||||
static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) {
|
||||
unsigned char keydata[96];
|
||||
static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, unsigned int counter, const void *data) {
|
||||
unsigned char keydata[112];
|
||||
int keylen = 64;
|
||||
secp256k1_rfc6979_hmac_sha256_t rng;
|
||||
unsigned int i;
|
||||
/* We feed a byte array to the PRNG as input, consisting of:
|
||||
* - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d.
|
||||
* - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data.
|
||||
* - optionally 16 extra bytes with the algorithm name (the extra data bytes
|
||||
* are set to zeroes when not present, while the algorithm name is).
|
||||
*/
|
||||
memcpy(keydata, key32, 32);
|
||||
memcpy(keydata + 32, msg32, 32);
|
||||
if (data != NULL) {
|
||||
memcpy(keydata + 64, data, 32);
|
||||
keylen = 96;
|
||||
}
|
||||
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, data != NULL ? 96 : 64);
|
||||
if (algo16 != NULL) {
|
||||
memset(keydata + keylen, 0, 96 - keylen);
|
||||
memcpy(keydata + 96, algo16, 16);
|
||||
keylen = 112;
|
||||
}
|
||||
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen);
|
||||
memset(keydata, 0, sizeof(keydata));
|
||||
for (i = 0; i <= counter; i++) {
|
||||
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
|
||||
|
@ -314,7 +323,7 @@ int secp256k1_ecdsa_sign(const secp256k1_context_t* ctx, const unsigned char *ms
|
|||
secp256k1_scalar_set_b32(&msg, msg32, NULL);
|
||||
while (1) {
|
||||
unsigned char nonce32[32];
|
||||
ret = noncefp(nonce32, msg32, seckey, count, noncedata);
|
||||
ret = noncefp(nonce32, msg32, seckey, NULL, count, noncedata);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
|
@ -527,6 +536,34 @@ int secp256k1_context_randomize(secp256k1_context_t* ctx, const unsigned char *s
|
|||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_ec_pubkey_combine(const secp256k1_context_t* ctx, secp256k1_pubkey_t *pubnonce, int n, const secp256k1_pubkey_t * const *pubnonces) {
|
||||
int i;
|
||||
secp256k1_gej_t Qj;
|
||||
secp256k1_ge_t Q;
|
||||
|
||||
ARG_CHECK(pubnonce != NULL);
|
||||
ARG_CHECK(n >= 1);
|
||||
ARG_CHECK(pubnonces != NULL);
|
||||
|
||||
secp256k1_gej_set_infinity(&Qj);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
secp256k1_pubkey_load(ctx, &Q, pubnonces[i]);
|
||||
secp256k1_gej_add_ge(&Qj, &Qj, &Q);
|
||||
}
|
||||
if (secp256k1_gej_is_infinity(&Qj)) {
|
||||
memset(pubnonce, 0, sizeof(*pubnonce));
|
||||
return 0;
|
||||
}
|
||||
secp256k1_ge_set_gej(&Q, &Qj);
|
||||
secp256k1_pubkey_save(pubnonce, &Q);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MODULE_ECDH
|
||||
# include "modules/ecdh/main_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_SCHNORR
|
||||
# include "modules/schnorr/main_impl.h"
|
||||
#endif
|
||||
|
|
53
src/tests.c
53
src/tests.c
|
@ -1271,6 +1271,38 @@ void run_ge(void) {
|
|||
test_add_neg_y_diff_x();
|
||||
}
|
||||
|
||||
void test_ec_combine(void) {
|
||||
secp256k1_scalar_t sum = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
secp256k1_pubkey_t data[6];
|
||||
const secp256k1_pubkey_t* d[6];
|
||||
secp256k1_pubkey_t sd;
|
||||
secp256k1_pubkey_t sd2;
|
||||
secp256k1_gej_t Qj;
|
||||
secp256k1_ge_t Q;
|
||||
int i;
|
||||
for (i = 1; i <= 6; i++) {
|
||||
secp256k1_scalar_t s;
|
||||
random_scalar_order_test(&s);
|
||||
secp256k1_scalar_add(&sum, &sum, &s);
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &s);
|
||||
secp256k1_ge_set_gej(&Q, &Qj);
|
||||
secp256k1_pubkey_save(&data[i - 1], &Q);
|
||||
d[i - 1] = &data[i - 1];
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sum);
|
||||
secp256k1_ge_set_gej(&Q, &Qj);
|
||||
secp256k1_pubkey_save(&sd, &Q);
|
||||
CHECK(secp256k1_ec_pubkey_combine(ctx, &sd2, i, d) == 1);
|
||||
CHECK(memcmp(&sd, &sd2, sizeof(sd)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void run_ec_combine(void) {
|
||||
int i;
|
||||
for (i = 0; i < count * 8; i++) {
|
||||
test_ec_combine();
|
||||
}
|
||||
}
|
||||
|
||||
/***** ECMULT TESTS *****/
|
||||
|
||||
void run_ecmult_chain(void) {
|
||||
|
@ -1755,22 +1787,23 @@ void run_ecdsa_sign_verify(void) {
|
|||
}
|
||||
|
||||
/** Dummy nonce generation function that just uses a precomputed nonce, and fails if it is not accepted. Use only for testing. */
|
||||
static int precomputed_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) {
|
||||
static int precomputed_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, unsigned int counter, const void *data) {
|
||||
(void)msg32;
|
||||
(void)key32;
|
||||
(void)algo16;
|
||||
memcpy(nonce32, data, 32);
|
||||
return (counter == 0);
|
||||
}
|
||||
|
||||
static int nonce_function_test_fail(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) {
|
||||
static int nonce_function_test_fail(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, unsigned int counter, const void *data) {
|
||||
/* Dummy nonce generator that has a fatal error on the first counter value. */
|
||||
if (counter == 0) {
|
||||
return 0;
|
||||
}
|
||||
return nonce_function_rfc6979(nonce32, msg32, key32, counter - 1, data);
|
||||
return nonce_function_rfc6979(nonce32, msg32, key32, algo16, counter - 1, data);
|
||||
}
|
||||
|
||||
static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, unsigned int counter, const void *data) {
|
||||
static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, unsigned int counter, const void *data) {
|
||||
/* Dummy nonce generator that produces unacceptable nonces for the first several counter values. */
|
||||
if (counter < 3) {
|
||||
memset(nonce32, counter==0 ? 0 : 255, 32);
|
||||
|
@ -1797,7 +1830,7 @@ static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char
|
|||
if (counter > 5) {
|
||||
return 0;
|
||||
}
|
||||
return nonce_function_rfc6979(nonce32, msg32, key32, counter - 5, data);
|
||||
return nonce_function_rfc6979(nonce32, msg32, key32, algo16, counter - 5, data);
|
||||
}
|
||||
|
||||
int is_empty_signature(const secp256k1_ecdsa_signature_t *sig) {
|
||||
|
@ -2378,6 +2411,10 @@ void run_ecdsa_openssl(void) {
|
|||
# include "modules/ecdh/tests_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_SCHNORR
|
||||
# include "modules/schnorr/tests_impl.h"
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned char seed16[16] = {0};
|
||||
unsigned char run32[32] = {0};
|
||||
|
@ -2460,6 +2497,7 @@ int main(int argc, char **argv) {
|
|||
run_ecmult_constants();
|
||||
run_ecmult_gen_blind();
|
||||
run_ecmult_const_tests();
|
||||
run_ec_combine();
|
||||
|
||||
/* endomorphism tests */
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
|
@ -2480,6 +2518,11 @@ int main(int argc, char **argv) {
|
|||
run_ecdsa_openssl();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_SCHNORR
|
||||
/* Schnorr tests */
|
||||
run_schnorr_tests();
|
||||
#endif
|
||||
|
||||
secp256k1_rand256(run32);
|
||||
printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]);
|
||||
|
||||
|
|
Loading…
Reference in New Issue