diff --git a/Makefile.am b/Makefile.am index 1cba7a34f..7309fa4aa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,6 +16,8 @@ noinst_HEADERS += src/group.h noinst_HEADERS += src/group_impl.h noinst_HEADERS += src/num_gmp.h noinst_HEADERS += src/num_gmp_impl.h +noinst_HEADERS += src/eccommit.h +noinst_HEADERS += src/eccommit_impl.h noinst_HEADERS += src/ecdsa.h noinst_HEADERS += src/ecdsa_impl.h noinst_HEADERS += src/eckey.h diff --git a/src/eccommit.h b/src/eccommit.h new file mode 100644 index 000000000..6bb110399 --- /dev/null +++ b/src/eccommit.h @@ -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 */ diff --git a/src/eccommit_impl.h b/src/eccommit_impl.h new file mode 100644 index 000000000..641c07d21 --- /dev/null +++ b/src/eccommit_impl.h @@ -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 + +#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); +} + diff --git a/src/modules/musig/example.c b/src/modules/musig/example.c index 2c5b70061..fa3f58335 100644 --- a/src/modules/musig/example.c +++ b/src/modules/musig/example.c @@ -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 diff --git a/src/secp256k1.c b/src/secp256k1.c index 42309a03d..0c7a25757 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -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" diff --git a/src/tests.c b/src/tests.c index 33aef6edd..bd6f7fd14 100644 --- a/src/tests.c +++ b/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; @@ -5858,6 +5935,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();