Skip to content

Commit 4debe30

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 d65adc8 commit 4debe30

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed

src/secp256k1.c

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,103 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *
609609
return 1;
610610
}
611611

612+
/* Compute an ec commitment tweak as hash(pubkey, data). */
613+
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) {
614+
secp256k1_ge p;
615+
unsigned char rbuf[33];
616+
size_t rbuf_size = sizeof(rbuf);
617+
secp256k1_sha256 sha;
618+
619+
if (data_size == 0) {
620+
/* That's probably not what the caller wanted */
621+
return 0;
622+
}
623+
if(!secp256k1_pubkey_load(ctx, &p, pubkey)) {
624+
return 0;
625+
}
626+
secp256k1_eckey_pubkey_serialize(&p, rbuf, &rbuf_size, 1);
627+
628+
secp256k1_sha256_initialize(&sha);
629+
secp256k1_sha256_write(&sha, rbuf, rbuf_size);
630+
secp256k1_sha256_write(&sha, data, data_size);
631+
secp256k1_sha256_finalize(&sha, tweak32);
632+
return 1;
633+
}
634+
635+
/* Compute an ec commitment as pubkey + hash(pubkey, data)*G. */
636+
static int secp256k1_ec_commit(const secp256k1_context* ctx, secp256k1_pubkey *commitment, const secp256k1_pubkey *pubkey, const unsigned char *data, size_t data_size) {
637+
unsigned char tweak[32];
638+
639+
VERIFY_CHECK(ctx != NULL);
640+
ARG_CHECK(commitment != NULL);
641+
ARG_CHECK(pubkey != NULL);
642+
ARG_CHECK(data != NULL);
643+
644+
*commitment = *pubkey;
645+
if (!secp256k1_ec_commit_tweak(ctx, tweak, commitment, data, data_size)) {
646+
return 0;
647+
}
648+
return secp256k1_ec_pubkey_tweak_add(ctx, commitment, tweak);
649+
}
650+
651+
/* Compute the seckey of an ec commitment from the original secret key of the pubkey as seckey +
652+
* hash(pubkey, data). */
653+
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) {
654+
unsigned char tweak[32];
655+
secp256k1_pubkey pubkey_tmp;
656+
657+
VERIFY_CHECK(ctx != NULL);
658+
ARG_CHECK(seckey != NULL);
659+
ARG_CHECK(data != NULL);
660+
661+
if (pubkey == NULL) {
662+
/* Compute pubkey from seckey if not provided */
663+
int overflow;
664+
secp256k1_scalar x;
665+
secp256k1_gej pj;
666+
secp256k1_ge p;
667+
668+
secp256k1_scalar_set_b32(&x, seckey, &overflow);
669+
if (overflow != 0) {
670+
return 0;
671+
}
672+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &x);
673+
secp256k1_ge_set_gej(&p, &pj);
674+
secp256k1_pubkey_save(&pubkey_tmp, &p);
675+
pubkey = &pubkey_tmp;
676+
}
677+
678+
if (!secp256k1_ec_commit_tweak(ctx, tweak, pubkey, data, data_size)) {
679+
return 0;
680+
}
681+
return secp256k1_ec_privkey_tweak_add(ctx, seckey, tweak);
682+
}
683+
684+
/* Verify an ec commitment as pubkey + hash(pubkey, data)*G ?= commitment. */
685+
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) {
686+
secp256k1_gej pj;
687+
secp256k1_ge p;
688+
secp256k1_pubkey commitment_tmp;
689+
690+
VERIFY_CHECK(ctx != NULL);
691+
ARG_CHECK(commitment != NULL);
692+
ARG_CHECK(pubkey != NULL);
693+
ARG_CHECK(data != NULL);
694+
695+
if (!secp256k1_ec_commit(ctx, &commitment_tmp, pubkey, data, data_size)) {
696+
return 0;
697+
}
698+
699+
/* Return commitment == commitment_tmp */
700+
secp256k1_gej_set_infinity(&pj);
701+
secp256k1_pubkey_load(ctx, &p, &commitment_tmp);
702+
secp256k1_gej_add_ge(&pj, &pj, &p);
703+
secp256k1_pubkey_load(ctx, &p, commitment);
704+
secp256k1_ge_neg(&p, &p);
705+
secp256k1_gej_add_ge(&pj, &pj, &p);
706+
return secp256k1_gej_is_infinity(&pj);
707+
}
708+
612709
#ifdef ENABLE_MODULE_ECDH
613710
# include "modules/ecdh/main_impl.h"
614711
#endif

src/tests.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2339,6 +2339,106 @@ void run_ec_combine(void) {
23392339
}
23402340
}
23412341

