diff --git a/configure.ac b/configure.ac index 82cb469864db7d..6823f04b6a32d2 100644 --- a/configure.ac +++ b/configure.ac @@ -2020,7 +2020,7 @@ LIBS_TEMP="$LIBS" unset LIBS LIBS="$LIBS_TEMP" -ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-schnorrsig" +ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --enable-module-recovery --enable-module-schnorrsig --enable-module-ecdh" AC_CONFIG_SUBDIRS([src/secp256k1]) AC_OUTPUT diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h index c8577984b1a5ef..4afa3427af2677 100644 --- a/src/secp256k1/include/secp256k1_ecdh.h +++ b/src/secp256k1/include/secp256k1_ecdh.h @@ -29,6 +29,10 @@ typedef int (*secp256k1_ecdh_hash_function)( * Populates the output parameter with 32 bytes. */ SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256; +/** An implementation of SHA256 hash function that applies to the x-only part of a public key. + * Populates the output parameter with 32 bytes. */ +SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_xonly_sha256; + /** A default ECDH hash function (currently equal to secp256k1_ecdh_hash_function_sha256). * Populates the output parameter with 32 bytes. */ SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default; @@ -56,6 +60,32 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( void *data ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +#include "secp256k1_extrakeys.h" + +/** Compute an EC Diffie-Hellman secret in constant time (xonly version) + * (Only present if extrakeys is enabled). + * + * Returns: 1: exponentiation was successful + * 0: scalar was invalid (zero or overflow) or hashfp returned 0 + * Args: ctx: pointer to a context object. + * Out: output: pointer to an array to be filled by hashfp. + * In: pubkey: a pointer to a secp256k1_xonly_pubkey containing an initialized public key. + * seckey: a 32-byte scalar with which to multiply the point. + * hashfp: pointer to a hash function. If NULL, + * secp256k1_ecdh_hash_function_xonly_sha256 is used + * (in which case, 32 bytes will be written to output). + * data: arbitrary data pointer that is passed through to hashfp + * (can be NULL for secp256k1_ecdh_hash_function_xonly_sha256). + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh_xonly( + const secp256k1_context* ctx, + unsigned char *output, + const secp256k1_xonly_pubkey *pubkey, + const unsigned char *scalar, + secp256k1_ecdh_hash_function hashfp, + void *data +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + #ifdef __cplusplus } #endif diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h index 5408c9de707107..aa0aafd434bf98 100644 --- a/src/secp256k1/src/modules/ecdh/main_impl.h +++ b/src/secp256k1/src/modules/ecdh/main_impl.h @@ -23,41 +23,43 @@ static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char return 1; } +static int ecdh_hash_function_xonly_sha256(unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) { + secp256k1_sha256 sha; + (void)data; + (void)y32; + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, x32, 32); + secp256k1_sha256_finalize(&sha, output); + + return 1; +} + const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256 = ecdh_hash_function_sha256; +const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_xonly_sha256 = ecdh_hash_function_xonly_sha256; const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default = ecdh_hash_function_sha256; -int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pubkey *point, const unsigned char *scalar, secp256k1_ecdh_hash_function hashfp, void *data) { +static int ecdh_core(unsigned char *output, secp256k1_ge *pt, const unsigned char *scalar, secp256k1_ecdh_hash_function hashfp, void *data) { int ret = 0; int overflow = 0; secp256k1_gej res; - secp256k1_ge pt; secp256k1_scalar s; unsigned char x[32]; unsigned char y[32]; - VERIFY_CHECK(ctx != NULL); - ARG_CHECK(output != NULL); - ARG_CHECK(point != NULL); - ARG_CHECK(scalar != NULL); - - if (hashfp == NULL) { - hashfp = secp256k1_ecdh_hash_function_default; - } - - secp256k1_pubkey_load(ctx, &pt, point); secp256k1_scalar_set_b32(&s, scalar, &overflow); overflow |= secp256k1_scalar_is_zero(&s); secp256k1_scalar_cmov(&s, &secp256k1_scalar_one, overflow); - secp256k1_ecmult_const(&res, &pt, &s, 256); - secp256k1_ge_set_gej(&pt, &res); + secp256k1_ecmult_const(&res, pt, &s, 256); + secp256k1_ge_set_gej(pt, &res); /* Compute a hash of the point */ - secp256k1_fe_normalize(&pt.x); - secp256k1_fe_normalize(&pt.y); - secp256k1_fe_get_b32(x, &pt.x); - secp256k1_fe_get_b32(y, &pt.y); + secp256k1_fe_normalize(&pt->x); + secp256k1_fe_normalize(&pt->y); + secp256k1_fe_get_b32(x, &pt->x); + secp256k1_fe_get_b32(y, &pt->y); ret = hashfp(output, x, y, data); @@ -68,4 +70,44 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const se return !!ret & !overflow; } +int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *output, const secp256k1_pubkey *point, const unsigned char *scalar, secp256k1_ecdh_hash_function hashfp, void *data) { + secp256k1_ge pt; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(point != NULL); + ARG_CHECK(scalar != NULL); + + if (hashfp == NULL) { + hashfp = secp256k1_ecdh_hash_function_default; + } + + secp256k1_pubkey_load(ctx, &pt, point); + return ecdh_core(output, &pt, scalar, hashfp, data); +} + +#ifdef ENABLE_MODULE_EXTRAKEYS +#include "../../../include/secp256k1_extrakeys.h" + +int secp256k1_ecdh_xonly(const secp256k1_context* ctx, unsigned char *output, const secp256k1_xonly_pubkey *pubkey, const unsigned char *scalar, secp256k1_ecdh_hash_function hashfp, void *data) { + secp256k1_ge pt; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(scalar != NULL); + + if (hashfp == NULL) { + hashfp = secp256k1_ecdh_hash_function_xonly_sha256; + } + + /* FIXME: this is secp256k1_xonly_pubkey_load(), but that's buried inside + * extrakeys/main_impl.h. */ + if (!secp256k1_pubkey_load(ctx, &pt, (const secp256k1_pubkey *) pubkey)) { + return 0; + } + return ecdh_core(output, &pt, scalar, hashfp, data); +} +#endif /* ENABLE_MODULE_EXTRAKEYS */ + #endif /* SECP256K1_MODULE_ECDH_MAIN_H */ diff --git a/src/secp256k1/src/modules/extrakeys/tests_impl.h b/src/secp256k1/src/modules/extrakeys/tests_impl.h index c8a99f44668651..9d29750fdd585b 100644 --- a/src/secp256k1/src/modules/extrakeys/tests_impl.h +++ b/src/secp256k1/src/modules/extrakeys/tests_impl.h @@ -579,6 +579,35 @@ void test_keypair_add(void) { secp256k1_context_destroy(verify); } +void test_xonly_ecdh(void) { +#ifdef ENABLE_MODULE_ECDH + unsigned char sk1[32], sk2[32]; + secp256k1_keypair keypair1, keypair2; + secp256k1_xonly_pubkey xpubkey1, xpubkey2; + secp256k1_pubkey pubkey1; + unsigned char output1[32], output2[32], output3[32]; + + /* Create random points */ + secp256k1_testrand256(sk1); + secp256k1_testrand256(sk2); + CHECK(secp256k1_keypair_create(ctx, &keypair1, sk1) == 1); + CHECK(secp256k1_keypair_create(ctx, &keypair2, sk2) == 1); + CHECK(secp256k1_keypair_xonly_pub(ctx, &xpubkey1, NULL, &keypair1) == 1); + CHECK(secp256k1_keypair_xonly_pub(ctx, &xpubkey2, NULL, &keypair2) == 1); + + /* They should get the same shared secret. */ + CHECK(secp256k1_ecdh_xonly(ctx, output1, &xpubkey1, sk2, NULL, NULL) == 1); + CHECK(secp256k1_ecdh_xonly(ctx, output2, &xpubkey2, sk1, NULL, NULL) == 1); + CHECK(memcmp(output1, output2, sizeof(output1)) == 0); + + /* We can also do ECDH via normal compressed pubkey functions, if we + * use the same hashfn */ + CHECK(secp256k1_keypair_pub(ctx, &pubkey1, &keypair1) == 1); + CHECK(secp256k1_ecdh(ctx, output3, &pubkey1, sk2, secp256k1_ecdh_hash_function_xonly_sha256, NULL) == 1); + CHECK(memcmp(output1, output3, sizeof(output1)) == 0); +#endif /* ENABLE_MODULE_ECDH */ +} + void run_extrakeys_tests(void) { /* xonly key test cases */ test_xonly_pubkey(); @@ -586,6 +615,7 @@ void run_extrakeys_tests(void) { test_xonly_pubkey_tweak_check(); test_xonly_pubkey_tweak_recursive(); test_xonly_pubkey_comparison(); + test_xonly_ecdh(); /* keypair tests */ test_keypair();