Skip to content

Commit da4ed8d

Browse files
committed
Add ec_commitments which are essentially the pay-to-contract-style tweaks of public keys.
The functionality is not exposed.
1 parent d6dfc45 commit da4ed8d

File tree

2 files changed

+163
-0
lines changed

2 files changed

+163
-0
lines changed

src/secp256k1.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,89 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *
705705
return 1;
706706
}
707707

708+
/* Compute an ec commitment tweak as hash(pubkey, data). */
709+
static int secp256k1_ec_commit_tweak(const secp256k1_context *ctx, unsigned char *tweak32, const secp256k1_pubkey *pubkey, const unsigned char *data, size_t data_size) {
710+
secp256k1_ge p;
711+
unsigned char rbuf[33];
712+
size_t rbuf_size = sizeof(rbuf);
713+
secp256k1_sha256 sha;
714+
715+
if (data_size == 0) {
716+
/* That's probably not what the caller wanted */
717+
return 0;
718+
}
719+
if(!secp256k1_pubkey_load(ctx, &p, pubkey)) {
720+
return 0;
721+
}
722+
secp256k1_eckey_pubkey_serialize(&p, rbuf, &rbuf_size, 1);
723+
724+
secp256k1_sha256_initialize(&sha);
725+
secp256k1_sha256_write(&sha, rbuf, rbuf_size);
726+
secp256k1_sha256_write(&sha, data, data_size);
727+
secp256k1_sha256_finalize(&sha, tweak32);
728+
return 1;
729+
}
730+
731+
/* Compute an ec commitment as pubkey + hash(pubkey, data)*G. */
732+
static int secp256k1_ec_commit(const secp256k1_context* ctx, secp256k1_pubkey *commitment, const secp256k1_pubkey *pubkey, const unsigned char *data, size_t data_size) {
733+
unsigned char tweak[32];
734+
735+
*commitment = *pubkey;
736+
if (!secp256k1_ec_commit_tweak(ctx, tweak, commitment, data, data_size)) {
737+
return 0;
738+
}
739+
return secp256k1_ec_pubkey_tweak_add(ctx, commitment, tweak);
740+
}
741+
742+
/* Compute the seckey of an ec commitment from the original secret key of the pubkey as seckey +
743+
* hash(pubkey, data). */
744+
static int secp256k1_ec_commit_seckey(const secp256k1_context* ctx, unsigned char *seckey, const secp256k1_pubkey *pubkey, const unsigned char *data, size_t data_size) {
745+
unsigned char tweak[32];
746+
secp256k1_pubkey pubkey_tmp;
747+
748+
if (pubkey == NULL) {
749+
/* Compute pubkey from seckey if not provided */
750+
int overflow;
751+
secp256k1_scalar x;
752+
secp256k1_gej pj;
753+
secp256k1_ge p;
754+
755+
secp256k1_scalar_set_b32(&x, seckey, &overflow);
756+
if (overflow != 0) {
757+
return 0;
758+
}
759+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &x);
760+
secp256k1_ge_set_gej(&p, &pj);
761+
secp256k1_pubkey_save(&pubkey_tmp, &p);
762+
pubkey = &pubkey_tmp;
763+
}
764+
765+
if (!secp256k1_ec_commit_tweak(ctx, tweak, pubkey, data, data_size)) {
766+
return 0;
767+
}
768+
return secp256k1_ec_privkey_tweak_add(ctx, seckey, tweak);
769+
}
770+
771+
/* Verify an ec commitment as pubkey + hash(pubkey, data)*G ?= commitment. */
772+
static int secp256k1_ec_commit_verify(const secp256k1_context* ctx, const secp256k1_pubkey *commitment, const secp256k1_pubkey *pubkey, const unsigned char *data, size_t data_size) {
773+
secp256k1_gej pj;
774+
secp256k1_ge p;
775+
secp256k1_pubkey commitment_tmp;
776+
777+
if (!secp256k1_ec_commit(ctx, &commitment_tmp, pubkey, data, data_size)) {
778+
return 0;
779+
}
780+
781+
/* Return commitment == commitment_tmp */
782+
secp256k1_gej_set_infinity(&pj);
783+
secp256k1_pubkey_load(ctx, &p, &commitment_tmp);
784+
secp256k1_gej_add_ge_var(&pj, &pj, &p, NULL);
785+
secp256k1_pubkey_load(ctx, &p, commitment);
786+
secp256k1_ge_neg(&p, &p);
787+
secp256k1_gej_add_ge_var(&pj, &pj, &p, NULL);
788+
return secp256k1_gej_is_infinity(&pj);
789+
}
790+
708791
#ifdef ENABLE_MODULE_ECDH
709792
# include "modules/ecdh/main_impl.h"
710793
#endif