2342+
int test_ec_commit_seckey(unsigned char *seckey, secp256k1_pubkey *commitment) {
2343+
/* Return if seckey is the discrete log of commitment */
2344+
secp256k1_pubkey pubkey_tmp;
2345+
return secp256k1_ec_pubkey_create(ctx, &pubkey_tmp, seckey) == 1
2346+
&& memcmp(&pubkey_tmp, commitment, sizeof(pubkey_tmp)) == 0;
2347+
}
2348+
2349+
void test_ec_commit(void) {
2350+
unsigned char seckey[32];
2351+
secp256k1_pubkey pubkey;
2352+
secp256k1_pubkey commitment;
2353+
unsigned char data[32];
2354+
2355+
/* Create random keypair and data */
2356+
secp256k1_rand256(seckey);
2357+
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey));
2358+
secp256k1_rand256_test(data);
2359+
2360+
/* Commit to data and verify */
2361+
CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 32));
2362+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 32));
2363+
CHECK(secp256k1_ec_commit_seckey(ctx, seckey, &pubkey, data, 32));
2364+
CHECK(test_ec_commit_seckey(seckey, &commitment) == 1);
2365+
2366+
/* Check that verification fails with different data */
2367+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 31) == 0);
2368+
}
2369+
2370+
void test_ec_commit_api(void) {
2371+
unsigned char seckey[32];
2372+
secp256k1_pubkey pubkey;
2373+
secp256k1_pubkey commitment;
2374+
unsigned char data[32];
2375+
int32_t ecount;
2376+
2377+
memset(data, 23, sizeof(data));
2378+
secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount);
2379+
2380+
/* Create random keypair */
2381+
secp256k1_rand256(seckey);
2382+
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey));
2383+
2384+
ecount = 0;
2385+
CHECK(secp256k1_ec_commit(ctx, NULL, &pubkey, data, 1) == 0);
2386+
CHECK(ecount == 1);
2387+
CHECK(secp256k1_ec_commit(ctx, &commitment, NULL, data, 1) == 0);
2388+
CHECK(ecount == 2);
2389+
CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, NULL, 1) == 0);
2390+
CHECK(ecount == 3);
2391+
CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 1) == 1);
2392+
/* The same pubkey can be both input and output of the function */
2393+
{
2394+
secp256k1_pubkey pubkey_tmp = pubkey;
2395+
CHECK(secp256k1_ec_commit(ctx, &pubkey_tmp, &pubkey_tmp, data, 1) == 1);
2396+
CHECK(memcmp(commitment.data, pubkey_tmp.data, sizeof(commitment.data)) == 0);
2397+
}
2398+
2399+
ecount = 0;
2400+
CHECK(secp256k1_ec_commit_seckey(ctx, NULL, &pubkey, data, 1) == 0);
2401+
CHECK(ecount == 1);
2402+
/* If the pubkey is not provided it will be computed from seckey */
2403+
CHECK(secp256k1_ec_commit_seckey(ctx, seckey, NULL, data, 1) == 1);
2404+
CHECK(test_ec_commit_seckey(seckey, &commitment) == 1);
2405+
/* pubkey is not provided but seckey overflows */
2406+
{
2407+
unsigned char overflowed_seckey[32];
2408+
memset(overflowed_seckey, 0xFF, sizeof(overflowed_seckey));
2409+
CHECK(secp256k1_ec_commit_seckey(ctx, overflowed_seckey, NULL, data, 1) == 0);
2410+
}
2411+
CHECK(secp256k1_ec_commit_seckey(ctx, seckey, &pubkey, NULL, 1) == 0);
2412+
CHECK(ecount == 2);
2413+
2414+
ecount = 0;
2415+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 1) == 1);
2416+
CHECK(secp256k1_ec_commit_verify(ctx, NULL, &pubkey, data, 1) == 0);
2417+
CHECK(ecount == 1);
2418+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, NULL, data, 1) == 0);
2419+
CHECK(ecount == 2);
2420+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, NULL, 1) == 0);
2421+
CHECK(ecount == 3);
2422+
2423+
/* Commitment to 0-len data should fail */
2424+
CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 0) == 0);
2425+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 0) == 0);
2426+
CHECK(memcmp(&pubkey.data, &commitment.data, sizeof(pubkey.data)) == 0);
2427+
{
2428+
unsigned char seckey_tmp[32];
2429+
memcpy(seckey_tmp, seckey, 32);
2430+
CHECK(secp256k1_ec_commit_seckey(ctx, seckey_tmp, &pubkey, data, 0) == 0);
2431+
}
2432+
}
2433+
2434+
void run_ec_commit(void) {
2435+
int i;
2436+
for (i = 0; i < count * 8; i++) {
2437+
test_ec_commit();
2438+
}
2439+
test_ec_commit_api();
2440+
}
2441+
23422442
void test_group_decompress(const secp256k1_fe* x) {
23432443
/* The input itself, normalized. */
23442444
secp256k1_fe fex = *x;
@@ -5157,6 +5257,7 @@ int main(int argc, char **argv) {
51575257
run_ecmult_const_tests();
51585258
run_ecmult_multi_tests();
51595259
run_ec_combine();
5260+
run_ec_commit();
51605261

51615262
/* endomorphism tests */
51625263
#ifdef USE_ENDOMORPHISM

0 commit comments

Comments
 (0)