Merge #111: Add ECDSA sign-to-contract module
47efb5e39aecdsa-s2c: add ctime tests (Andrew Poelstra)396b558273ecdsa-s2c: add anti-klepto protocol (Andrew Poelstra)290dee566eecdsa-s2c: add actual sign-to-contract functionality (Andrew Poelstra)8e46cac5b3ecdsa-s2c: block in module (Andrew Poelstra)826bd04b43add eccommit functionality (Andrew Poelstra) Pull request description: This is a backport and rebase of https://github.com/bitcoin-core/secp256k1/pull/669 ACKs for top commit: jonasnick: ACK47efb5e39areal-or-random: ACK47efb5e39aTree-SHA512: e1f3ee3985bc77197eb57c03884b5d4a5f8733523bba50e11309f86388471c6265b7241e9856e1b80a88f4c268f2826c0394e26161292aa438b2246a1ad86aa1
This commit is contained in:
28
src/eccommit.h
Normal file
28
src/eccommit.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/**********************************************************************
|
||||
* Copyright (c) 2020 The libsecp256k1-zkp Developers *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_ECCOMMIT_H
|
||||
#define SECP256K1_ECCOMMIT_H
|
||||
|
||||
/** Helper function to add a 32-byte value to a scalar */
|
||||
static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak);
|
||||
/** Helper function to add a 32-byte value, times G, to an EC point */
|
||||
static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *p, const unsigned char *tweak);
|
||||
|
||||
/** Serializes elem as a 33 byte array. This is non-constant time with respect to
|
||||
* whether pubp is the point at infinity. Thus, you may need to declassify
|
||||
* pubp->infinity before calling this function. */
|
||||
static int secp256k1_ec_commit_pubkey_serialize_const(secp256k1_ge *pubp, unsigned char *buf33);
|
||||
/** Compute an ec commitment tweak as hash(pubkey, data). */
|
||||
static int secp256k1_ec_commit_tweak(unsigned char *tweak32, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size);
|
||||
/** Compute an ec commitment as pubkey + hash(pubkey, data)*G. */
|
||||
static int secp256k1_ec_commit(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size);
|
||||
/** Compute a secret key commitment as seckey + hash(pubkey, data). */
|
||||
static int secp256k1_ec_commit_seckey(const secp256k1_ecmult_gen_context* ecmult_gen_ctx, secp256k1_scalar* seckey, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size);
|
||||
/** Verify an ec commitment as pubkey + hash(pubkey, data)*G ?= commitment. */
|
||||
static int secp256k1_ec_commit_verify(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size);
|
||||
|
||||
#endif /* SECP256K1_ECCOMMIT_H */
|
||||
73
src/eccommit_impl.h
Normal file
73
src/eccommit_impl.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/**********************************************************************
|
||||
* Copyright (c) 2020 The libsecp256k1 Developers *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "eckey.h"
|
||||
#include "hash.h"
|
||||
|
||||
/* from secp256k1.c */
|
||||
static int secp256k1_ec_seckey_tweak_add_helper(secp256k1_scalar *sec, const unsigned char *tweak);
|
||||
static int secp256k1_ec_pubkey_tweak_add_helper(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge *pubp, const unsigned char *tweak);
|
||||
|
||||
static int secp256k1_ec_commit_pubkey_serialize_const(secp256k1_ge *pubp, unsigned char *buf33) {
|
||||
if (secp256k1_ge_is_infinity(pubp)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_fe_normalize(&pubp->x);
|
||||
secp256k1_fe_normalize(&pubp->y);
|
||||
secp256k1_fe_get_b32(&buf33[1], &pubp->x);
|
||||
buf33[0] = secp256k1_fe_is_odd(&pubp->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Compute an ec commitment tweak as hash(pubp, data). */
|
||||
static int secp256k1_ec_commit_tweak(unsigned char *tweak32, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size)
|
||||
{
|
||||
unsigned char rbuf[33];
|
||||
|
||||
if (!secp256k1_ec_commit_pubkey_serialize_const(pubp, rbuf)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_sha256_write(sha, rbuf, sizeof(rbuf));
|
||||
secp256k1_sha256_write(sha, data, data_size);
|
||||
secp256k1_sha256_finalize(sha, tweak32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Compute an ec commitment as pubp + hash(pubp, data)*G. */
|
||||
static int secp256k1_ec_commit(const secp256k1_ecmult_context* ecmult_ctx, secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) {
|
||||
unsigned char tweak[32];
|
||||
|
||||
*commitp = *pubp;
|
||||
return secp256k1_ec_commit_tweak(tweak, commitp, sha, data, data_size)
|
||||
&& secp256k1_ec_pubkey_tweak_add_helper(ecmult_ctx, commitp, tweak);
|
||||
}
|
||||
|
||||
/* Compute the seckey of an ec commitment from the original secret key of the pubkey as seckey +
|
||||
* hash(pubp, data). */
|
||||
static int secp256k1_ec_commit_seckey(secp256k1_scalar* seckey, secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) {
|
||||
unsigned char tweak[32];
|
||||
return secp256k1_ec_commit_tweak(tweak, pubp, sha, data, data_size)
|
||||
&& secp256k1_ec_seckey_tweak_add_helper(seckey, tweak);
|
||||
}
|
||||
|
||||
/* Verify an ec commitment as pubp + hash(pubp, data)*G ?= commitment. */
|
||||
static int secp256k1_ec_commit_verify(const secp256k1_ecmult_context* ecmult_ctx, const secp256k1_ge* commitp, const secp256k1_ge* pubp, secp256k1_sha256* sha, const unsigned char *data, size_t data_size) {
|
||||
secp256k1_gej pj;
|
||||
secp256k1_ge p;
|
||||
|
||||
if (!secp256k1_ec_commit(ecmult_ctx, &p, pubp, sha, data, data_size)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return p == commitp */
|
||||
secp256k1_ge_neg(&p, &p);
|
||||
secp256k1_gej_set_ge(&pj, &p);
|
||||
secp256k1_gej_add_ge_var(&pj, &pj, commitp, NULL);
|
||||
return secp256k1_gej_is_infinity(&pj);
|
||||
}
|
||||
|
||||
3
src/modules/ecdsa_s2c/Makefile.am.include
Normal file
3
src/modules/ecdsa_s2c/Makefile.am.include
Normal file
@@ -0,0 +1,3 @@
|
||||
include_HEADERS += include/secp256k1_ecdsa_s2c.h
|
||||
noinst_HEADERS += src/modules/ecdsa_s2c/main_impl.h
|
||||
noinst_HEADERS += src/modules/ecdsa_s2c/tests_impl.h
|
||||
198
src/modules/ecdsa_s2c/main_impl.h
Executable file
198
src/modules/ecdsa_s2c/main_impl.h
Executable file
@@ -0,0 +1,198 @@
|
||||
/**********************************************************************
|
||||
* Copyright (c) 2019-2020 Marko Bencun, Jonas Nick *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_ECDSA_S2C_MAIN_H
|
||||
#define SECP256K1_MODULE_ECDSA_S2C_MAIN_H
|
||||
|
||||
#include "include/secp256k1.h"
|
||||
#include "include/secp256k1_ecdsa_s2c.h"
|
||||
|
||||
static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge) {
|
||||
secp256k1_pubkey_save((secp256k1_pubkey*) opening, ge);
|
||||
}
|
||||
|
||||
static int secp256k1_ecdsa_s2c_opening_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_ecdsa_s2c_opening* opening) {
|
||||
return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey*) opening);
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_s2c_opening_parse(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* input33) {
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(opening != NULL);
|
||||
ARG_CHECK(input33 != NULL);
|
||||
return secp256k1_ec_pubkey_parse(ctx, (secp256k1_pubkey*) opening, input33, 33);
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_s2c_opening_serialize(const secp256k1_context* ctx, unsigned char* output33, const secp256k1_ecdsa_s2c_opening* opening) {
|
||||
size_t out_len = 33;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(output33 != NULL);
|
||||
ARG_CHECK(opening != NULL);
|
||||
return secp256k1_ec_pubkey_serialize(ctx, output33, &out_len, (const secp256k1_pubkey*) opening, SECP256K1_EC_COMPRESSED);
|
||||
}
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("s2c/ecdsa/point")||SHA256("s2c/ecdsa/point"). */
|
||||
static void secp256k1_s2c_ecdsa_point_sha256_tagged(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
sha->s[0] = 0xa9b21c7bul;
|
||||
sha->s[1] = 0x358c3e3eul;
|
||||
sha->s[2] = 0x0b6863d1ul;
|
||||
sha->s[3] = 0xc62b2035ul;
|
||||
sha->s[4] = 0xb44b40ceul;
|
||||
sha->s[5] = 0x254a8912ul;
|
||||
sha->s[6] = 0x0f85d0d4ul;
|
||||
sha->s[7] = 0x8a5bf91cul;
|
||||
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("s2c/ecdsa/data")||SHA256("s2c/ecdsa/data"). */
|
||||
static void secp256k1_s2c_ecdsa_data_sha256_tagged(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
sha->s[0] = 0xfeefd675ul;
|
||||
sha->s[1] = 0x73166c99ul;
|
||||
sha->s[2] = 0xe2309cb8ul;
|
||||
sha->s[3] = 0x6d458113ul;
|
||||
sha->s[4] = 0x01d3a512ul;
|
||||
sha->s[5] = 0x00e18112ul;
|
||||
sha->s[6] = 0x37ee0874ul;
|
||||
sha->s[7] = 0x421fc55ful;
|
||||
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_s2c_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* signature, secp256k1_ecdsa_s2c_opening* s2c_opening, const unsigned char
|
||||
*msg32, const unsigned char *seckey, const unsigned char* s2c_data32) {
|
||||
secp256k1_scalar r, s;
|
||||
int ret;
|
||||
unsigned char ndata[32];
|
||||
secp256k1_sha256 s2c_sha;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(signature != NULL);
|
||||
ARG_CHECK(seckey != NULL);
|
||||
ARG_CHECK(s2c_data32 != NULL);
|
||||
|
||||
/* Provide `s2c_data32` to the nonce function as additional data to
|
||||
* derive the nonce. It is first hashed because it should be possible
|
||||
* to derive nonces even if only a SHA256 commitment to the data is
|
||||
* known. This is important in the ECDSA anti-klepto protocol. */
|
||||
secp256k1_s2c_ecdsa_data_sha256_tagged(&s2c_sha);
|
||||
secp256k1_sha256_write(&s2c_sha, s2c_data32, 32);
|
||||
secp256k1_sha256_finalize(&s2c_sha, ndata);
|
||||
|
||||
secp256k1_s2c_ecdsa_point_sha256_tagged(&s2c_sha);
|
||||
ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, &s2c_sha, s2c_opening, s2c_data32, msg32, seckey, NULL, ndata);
|
||||
secp256k1_scalar_cmov(&r, &secp256k1_scalar_zero, !ret);
|
||||
secp256k1_scalar_cmov(&s, &secp256k1_scalar_zero, !ret);
|
||||
secp256k1_ecdsa_signature_save(signature, &r, &s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_s2c_verify_commit(const secp256k1_context* ctx, const secp256k1_ecdsa_signature* sig, const unsigned char* data32, const secp256k1_ecdsa_s2c_opening* opening) {
|
||||
secp256k1_ge commitment_ge;
|
||||
secp256k1_ge original_pubnonce_ge;
|
||||
unsigned char x_bytes[32];
|
||||
secp256k1_scalar sigr, sigs, x_scalar;
|
||||
secp256k1_sha256 s2c_sha;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(sig != NULL);
|
||||
ARG_CHECK(data32 != NULL);
|
||||
ARG_CHECK(opening != NULL);
|
||||
|
||||
if (!secp256k1_ecdsa_s2c_opening_load(ctx, &original_pubnonce_ge, opening)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_s2c_ecdsa_point_sha256_tagged(&s2c_sha);
|
||||
if (!secp256k1_ec_commit(&ctx->ecmult_ctx, &commitment_ge, &original_pubnonce_ge, &s2c_sha, data32, 32)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check that sig_r == commitment_x (mod n)
|
||||
* sig_r is the x coordinate of R represented by a scalar.
|
||||
* commitment_x is the x coordinate of the commitment (field element).
|
||||
*
|
||||
* Note that we are only checking the x-coordinate -- this is because the y-coordinate
|
||||
* is not part of the ECDSA signature (and therefore not part of the commitment!)
|
||||
*/
|
||||
secp256k1_ecdsa_signature_load(ctx, &sigr, &sigs, sig);
|
||||
|
||||
secp256k1_fe_normalize(&commitment_ge.x);
|
||||
secp256k1_fe_get_b32(x_bytes, &commitment_ge.x);
|
||||
/* Do not check overflow; overflowing a scalar does not affect whether
|
||||
* or not the R value is a cryptographic commitment, only whether it
|
||||
* is a valid R value for an ECDSA signature. If users care about that
|
||||
* they should use `ecdsa_verify` or `anti_klepto_host_verify`. In other
|
||||
* words, this check would be (at best) unnecessary, and (at worst)
|
||||
* insufficient. */
|
||||
secp256k1_scalar_set_b32(&x_scalar, x_bytes, NULL);
|
||||
return secp256k1_scalar_eq(&sigr, &x_scalar);
|
||||
}
|
||||
|
||||
/*** anti-klepto ***/
|
||||
int secp256k1_ecdsa_anti_klepto_host_commit(const secp256k1_context* ctx, unsigned char* rand_commitment32, const unsigned char* rand32) {
|
||||
secp256k1_sha256 sha;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(rand_commitment32 != NULL);
|
||||
ARG_CHECK(rand32 != NULL);
|
||||
|
||||
secp256k1_s2c_ecdsa_data_sha256_tagged(&sha);
|
||||
secp256k1_sha256_write(&sha, rand32, 32);
|
||||
secp256k1_sha256_finalize(&sha, rand_commitment32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_anti_klepto_signer_commit(const secp256k1_context* ctx, secp256k1_ecdsa_s2c_opening* opening, const unsigned char* msg32, const unsigned char* seckey32, const unsigned char* rand_commitment32) {
|
||||
unsigned char nonce32[32];
|
||||
secp256k1_scalar k;
|
||||
secp256k1_gej rj;
|
||||
secp256k1_ge r;
|
||||
unsigned int count = 0;
|
||||
int is_nonce_valid = 0;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(opening != NULL);
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(seckey32 != NULL);
|
||||
ARG_CHECK(rand_commitment32 != NULL);
|
||||
|
||||
memset(nonce32, 0, 32);
|
||||
while (!is_nonce_valid) {
|
||||
/* cast to void* removes const qualifier, but secp256k1_nonce_function_default does not modify it */
|
||||
if (!secp256k1_nonce_function_default(nonce32, msg32, seckey32, NULL, (void*)rand_commitment32, count)) {
|
||||
secp256k1_callback_call(&ctx->error_callback, "(cryptographically unreachable) generated bad nonce");
|
||||
}
|
||||
is_nonce_valid = secp256k1_scalar_set_b32_seckey(&k, nonce32);
|
||||
/* The nonce is still secret here, but it being invalid is is less likely than 1:2^255. */
|
||||
secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid));
|
||||
count++;
|
||||
}
|
||||
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k);
|
||||
secp256k1_ge_set_gej(&r, &rj);
|
||||
secp256k1_ecdsa_s2c_opening_save(opening, &r);
|
||||
memset(nonce32, 0, 32);
|
||||
secp256k1_scalar_clear(&k);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_anti_klepto_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char* msg32, const unsigned char* seckey, const unsigned char* host_data32) {
|
||||
return secp256k1_ecdsa_s2c_sign(ctx, sig, NULL, msg32, seckey, host_data32);
|
||||
}
|
||||
|
||||
int secp256k1_anti_klepto_host_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey, const unsigned char *host_data32, const secp256k1_ecdsa_s2c_opening *opening) {
|
||||
return secp256k1_ecdsa_s2c_verify_commit(ctx, sig, host_data32, opening) &&
|
||||
secp256k1_ecdsa_verify(ctx, sig, msg32, pubkey);
|
||||
}
|
||||
|
||||
#endif /* SECP256K1_ECDSA_S2C_MAIN_H */
|
||||
416
src/modules/ecdsa_s2c/tests_impl.h
Normal file
416
src/modules/ecdsa_s2c/tests_impl.h
Normal file
@@ -0,0 +1,416 @@
|
||||
/**********************************************************************
|
||||
* Copyright (c) 2019-2020 Marko Bencun, Jonas Nick *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_ECDSA_S2C_TESTS_H
|
||||
#define SECP256K1_MODULE_ECDSA_S2C_TESTS_H
|
||||
|
||||
#include "include/secp256k1_ecdsa_s2c.h"
|
||||
|
||||
static void test_ecdsa_s2c_tagged_hash(void) {
|
||||
unsigned char tag_data[14] = "s2c/ecdsa/data";
|
||||
unsigned char tag_point[15] = "s2c/ecdsa/point";
|
||||
secp256k1_sha256 sha;
|
||||
secp256k1_sha256 sha_optimized;
|
||||
unsigned char output[32];
|
||||
unsigned char output_optimized[32];
|
||||
|
||||
secp256k1_sha256_initialize_tagged(&sha, tag_data, sizeof(tag_data));
|
||||
secp256k1_s2c_ecdsa_data_sha256_tagged(&sha_optimized);
|
||||
secp256k1_sha256_finalize(&sha, output);
|
||||
secp256k1_sha256_finalize(&sha_optimized, output_optimized);
|
||||
CHECK(secp256k1_memcmp_var(output, output_optimized, 32) == 0);
|
||||
|
||||
secp256k1_sha256_initialize_tagged(&sha, tag_point, sizeof(tag_point));
|
||||
secp256k1_s2c_ecdsa_point_sha256_tagged(&sha_optimized);
|
||||
secp256k1_sha256_finalize(&sha, output);
|
||||
secp256k1_sha256_finalize(&sha_optimized, output_optimized);
|
||||
CHECK(secp256k1_memcmp_var(output, output_optimized, 32) == 0);
|
||||
}
|
||||
|
||||
void run_s2c_opening_test(void) {
|
||||
int i = 0;
|
||||
unsigned char output[33];
|
||||
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
|
||||
unsigned char input[33] = {
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02
|
||||
};
|
||||
secp256k1_ecdsa_s2c_opening opening;
|
||||
int32_t ecount = 0;
|
||||
|
||||
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
|
||||
|
||||
/* First parsing, then serializing works */
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1);
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, output, &opening) == 1);
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1);
|
||||
CHECK(ecount == 0);
|
||||
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_parse(none, NULL, input) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, NULL) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1);
|
||||
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, NULL, &opening) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, output, NULL) == 0);
|
||||
|
||||
CHECK(ecount == 4);
|
||||
/* Invalid pubkey makes parsing fail */
|
||||
input[0] = 0; /* bad oddness bit */
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 0);
|
||||
input[0] = 2;
|
||||
input[31] = 1; /* point not on the curve */
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 0);
|
||||
CHECK(ecount == 4); /* neither of the above are API errors */
|
||||
|
||||
/* Try parsing and serializing a bunch of openings */
|
||||
for (i = 0; i < count; i++) {
|
||||
/* This is expected to fail in about 50% of iterations because the
|
||||
* points' x-coordinates are uniformly random */
|
||||
if (secp256k1_ecdsa_s2c_opening_parse(none, &opening, input) == 1) {
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_serialize(none, output, &opening) == 1);
|
||||
CHECK(memcmp(output, input, sizeof(output)) == 0);
|
||||
}
|
||||
secp256k1_testrand256(&input[1]);
|
||||
/* Set pubkey oddness tag to first bit of input[1] */
|
||||
input[0] = (input[1] & 1) + 2;
|
||||
}
|
||||
|
||||
secp256k1_context_destroy(none);
|
||||
}
|
||||
|
||||
static void test_ecdsa_s2c_api(void) {
|
||||
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||||
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
|
||||
secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
|
||||
secp256k1_ecdsa_s2c_opening s2c_opening;
|
||||
secp256k1_ecdsa_signature sig;
|
||||
const unsigned char msg[32] = "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm";
|
||||
const unsigned char sec[32] = "ssssssssssssssssssssssssssssssss";
|
||||
const unsigned char s2c_data[32] = "dddddddddddddddddddddddddddddddd";
|
||||
const unsigned char hostrand[32] = "hrhrhrhrhrhrhrhrhrhrhrhrhrhrhrhr";
|
||||
unsigned char hostrand_commitment[32];
|
||||
secp256k1_pubkey pk;
|
||||
|
||||
int32_t ecount;
|
||||
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount);
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sec));
|
||||
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(both, NULL, &s2c_opening, msg, sec, s2c_data) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(both, &sig, NULL, msg, sec, s2c_data) == 1);
|
||||
CHECK(ecount == 1); /* NULL opening is not an API error */
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(both, &sig, &s2c_opening, NULL, sec, s2c_data) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(both, &sig, &s2c_opening, msg, NULL, s2c_data) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(both, &sig, &s2c_opening, msg, sec, NULL) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(none, &sig, &s2c_opening, msg, sec, s2c_data) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(vrfy, &sig, &s2c_opening, msg, sec, s2c_data) == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(sign, &sig, &s2c_opening, msg, sec, s2c_data) == 1);
|
||||
CHECK(ecount == 6);
|
||||
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pk) == 1);
|
||||
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(both, NULL, s2c_data, &s2c_opening) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(both, &sig, NULL, &s2c_opening) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(both, &sig, s2c_data, NULL) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(none, &sig, s2c_data, &s2c_opening) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(sign, &sig, s2c_data, &s2c_opening) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(vrfy, &sig, s2c_data, &s2c_opening) == 1);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(vrfy, &sig, sec, &s2c_opening) == 0);
|
||||
CHECK(ecount == 5); /* wrong data is not an API error */
|
||||
|
||||
/* Signing with NULL s2c_opening gives the same result */
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(sign, &sig, NULL, msg, sec, s2c_data) == 1);
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(vrfy, &sig, s2c_data, &s2c_opening) == 1);
|
||||
|
||||
/* anti-klepto */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, NULL, hostrand) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, hostrand_commitment, NULL) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_host_commit(none, hostrand_commitment, hostrand) == 1);
|
||||
CHECK(ecount == 2);
|
||||
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, NULL, msg, sec, hostrand_commitment) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, NULL, sec, hostrand_commitment) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, msg, NULL, hostrand_commitment) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(both, &s2c_opening, msg, sec, NULL) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(none, &s2c_opening, msg, sec, hostrand_commitment) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(vrfy, &s2c_opening, msg, sec, hostrand_commitment) == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(sign, &s2c_opening, msg, sec, hostrand_commitment) == 1);
|
||||
CHECK(ecount == 6);
|
||||
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_anti_klepto_sign(both, NULL, msg, sec, hostrand) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_anti_klepto_sign(both, &sig, NULL, sec, hostrand) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, NULL, hostrand) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, sec, NULL) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_anti_klepto_sign(none, &sig, msg, sec, hostrand) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_anti_klepto_sign(vrfy, &sig, msg, sec, hostrand) == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_anti_klepto_sign(both, &sig, msg, sec, hostrand) == 1);
|
||||
CHECK(ecount == 6);
|
||||
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_anti_klepto_host_verify(both, NULL, msg, &pk, hostrand, &s2c_opening) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(both, &sig, NULL, &pk, hostrand, &s2c_opening) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, NULL, hostrand, &s2c_opening) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, &pk, NULL, &s2c_opening) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(both, &sig, msg, &pk, hostrand, NULL) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(none, &sig, msg, &pk, hostrand, &s2c_opening) == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(sign, &sig, msg, &pk, hostrand, &s2c_opening) == 0);
|
||||
CHECK(ecount == 7);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(vrfy, &sig, msg, &pk, hostrand, &s2c_opening) == 1);
|
||||
CHECK(ecount == 7);
|
||||
|
||||
secp256k1_context_destroy(both);
|
||||
secp256k1_context_destroy(vrfy);
|
||||
secp256k1_context_destroy(sign);
|
||||
secp256k1_context_destroy(none);
|
||||
}
|
||||
|
||||
/* When using sign-to-contract commitments, the nonce function is fixed, so we can use fixtures to test. */
|
||||
typedef struct {
|
||||
/* Data to commit to */
|
||||
unsigned char s2c_data[32];
|
||||
/* Original nonce */
|
||||
unsigned char expected_s2c_opening[33];
|
||||
/* Original nonce (anti-klepto protocol, which mixes in host randomness) */
|
||||
unsigned char expected_s2c_klepto_opening[33];
|
||||
} ecdsa_s2c_test;
|
||||
|
||||
static ecdsa_s2c_test ecdsa_s2c_tests[] = {
|
||||
{
|
||||
"\x1b\xf6\xfb\x42\xf4\x1e\xb8\x76\xc4\xd7\xaa\x0d\x67\x24\x2b\x00\xba\xab\x99\xdc\x20\x84\x49\x3e\x4e\x63\x27\x7f\xa1\xf7\x7f\x22",
|
||||
"\x03\xf0\x30\xde\xf3\x18\x8c\x0f\x56\xfc\xea\x87\x43\x5b\x30\x76\x43\xf4\x5d\xaf\xe2\x2c\xbc\x82\xfd\x56\x03\x4f\xae\x97\x41\x7d\x3a",
|
||||
"\x02\xdf\x63\x75\x5d\x1f\x32\x92\xbf\xfe\xd8\x29\x86\xb1\x06\x49\x7c\x93\xb1\xf8\xbd\xc0\x45\x4b\x6b\x0b\x0a\x47\x79\xc0\xef\x71\x88",
|
||||
},
|
||||
{
|
||||
"\x35\x19\x9a\x8f\xbf\x84\xad\x6e\xf6\x9a\x18\x4c\x1b\x19\x28\x5b\xef\xbe\x06\xe6\x0b\x62\x64\xe6\xd3\x73\x89\x3f\x68\x55\xe2\x4a",
|
||||
"\x03\x90\x17\x17\xce\x7c\x74\x84\xa2\xce\x1b\x7d\xc7\x40\x3b\x14\xe0\x35\x49\x71\x39\x3e\xc0\x92\xa7\xf3\xe0\xc8\xe4\xe2\xd2\x63\x9d",
|
||||
"\x02\xc0\x4a\xc7\xf7\x71\xe8\xeb\xdb\xf3\x15\xff\x5e\x58\xb7\xfe\x95\x16\x10\x21\x03\x50\x00\x66\x17\x2c\x4f\xac\x5b\x20\xf9\xe0\xea",
|
||||
},
|
||||
};
|
||||
|
||||
static void test_ecdsa_s2c_fixed_vectors(void) {
|
||||
const unsigned char privkey[32] = {
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
};
|
||||
const unsigned char message[32] = {
|
||||
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
|
||||
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(ecdsa_s2c_tests) / sizeof(ecdsa_s2c_tests[0]); i++) {
|
||||
secp256k1_ecdsa_s2c_opening s2c_opening;
|
||||
unsigned char opening_ser[33];
|
||||
const ecdsa_s2c_test *test = &ecdsa_s2c_tests[i];
|
||||
secp256k1_ecdsa_signature signature;
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, message, privkey, test->s2c_data) == 1);
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_serialize(ctx, opening_ser, &s2c_opening) == 1);
|
||||
CHECK(memcmp(test->expected_s2c_opening, opening_ser, sizeof(opening_ser)) == 0);
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, test->s2c_data, &s2c_opening) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_ecdsa_s2c_sign_verify(void) {
|
||||
unsigned char privkey[32];
|
||||
secp256k1_pubkey pubkey;
|
||||
unsigned char message[32];
|
||||
unsigned char noncedata[32];
|
||||
unsigned char s2c_data[32];
|
||||
unsigned char s2c_data2[32];
|
||||
secp256k1_ecdsa_signature signature;
|
||||
secp256k1_ecdsa_s2c_opening s2c_opening;
|
||||
|
||||
/* Generate a random key, message, noncedata and s2c_data. */
|
||||
{
|
||||
secp256k1_scalar key;
|
||||
random_scalar_order_test(&key);
|
||||
secp256k1_scalar_get_b32(privkey, &key);
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1);
|
||||
|
||||
secp256k1_testrand256_test(message);
|
||||
secp256k1_testrand256_test(noncedata);
|
||||
secp256k1_testrand256_test(s2c_data);
|
||||
secp256k1_testrand256_test(s2c_data2);
|
||||
}
|
||||
|
||||
{ /* invalid privkeys */
|
||||
unsigned char zero_privkey[32] = {0};
|
||||
unsigned char overflow_privkey[32] = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, NULL, message, zero_privkey, s2c_data) == 0);
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, NULL, message, overflow_privkey, s2c_data) == 0);
|
||||
}
|
||||
/* Check that the sign-to-contract signature is valid, with s2c_data. Also check the commitment. */
|
||||
{
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, message, privkey, s2c_data) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &signature, message, &pubkey) == 1);
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, s2c_data, &s2c_opening) == 1);
|
||||
}
|
||||
/* Check that an invalid commitment does not verify */
|
||||
{
|
||||
unsigned char sigbytes[64];
|
||||
size_t i;
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, message, privkey, s2c_data) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &signature, message, &pubkey) == 1);
|
||||
|
||||
CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, sigbytes, &signature) == 1);
|
||||
for(i = 0; i < 32; i++) {
|
||||
/* change one byte */
|
||||
sigbytes[i] = (((int)sigbytes[i]) + 1) % 256;
|
||||
CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &signature, sigbytes) == 1);
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, s2c_data, &s2c_opening) == 0);
|
||||
/* revert */
|
||||
sigbytes[i] = (((int)sigbytes[i]) + 255) % 256;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_ecdsa_anti_klepto_signer_commit(void) {
|
||||
size_t i;
|
||||
unsigned char privkey[32] = {
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
};
|
||||
unsigned char message[32] = {
|
||||
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
|
||||
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
|
||||
};
|
||||
/* Check that original pubnonce is derived from s2c_data */
|
||||
for (i = 0; i < sizeof(ecdsa_s2c_tests) / sizeof(ecdsa_s2c_tests[0]); i++) {
|
||||
secp256k1_ecdsa_s2c_opening s2c_opening;
|
||||
unsigned char buf[33];
|
||||
const ecdsa_s2c_test *test = &ecdsa_s2c_tests[i];
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &s2c_opening, message, privkey, test->s2c_data) == 1);
|
||||
CHECK(secp256k1_ecdsa_s2c_opening_serialize(ctx, buf, &s2c_opening) == 1);
|
||||
CHECK(memcmp(test->expected_s2c_klepto_opening, buf, sizeof(buf)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* This tests the full ECDSA Anti-Klepto Protocol */
|
||||
static void test_ecdsa_anti_klepto(void) {
|
||||
unsigned char signer_privkey[32];
|
||||
unsigned char host_msg[32];
|
||||
unsigned char host_commitment[32];
|
||||
unsigned char host_nonce_contribution[32];
|
||||
secp256k1_pubkey signer_pubkey;
|
||||
secp256k1_ecdsa_signature signature;
|
||||
secp256k1_ecdsa_s2c_opening s2c_opening;
|
||||
|
||||
/* Generate a random key, message. */
|
||||
{
|
||||
secp256k1_scalar key;
|
||||
random_scalar_order_test(&key);
|
||||
secp256k1_scalar_get_b32(signer_privkey, &key);
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &signer_pubkey, signer_privkey) == 1);
|
||||
secp256k1_testrand256_test(host_msg);
|
||||
secp256k1_testrand256_test(host_nonce_contribution);
|
||||
}
|
||||
|
||||
/* Protocol step 1. */
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_host_commit(ctx, host_commitment, host_nonce_contribution) == 1);
|
||||
/* Protocol step 2. */
|
||||
CHECK(secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &s2c_opening, host_msg, signer_privkey, host_commitment) == 1);
|
||||
/* Protocol step 3: host_nonce_contribution send to signer to be used in step 4. */
|
||||
/* Protocol step 4. */
|
||||
CHECK(secp256k1_anti_klepto_sign(ctx, &signature, host_msg, signer_privkey, host_nonce_contribution) == 1);
|
||||
/* Protocol step 5. */
|
||||
CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1);
|
||||
/* Protocol step 5 (explicitly) */
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, host_nonce_contribution, &s2c_opening) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &signature, host_msg, &signer_pubkey) == 1);
|
||||
|
||||
{ /* host_verify: commitment does not match */
|
||||
unsigned char sigbytes[64];
|
||||
size_t i;
|
||||
CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, sigbytes, &signature) == 1);
|
||||
for(i = 0; i < 32; i++) {
|
||||
/* change one byte */
|
||||
sigbytes[i] += 1;
|
||||
CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &signature, sigbytes) == 1);
|
||||
CHECK(secp256k1_ecdsa_s2c_verify_commit(ctx, &signature, host_nonce_contribution, &s2c_opening) == 0);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0);
|
||||
/* revert */
|
||||
sigbytes[i] -= 1;
|
||||
}
|
||||
CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &signature, sigbytes) == 1);
|
||||
}
|
||||
{ /* host_verify: message does not match */
|
||||
unsigned char bad_msg[32];
|
||||
secp256k1_testrand256_test(bad_msg);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 1);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, bad_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0);
|
||||
}
|
||||
{ /* s2c_sign: host provided data that didn't match commitment */
|
||||
secp256k1_ecdsa_s2c_opening orig_opening = s2c_opening;
|
||||
unsigned char bad_nonce_contribution[32] = { 1, 2, 3, 4 };
|
||||
CHECK(secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, host_msg, signer_privkey, bad_nonce_contribution) == 1);
|
||||
/* good signature but the opening (original public nonce does not match the original */
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &signature, host_msg, &signer_pubkey) == 1);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, host_nonce_contribution, &s2c_opening) == 0);
|
||||
CHECK(secp256k1_anti_klepto_host_verify(ctx, &signature, host_msg, &signer_pubkey, bad_nonce_contribution, &s2c_opening) == 1);
|
||||
CHECK(memcmp(&s2c_opening, &orig_opening, sizeof(s2c_opening)) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void run_ecdsa_s2c_tests(void) {
|
||||
run_s2c_opening_test();
|
||||
test_ecdsa_s2c_tagged_hash();
|
||||
test_ecdsa_s2c_api();
|
||||
test_ecdsa_s2c_fixed_vectors();
|
||||
test_ecdsa_s2c_sign_verify();
|
||||
|
||||
test_ecdsa_anti_klepto_signer_commit();
|
||||
test_ecdsa_anti_klepto();
|
||||
}
|
||||
|
||||
#endif /* SECP256K1_MODULE_ECDSA_S2C_TESTS_H */
|
||||
@@ -107,7 +107,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25
|
||||
for (i = 0; i < N_SIGNERS; i++) {
|
||||
for (j = 0; j < N_SIGNERS; j++) {
|
||||
/* To check whether signing was successful, it suffices to either verify
|
||||
* the the combined signature with the combined public key using
|
||||
* the combined signature with the combined public key using
|
||||
* secp256k1_schnorrsig_verify, or verify all partial signatures of all
|
||||
* signers individually. Verifying the combined signature is cheaper but
|
||||
* verifying the individual partial signatures has the advantage that it
|
||||
|
||||
@@ -129,7 +129,7 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd
|
||||
ARG_CHECK(signature != NULL);
|
||||
ARG_CHECK(seckey != NULL);
|
||||
|
||||
ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, msg32, seckey, noncefp, noncedata);
|
||||
ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, &recid, NULL, NULL, NULL, msg32, seckey, noncefp, noncedata);
|
||||
secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "field_impl.h"
|
||||
#include "scalar_impl.h"
|
||||
#include "group_impl.h"
|
||||
#include "eccommit_impl.h"
|
||||
#include "ecmult_impl.h"
|
||||
#include "ecmult_const_impl.h"
|
||||
#include "ecmult_gen_impl.h"
|
||||
@@ -36,6 +37,18 @@
|
||||
# include "modules/rangeproof/rangeproof.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_ECDSA_S2C
|
||||
# include "include/secp256k1_ecdsa_s2c.h"
|
||||
static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge);
|
||||
#else
|
||||
typedef void secp256k1_ecdsa_s2c_opening;
|
||||
static void secp256k1_ecdsa_s2c_opening_save(secp256k1_ecdsa_s2c_opening* opening, secp256k1_ge* ge) {
|
||||
(void) opening;
|
||||
(void) ge;
|
||||
VERIFY_CHECK(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ARG_CHECK(cond) do { \
|
||||
if (EXPECT(!(cond), 0)) { \
|
||||
secp256k1_callback_call(&ctx->illegal_callback, #cond); \
|
||||
@@ -487,7 +500,7 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
|
||||
const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979;
|
||||
const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979;
|
||||
|
||||
static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
|
||||
static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, secp256k1_sha256* s2c_sha, secp256k1_ecdsa_s2c_opening *s2c_opening, const unsigned char* s2c_data32, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
|
||||
secp256k1_scalar sec, non, msg;
|
||||
int ret = 0;
|
||||
int is_sec_valid;
|
||||
@@ -502,6 +515,11 @@ static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_sc
|
||||
if (noncefp == NULL) {
|
||||
noncefp = secp256k1_nonce_function_default;
|
||||
}
|
||||
/* sign-to-contract commitments only work with the default nonce function,
|
||||
* because we need to ensure that s2c_data is actually hashed into the nonce and
|
||||
* not just ignored. Otherwise an attacker can exfiltrate the secret key by
|
||||
* signing the same message thrice with different commitments. */
|
||||
VERIFY_CHECK(s2c_data32 == NULL || noncefp == secp256k1_nonce_function_default);
|
||||
|
||||
/* Fail if the secret key is invalid. */
|
||||
is_sec_valid = secp256k1_scalar_set_b32_seckey(&sec, seckey);
|
||||
@@ -517,6 +535,30 @@ static int secp256k1_ecdsa_sign_inner(const secp256k1_context* ctx, secp256k1_sc
|
||||
/* The nonce is still secret here, but it being invalid is is less likely than 1:2^255. */
|
||||
secp256k1_declassify(ctx, &is_nonce_valid, sizeof(is_nonce_valid));
|
||||
if (is_nonce_valid) {
|
||||
if (s2c_data32 != NULL) {
|
||||
secp256k1_gej nonce_pj;
|
||||
secp256k1_ge nonce_p;
|
||||
|
||||
/* Compute original nonce commitment/pubkey */
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &nonce_pj, &non);
|
||||
secp256k1_ge_set_gej(&nonce_p, &nonce_pj);
|
||||
if (s2c_opening != NULL) {
|
||||
secp256k1_ecdsa_s2c_opening_save(s2c_opening, &nonce_p);
|
||||
}
|
||||
|
||||
/* Because the nonce is valid, the nonce point isn't the point
|
||||
* at infinity and we can declassify that information to be able to
|
||||
* serialize the point. */
|
||||
secp256k1_declassify(ctx, &nonce_p.infinity, sizeof(nonce_p.infinity));
|
||||
|
||||
/* Tweak nonce with s2c commitment. */
|
||||
ret = secp256k1_ec_commit_seckey(&non, &nonce_p, s2c_sha, s2c_data32, 32);
|
||||
secp256k1_declassify(ctx, &ret, sizeof(ret)); /* may be secret that the tweak falied, but happens with negligible probability */
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, r, s, &sec, &msg, &non, recid);
|
||||
/* The final signature is no longer a secret, nor is the fact that we were successful or not. */
|
||||
secp256k1_declassify(ctx, &ret, sizeof(ret));
|
||||
@@ -552,7 +594,7 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature
|
||||
ARG_CHECK(signature != NULL);
|
||||
ARG_CHECK(seckey != NULL);
|
||||
|
||||
ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, msg32, seckey, noncefp, noncedata);
|
||||
ret = secp256k1_ecdsa_sign_inner(ctx, &r, &s, NULL, NULL, NULL, NULL, msg32, seckey, noncefp, noncedata);
|
||||
secp256k1_ecdsa_signature_save(signature, &r, &s);
|
||||
return ret;
|
||||
}
|
||||
@@ -785,6 +827,10 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *
|
||||
# include "modules/schnorrsig/main_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_ECDSA_S2C
|
||||
# include "modules/ecdsa_s2c/main_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_MUSIG
|
||||
# include "modules/musig/main_impl.h"
|
||||
#endif
|
||||
|
||||
87
src/tests.c
87
src/tests.c
@@ -2609,6 +2609,83 @@ void run_ec_combine(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void test_ec_commit(void) {
|
||||
secp256k1_scalar seckey_s;
|
||||
secp256k1_ge pubkey;
|
||||
secp256k1_gej pubkeyj;
|
||||
secp256k1_ge commitment;
|
||||
unsigned char data[32];
|
||||
secp256k1_sha256 sha;
|
||||
|
||||
/* Create random keypair and data */
|
||||
random_scalar_order_test(&seckey_s);
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj, &seckey_s);
|
||||
secp256k1_ge_set_gej(&pubkey, &pubkeyj);
|
||||
secp256k1_testrand256_test(data);
|
||||
|
||||
/* Commit to data and verify */
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
CHECK(secp256k1_ec_commit(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 32) == 1);
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
CHECK(secp256k1_ec_commit_verify(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 32) == 1);
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
CHECK(secp256k1_ec_commit_seckey(&seckey_s, &pubkey, &sha, data, 32) == 1);
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj, &seckey_s);
|
||||
ge_equals_gej(&commitment, &pubkeyj);
|
||||
|
||||
/* Check that verification fails with different data */
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
CHECK(secp256k1_ec_commit_verify(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 31) == 0);
|
||||
|
||||
/* Check that commmitting fails when the inner pubkey is the point at
|
||||
* infinity */
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_ge_set_infinity(&pubkey);
|
||||
CHECK(secp256k1_ec_commit(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 32) == 0);
|
||||
secp256k1_scalar_set_int(&seckey_s, 0);
|
||||
CHECK(secp256k1_ec_commit_seckey(&seckey_s, &pubkey, &sha, data, 32) == 0);
|
||||
CHECK(secp256k1_ec_commit_verify(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 32) == 0);
|
||||
}
|
||||
|
||||
void test_ec_commit_api(void) {
|
||||
unsigned char seckey[32];
|
||||
secp256k1_scalar seckey_s;
|
||||
secp256k1_ge pubkey;
|
||||
secp256k1_gej pubkeyj;
|
||||
secp256k1_ge commitment;
|
||||
unsigned char data[32];
|
||||
secp256k1_sha256 sha;
|
||||
|
||||
memset(data, 23, sizeof(data));
|
||||
|
||||
/* Create random keypair */
|
||||
random_scalar_order_test(&seckey_s);
|
||||
secp256k1_scalar_get_b32(seckey, &seckey_s);
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj, &seckey_s);
|
||||
secp256k1_ge_set_gej(&pubkey, &pubkeyj);
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
CHECK(secp256k1_ec_commit(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 1) == 1);
|
||||
/* The same pubkey can be both input and output of the function */
|
||||
{
|
||||
secp256k1_ge pubkey_tmp = pubkey;
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
CHECK(secp256k1_ec_commit(&ctx->ecmult_ctx, &pubkey_tmp, &pubkey_tmp, &sha, data, 1) == 1);
|
||||
ge_equals_ge(&commitment, &pubkey_tmp);
|
||||
}
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
CHECK(secp256k1_ec_commit_verify(&ctx->ecmult_ctx, &commitment, &pubkey, &sha, data, 1) == 1);
|
||||
}
|
||||
|
||||
void run_ec_commit(void) {
|
||||
int i;
|
||||
for (i = 0; i < count * 8; i++) {
|
||||
test_ec_commit();
|
||||
}
|
||||
test_ec_commit_api();
|
||||
}
|
||||
|
||||
void test_group_decompress(const secp256k1_fe* x) {
|
||||
/* The input itself, normalized. */
|
||||
secp256k1_fe fex = *x;
|
||||
@@ -5620,6 +5697,10 @@ void run_ecdsa_openssl(void) {
|
||||
# include "modules/schnorrsig/tests_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_ECDSA_S2C
|
||||
# include "modules/ecdsa_s2c/tests_impl.h"
|
||||
#endif
|
||||
|
||||
void run_secp256k1_memczero_test(void) {
|
||||
unsigned char buf1[6] = {1, 2, 3, 4, 5, 6};
|
||||
unsigned char buf2[sizeof(buf1)];
|
||||
@@ -5867,6 +5948,7 @@ int main(int argc, char **argv) {
|
||||
run_ecmult_const_tests();
|
||||
run_ecmult_multi_tests();
|
||||
run_ec_combine();
|
||||
run_ec_commit();
|
||||
|
||||
/* endomorphism tests */
|
||||
run_endomorphism_tests();
|
||||
@@ -5929,6 +6011,11 @@ int main(int argc, char **argv) {
|
||||
run_schnorrsig_tests();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_ECDSA_S2C
|
||||
/* ECDSA sign to contract */
|
||||
run_ecdsa_s2c_tests();
|
||||
#endif
|
||||
|
||||
/* util tests */
|
||||
run_secp256k1_memczero_test();
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#include "include/secp256k1_schnorrsig.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_ECDSA_S2C
|
||||
#include "include/secp256k1_ecdsa_s2c.h"
|
||||
#endif
|
||||
|
||||
int main(void) {
|
||||
secp256k1_context* ctx;
|
||||
secp256k1_ecdsa_signature signature;
|
||||
@@ -152,6 +156,31 @@ int main(void) {
|
||||
CHECK(ret == 1);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_MODULE_ECDSA_S2C
|
||||
{
|
||||
unsigned char s2c_data[32] = {0};
|
||||
unsigned char s2c_data_comm[32] = {0};
|
||||
secp256k1_ecdsa_s2c_opening s2c_opening;
|
||||
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(s2c_data, 32);
|
||||
ret = secp256k1_ecdsa_s2c_sign(ctx, &signature, &s2c_opening, msg, key, s2c_data);
|
||||
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
|
||||
CHECK(ret == 1);
|
||||
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(s2c_data, 32);
|
||||
ret = secp256k1_ecdsa_anti_klepto_host_commit(ctx, s2c_data_comm, s2c_data);
|
||||
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
|
||||
CHECK(ret == 1);
|
||||
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(s2c_data, 32);
|
||||
ret = secp256k1_ecdsa_anti_klepto_signer_commit(ctx, &s2c_opening, msg, key, s2c_data);
|
||||
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
|
||||
CHECK(ret == 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
secp256k1_context_destroy(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user