src/tests.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2475,6 +2475,85 @@ void run_ec_combine(void) {
24752475
}
24762476
}
24772477

2478+
int test_ec_commit_seckey(unsigned char *seckey, secp256k1_pubkey *commitment) {
2479+
/* Return if seckey is the discrete log of commitment */
2480+
secp256k1_pubkey pubkey_tmp;
2481+
return secp256k1_ec_pubkey_create(ctx, &pubkey_tmp, seckey) == 1
2482+
&& memcmp(&pubkey_tmp, commitment, sizeof(pubkey_tmp)) == 0;
2483+
}
2484+
2485+
void test_ec_commit(void) {
2486+
unsigned char seckey[32];
2487+
secp256k1_pubkey pubkey;
2488+
secp256k1_pubkey commitment;
2489+
unsigned char data[32];
2490+
2491+
/* Create random keypair and data */
2492+
secp256k1_rand256(seckey);
2493+
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey));
2494+
secp256k1_rand256_test(data);
2495+
2496+
/* Commit to data and verify */
2497+
CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 32));
2498+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 32));
2499+
CHECK(secp256k1_ec_commit_seckey(ctx, seckey, &pubkey, data, 32));
2500+
CHECK(test_ec_commit_seckey(seckey, &commitment) == 1);
2501+
2502+
/* Check that verification fails with different data */
2503+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 31) == 0);
2504+
}
2505+
2506+
void test_ec_commit_api(void) {
2507+
unsigned char seckey[32];
2508+
secp256k1_pubkey pubkey;
2509+
secp256k1_pubkey commitment;
2510+
unsigned char data[32];
2511+
2512+
memset(data, 23, sizeof(data));
2513+
2514+
/* Create random keypair */
2515+
secp256k1_rand256(seckey);
2516+
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey));
2517+
2518+
CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 1) == 1);
2519+
/* The same pubkey can be both input and output of the function */
2520+
{
2521+
secp256k1_pubkey pubkey_tmp = pubkey;
2522+
CHECK(secp256k1_ec_commit(ctx, &pubkey_tmp, &pubkey_tmp, data, 1) == 1);
2523+
CHECK(memcmp(commitment.data, pubkey_tmp.data, sizeof(commitment.data)) == 0);
2524+
}
2525+
2526+
/* If the pubkey is not provided it will be computed from seckey */
2527+
CHECK(secp256k1_ec_commit_seckey(ctx, seckey, NULL, data, 1) == 1);
2528+
CHECK(test_ec_commit_seckey(seckey, &commitment) == 1);
2529+
/* pubkey is not provided but seckey overflows */
2530+
{
2531+
unsigned char overflowed_seckey[32];
2532+
memset(overflowed_seckey, 0xFF, sizeof(overflowed_seckey));
2533+
CHECK(secp256k1_ec_commit_seckey(ctx, overflowed_seckey, NULL, data, 1) == 0);
2534+
}
2535+
2536+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 1) == 1);
2537+
2538+
/* Commitment to 0-len data should fail */
2539+
CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 0) == 0);
2540+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 0) == 0);
2541+
CHECK(memcmp(&pubkey.data, &commitment.data, sizeof(pubkey.data)) == 0);
2542+
{
2543+
unsigned char seckey_tmp[32];
2544+
memcpy(seckey_tmp, seckey, 32);
2545+
CHECK(secp256k1_ec_commit_seckey(ctx, seckey_tmp, &pubkey, data, 0) == 0);
2546+
}
2547+
}
2548+
2549+
void run_ec_commit(void) {
2550+
int i;
2551+
for (i = 0; i < count * 8; i++) {
2552+
test_ec_commit();
2553+
}
2554+
test_ec_commit_api();
2555+
}
2556+
24782557
void test_group_decompress(const secp256k1_fe* x) {
24792558
/* The input itself, normalized. */
24802559
secp256k1_fe fex = *x;
@@ -5372,6 +5451,7 @@ int main(int argc, char **argv) {
53725451
run_ecmult_const_tests();
53735452
run_ecmult_multi_tests();
53745453
run_ec_combine();
5454+
run_ec_commit();
53755455

53765456
/* endomorphism tests */
53775457
#ifdef USE_ENDOMORPHISM

0 commit comments

Comments
 (0)