diff --git a/include/secp256k1_ecdh.h b/include/secp256k1_ecdh.h index 88492dc1a40bc..09a39e89c86d0 100644 --- a/include/secp256k1_ecdh.h +++ b/include/secp256k1_ecdh.h @@ -7,21 +7,41 @@ extern "C" { #endif +/** A pointer to a function that applies hash function to a point + * + * Returns: 1 if a point was successfully hashed. 0 will cause ecdh to fail + * Out: output: pointer to an array to be filled by the function + * In: x: pointer to a 32-byte x coordinate + * y: pointer to a 32-byte y coordinate + */ +typedef int (*secp256k1_ecdh_hash_function)( + unsigned char *output, + const unsigned char *x, + const unsigned char *y +); + +/** An implementation of SHA256 hash function that applies to compressed public key. */ +SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_sha256; + +/** A default ecdh hash function (currently equal to secp256k1_ecdh_hash_function_sha256). */ +SECP256K1_API extern const secp256k1_ecdh_hash_function secp256k1_ecdh_hash_function_default; + /** Compute an EC Diffie-Hellman secret in constant time * Returns: 1: exponentiation was successful * 0: scalar was invalid (zero or overflow) * Args: ctx: pointer to a context object (cannot be NULL) - * Out: result: a 32-byte array which will be populated by an ECDH - * secret computed from the point and scalar + * Out: output: pointer to an array to be filled by the function * In: pubkey: a pointer to a secp256k1_pubkey containing an * initialized public key * privkey: a 32-byte scalar with which to multiply the point + * hashfp: pointer to a hash function. If NULL, secp256k1_ecdh_hash_function_sha256 is used */ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( const secp256k1_context* ctx, - unsigned char *result, + unsigned char *output, const secp256k1_pubkey *pubkey, - const unsigned char *privkey + const unsigned char *privkey, + secp256k1_ecdh_hash_function hashfp ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); #ifdef __cplusplus diff --git a/src/bench_ecdh.c b/src/bench_ecdh.c index 5837f4e7d3637..1c9c1a107776f 100644 --- a/src/bench_ecdh.c +++ b/src/bench_ecdh.c @@ -42,7 +42,7 @@ static void bench_ecdh(void* arg) { bench_ecdh_data *data = (bench_ecdh_data*)arg; for (i = 0; i < 20000; i++) { - CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar) == 1); + CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar, NULL) == 1); } } diff --git a/src/java/org_bitcoin_NativeSecp256k1.c b/src/java/org_bitcoin_NativeSecp256k1.c index bcef7b32ce3e7..5d3cde37cb604 100644 --- a/src/java/org_bitcoin_NativeSecp256k1.c +++ b/src/java/org_bitcoin_NativeSecp256k1.c @@ -83,7 +83,7 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e secp256k1_ecdsa_signature sig[72]; - int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL ); + int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL); unsigned char outputSer[72]; size_t outputLen = 72; @@ -353,7 +353,8 @@ SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1e ctx, nonce_res, &pubkey, - secdata + secdata, + NULL ); } diff --git a/src/modules/ecdh/main_impl.h b/src/modules/ecdh/main_impl.h index df3ec5c85e4c3..1cc9f26fb79a2 100644 --- a/src/modules/ecdh/main_impl.h +++ b/src/modules/ecdh/main_impl.h @@ -10,16 +10,34 @@ #include "include/secp256k1_ecdh.h" #include "ecmult_const_impl.h" -int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar) { +static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x, const unsigned char *y) { + unsigned char version = (y[31] & 0x01) | 0x02; + secp256k1_sha256 sha; + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &version, 1); + secp256k1_sha256_write(&sha, x, 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_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) { int ret = 0; int overflow = 0; secp256k1_gej res; secp256k1_ge pt; secp256k1_scalar s; VERIFY_CHECK(ctx != NULL); - ARG_CHECK(result != 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); @@ -27,24 +45,18 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se ret = 0; } else { unsigned char x[32]; - unsigned char y[1]; - secp256k1_sha256 sha; + unsigned char y[32]; secp256k1_ecmult_const(&res, &pt, &s, 256); secp256k1_ge_set_gej(&pt, &res); - /* Compute a hash of the point in compressed form - * Note we cannot use secp256k1_eckey_pubkey_serialize here since it does not - * expect its output to be secret and has a timing sidechannel. */ + + /* Compute a hash of the point */ secp256k1_fe_normalize(&pt.x); secp256k1_fe_normalize(&pt.y); secp256k1_fe_get_b32(x, &pt.x); - y[0] = 0x02 | secp256k1_fe_is_odd(&pt.y); + secp256k1_fe_get_b32(y, &pt.y); - secp256k1_sha256_initialize(&sha); - secp256k1_sha256_write(&sha, y, sizeof(y)); - secp256k1_sha256_write(&sha, x, sizeof(x)); - secp256k1_sha256_finalize(&sha, result); - ret = 1; + ret = hashfp(output, x, y); } secp256k1_scalar_clear(&s); diff --git a/src/modules/ecdh/tests_impl.h b/src/modules/ecdh/tests_impl.h index 0c53f8ee08bd2..63d45df17d56a 100644 --- a/src/modules/ecdh/tests_impl.h +++ b/src/modules/ecdh/tests_impl.h @@ -7,6 +7,21 @@ #ifndef SECP256K1_MODULE_ECDH_TESTS_H #define SECP256K1_MODULE_ECDH_TESTS_H +int ecdh_hash_function_test_fail(unsigned char *output, const unsigned char *x, const unsigned char *y) { + (void)output; + (void)x; + (void)y; + return 0; +} + +int ecdh_hash_function_custom(unsigned char *output, const unsigned char *x, const unsigned char *y) { + /* Save x and y as uncompressed public key */ + output[0] = 0x04; + memcpy(output + 1, x, 32); + memcpy(output + 33, y, 32); + return 1; +} + void test_ecdh_api(void) { /* Setup context that just counts errors */ secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); @@ -21,15 +36,15 @@ void test_ecdh_api(void) { CHECK(secp256k1_ec_pubkey_create(tctx, &point, s_one) == 1); /* Check all NULLs are detected */ - CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); + CHECK(secp256k1_ecdh(tctx, res, &point, s_one, NULL) == 1); CHECK(ecount == 0); - CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one) == 0); + CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one, NULL) == 0); CHECK(ecount == 1); - CHECK(secp256k1_ecdh(tctx, res, NULL, s_one) == 0); + CHECK(secp256k1_ecdh(tctx, res, NULL, s_one, NULL) == 0); CHECK(ecount == 2); - CHECK(secp256k1_ecdh(tctx, res, &point, NULL) == 0); + CHECK(secp256k1_ecdh(tctx, res, &point, NULL, NULL) == 0); CHECK(ecount == 3); - CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); + CHECK(secp256k1_ecdh(tctx, res, &point, s_one, NULL) == 1); CHECK(ecount == 3); /* Cleanup */ @@ -46,27 +61,34 @@ void test_ecdh_generator_basepoint(void) { for (i = 0; i < 100; ++i) { secp256k1_sha256 sha; unsigned char s_b32[32]; - unsigned char output_ecdh[32]; + unsigned char output_ecdh[65]; unsigned char output_ser[32]; - unsigned char point_ser[33]; + unsigned char point_ser[65]; size_t point_ser_len = sizeof(point_ser); secp256k1_scalar s; random_scalar_order(&s); secp256k1_scalar_get_b32(s_b32, &s); - /* compute using ECDH function */ CHECK(secp256k1_ec_pubkey_create(ctx, &point[0], s_one) == 1); - CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32) == 1); - /* compute "explicitly" */ CHECK(secp256k1_ec_pubkey_create(ctx, &point[1], s_b32) == 1); + + /* compute using ECDH function with custom hash function */ + CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, ecdh_hash_function_custom) == 1); + /* compute "explicitly" */ + CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_UNCOMPRESSED) == 1); + /* compare */ + CHECK(memcmp(output_ecdh, point_ser, 65) == 0); + + /* compute using ECDH function with default hash function */ + CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32, NULL) == 1); + /* compute "explicitly" */ CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1); - CHECK(point_ser_len == sizeof(point_ser)); secp256k1_sha256_initialize(&sha); secp256k1_sha256_write(&sha, point_ser, point_ser_len); secp256k1_sha256_finalize(&sha, output_ser); /* compare */ - CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0); + CHECK(memcmp(output_ecdh, output_ser, 32) == 0); } } @@ -89,11 +111,14 @@ void test_bad_scalar(void) { CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_rand) == 1); /* Try to multiply it by bad values */ - CHECK(secp256k1_ecdh(ctx, output, &point, s_zero) == 0); - CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 0); + CHECK(secp256k1_ecdh(ctx, output, &point, s_zero, NULL) == 0); + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, NULL) == 0); /* ...and a good one */ s_overflow[31] -= 1; - CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 1); + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, NULL) == 1); + + /* Hash function failure results in ecdh failure */ + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, ecdh_hash_function_test_fail) == 0); } void run_ecdh_tests(void) {