Skip to content

Commit 97118ca

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 364123a commit 97118ca

File tree

2 files changed

+203
-0
lines changed

2 files changed

+203
-0
lines changed

src/secp256k1.c

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

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

src/tests.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2287,6 +2287,111 @@ void run_ec_combine(void) {
22872287
}
22882288
}
22892289

2290+
int test_ec_commit_seckey(unsigned char *seckey, secp256k1_pubkey *commitment) {
2291+
/* Return if seckey is the discrete log of commitment */
2292+
secp256k1_pubkey pubkey_tmp;
2293+
return secp256k1_ec_pubkey_create(ctx, &pubkey_tmp, seckey) == 1
2294+
&& memcmp(&pubkey_tmp, commitment, sizeof(pubkey_tmp)) == 0;
2295+
}
2296+
2297+
void test_ec_commit(void) {
2298+
unsigned char seckey[32];
2299+
secp256k1_pubkey pubkey;
2300+
secp256k1_pubkey commitment;
2301+
unsigned char data[32];
2302+
2303+
/* Create random keypair */
2304+
secp256k1_rand256(seckey);
2305+
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey));
2306+
/* Create random data */
2307+
{
2308+
secp256k1_scalar d;
2309+
random_scalar_order_test(&d);
2310+
secp256k1_scalar_get_b32(data, &d);
2311+
}
2312+
/* Commit to data and verify */
2313+
CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 32));
2314+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 32));
2315+
CHECK(secp256k1_ec_commit_seckey(ctx, seckey, &pubkey, data, 32));
2316+
CHECK(test_ec_commit_seckey(seckey, &commitment) == 1);
2317+
2318+
/* Check that verification fails with different data */
2319+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 31) == 0);
2320+
}
2321+
2322+
void test_ec_commit_api(void) {
2323+
unsigned char seckey[32];
2324+
secp256k1_pubkey pubkey;
2325+
secp256k1_pubkey commitment;
2326+
unsigned char data[32];
2327+
int32_t ecount;
2328+
2329+
memset(data, 23, sizeof(data));
2330+
secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount);
2331+
2332+
/* Create random keypair */
2333+
secp256k1_rand256(seckey);
2334+
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey));
2335+
2336+
ecount = 0;
2337+
CHECK(secp256k1_ec_commit(ctx, NULL, &pubkey, data, 1) == 0);
2338+
CHECK(ecount == 1);
2339+
CHECK(secp256k1_ec_commit(ctx, &commitment, NULL, data, 1) == 0);
2340+
CHECK(ecount == 2);
2341+
CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, NULL, 1) == 0);
2342+
CHECK(ecount == 3);
2343+
CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 1) == 1);
2344+
/* The same pubkey can be both input and output of the function */
2345+
{
2346+
secp256k1_pubkey pubkey_tmp = pubkey;
2347+
CHECK(secp256k1_ec_commit(ctx, &pubkey_tmp, &pubkey_tmp, data, 1) == 1);
2348+
CHECK(memcmp(commitment.data, pubkey_tmp.data, sizeof(commitment.data)) == 0);
2349+
}
2350+
2351+
ecount = 0;
2352+
CHECK(secp256k1_ec_commit_seckey(ctx, NULL, &pubkey, data, 1) == 0);
2353+
CHECK(ecount == 1);
2354+
/* If the pubkey is not provided it will be computed from seckey */
2355+
CHECK(secp256k1_ec_commit_seckey(ctx, seckey, NULL, data, 1) == 1);
2356+
CHECK(test_ec_commit_seckey(seckey, &commitment) == 1);
2357+
/* pubkey is not provided but seckey overflows */
2358+
{
2359+
unsigned char overflowed_seckey[32];
2360+
memset(overflowed_seckey, 0xFF, sizeof(overflowed_seckey));
2361+
CHECK(secp256k1_ec_commit_seckey(ctx, overflowed_seckey, NULL, data, 1) == 0);
2362+
}
2363+
CHECK(secp256k1_ec_commit_seckey(ctx, seckey, &pubkey, NULL, 1) == 0);
2364+
CHECK(ecount == 2);
2365+
2366+
ecount = 0;
2367+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 1) == 1);
2368+
CHECK(secp256k1_ec_commit_verify(ctx, NULL, &pubkey, data, 1) == 0);
2369+
CHECK(ecount == 1);
2370+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, NULL, data, 1) == 0);
2371+
CHECK(ecount == 2);
2372+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, NULL, 1) == 0);
2373+
CHECK(ecount == 3);
2374+
2375+
/* Commitment to 0-len data should not modify secret or public key */
2376+
CHECK(secp256k1_ec_commit(ctx, &commitment, &pubkey, data, 0));
2377+
CHECK(secp256k1_ec_commit_verify(ctx, &commitment, &pubkey, data, 0));
2378+
CHECK(memcmp(&pubkey.data, &commitment.data, sizeof(pubkey.data)) == 0);
2379+
{
2380+
unsigned char seckey_tmp[32];
2381+
memcpy(seckey_tmp, seckey, 32);
2382+
CHECK(secp256k1_ec_commit_seckey(ctx, seckey_tmp, &pubkey, data, 0));
2383+
CHECK(memcmp(seckey, seckey_tmp, sizeof(seckey)) == 0);
2384+
}
2385+
}
2386+
2387+
void run_ec_commit(void) {
2388+
int i;
2389+
for (i = 0; i < count * 8; i++) {
2390+
test_ec_commit();
2391+
}
2392+
test_ec_commit_api();
2393+
}
2394+
22902395
void test_group_decompress(const secp256k1_fe* x) {
22912396
/* The input itself, normalized. */
22922397
secp256k1_fe fex = *x;
@@ -5106,6 +5211,7 @@ int main(int argc, char **argv) {
51065211
run_ecmult_const_tests();
51075212
run_ecmult_multi_tests();
51085213
run_ec_combine();
5214+
run_ec_commit();
51095215

51105216
/* endomorphism tests */
51115217
#ifdef USE_ENDOMORPHISM

0 commit comments

Comments
 (0)