From 2a5914ac1a25cf84c7e140cd9001a4dce01277a8 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 1 Jul 2015 19:20:46 +0930 Subject: [PATCH] Demo code for x-only ECDH --- .gitignore | 1 + Makefile.am | 6 +++++- include/secp256k1.h | 6 ++++++ src/bench_ecdh_xo.c | 50 +++++++++++++++++++++++++++++++++++++++++++++ src/group.h | 2 ++ src/group_impl.h | 15 ++++++++++++++ src/secp256k1.c | 41 +++++++++++++++++++++++++++++++++++++ src/tests.c | 13 ++++++++++++ 8 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/bench_ecdh_xo.c diff --git a/.gitignore b/.gitignore index a697a794cc..904bdb26eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ bench_inv bench_ecdh +bench_ecdh_xo bench_sign bench_verify bench_recover diff --git a/Makefile.am b/Makefile.am index ea9770e011..54c3743036 100644 --- a/Makefile.am +++ b/Makefile.am @@ -51,7 +51,7 @@ libsecp256k1_la_LIBADD = $(SECP_LIBS) noinst_PROGRAMS = if USE_BENCHMARK -noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal bench_ecdh +noinst_PROGRAMS += bench_verify bench_recover bench_sign bench_internal bench_ecdh bench_ecdh_xo bench_verify_SOURCES = src/bench_verify.c bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) bench_verify_LDFLAGS = -static @@ -69,6 +69,10 @@ bench_ecdh_SOURCES = src/bench_ecdh.c bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS) bench_ecdh_LDFLAGS = -static bench_ecdh_CPPFLAGS = $(SECP_INCLUDES) +bench_ecdh_xo_SOURCES = src/bench_ecdh_xo.c +bench_ecdh_xo_LDADD = libsecp256k1.la $(SECP_LIBS) +bench_ecdh_xo_LDFLAGS = -static +bench_ecdh_xo_CPPFLAGS = $(SECP_INCLUDES) endif if USE_TESTS diff --git a/include/secp256k1.h b/include/secp256k1.h index c006ad4912..7c4ff801a3 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -235,6 +235,12 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( const unsigned char *scalar ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh_xo( + unsigned char *result, + const unsigned char *x, + const unsigned char *scalar +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + /** Verify an ECDSA secret key. * Returns: 1: secret key is valid * 0: secret key is invalid diff --git a/src/bench_ecdh_xo.c b/src/bench_ecdh_xo.c new file mode 100644 index 0000000000..7a8c42b3c6 --- /dev/null +++ b/src/bench_ecdh_xo.c @@ -0,0 +1,50 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "include/secp256k1.h" +#include "util.h" +#include "bench.h" + +typedef struct { + unsigned char point[33]; + int pointlen; + unsigned char scalar[32]; +} bench_multiply_t; + +static void bench_multiply_setup(void* arg) { + int i; + bench_multiply_t *data = (bench_multiply_t*)arg; + const unsigned char point[] = { + 0x03, + 0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06, + 0xc2, 0x39, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd, + 0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb, + 0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f + }; + + for (i = 0; i < 32; i++) data->scalar[i] = i + 1; + data->pointlen = sizeof(point); + memcpy(data->point, point, data->pointlen); +} + +static void bench_multiply(void* arg) { + int i; + unsigned char res[32]; + bench_multiply_t *data = (bench_multiply_t*)arg; + + for (i = 0; i < 20000; i++) { + CHECK(secp256k1_ecdh_xo(res, data->point+1, data->scalar) == 1); + } +} + +int main(void) { + bench_multiply_t data; + + run_benchmark("ecdh_mult_xo", bench_multiply, bench_multiply_setup, NULL, &data, 10, 20000); + return 0; +} diff --git a/src/group.h b/src/group.h index 1d9ef9d2aa..fcca4a6985 100644 --- a/src/group.h +++ b/src/group.h @@ -48,6 +48,8 @@ static void secp256k1_ge_set_xy(secp256k1_ge_t *r, const secp256k1_fe_t *x, cons * for Y. Return value indicates whether the result is valid. */ static int secp256k1_ge_set_xo_var(secp256k1_ge_t *r, const secp256k1_fe_t *x, int odd); +static int secp256k1_ge_set_xo_iso_var(secp256k1_ge_t *r, secp256k1_fe_t *rk, const secp256k1_fe_t *x); + /** Check whether a group element is the point at infinity. */ static int secp256k1_ge_is_infinity(const secp256k1_ge_t *a); diff --git a/src/group_impl.h b/src/group_impl.h index 2da8909793..43b2ec7403 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -196,6 +196,21 @@ static int secp256k1_ge_set_xo_var(secp256k1_ge_t *r, const secp256k1_fe_t *x, i return 1; } +static int secp256k1_ge_set_xo_iso_var(secp256k1_ge_t *r, secp256k1_fe_t *rk, const secp256k1_fe_t *x) { + secp256k1_fe_t t; + secp256k1_fe_sqr(&t, x); + secp256k1_fe_mul(&t, &t, x); + secp256k1_fe_set_int(rk, 7); + secp256k1_fe_add(rk, &t); /* K = X^3 + 7 (2) */ + + /* TODO Jacobi symbol test to make sure K is a square */ + + r->infinity = 0; + secp256k1_fe_mul(&r->x, rk, x); /* r->x = K*X (1) */ + secp256k1_fe_sqr(&r->y, rk); /* r->y = K^2 (1) */ + return 1; +} + static void secp256k1_gej_set_ge(secp256k1_gej_t *r, const secp256k1_ge_t *a) { r->infinity = a->infinity; r->x = a->x; diff --git a/src/secp256k1.c b/src/secp256k1.c index 0db661172b..2904c9a7a5 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -269,6 +269,47 @@ int secp256k1_ecdh(unsigned char *result, unsigned char *point, int *pointlen, c return ret; } +int secp256k1_ecdh_xo(unsigned char *result, const unsigned char *x, const unsigned char *scalar) { + int ret = 0; + int overflow = 0; + secp256k1_fe_t k, t; + secp256k1_gej_t res; + secp256k1_ge_t pt; + secp256k1_scalar_t s; + unsigned char input[32]; + secp256k1_sha256_t sha; + DEBUG_CHECK(result != NULL); + DEBUG_CHECK(x != NULL); + DEBUG_CHECK(scalar != NULL); + + secp256k1_scalar_set_b32(&s, scalar, &overflow); + if (secp256k1_scalar_is_zero(&s)) { + ret = -1; + } else if (overflow) { + ret = -2; + } else if (secp256k1_fe_set_b32(&t, x) && secp256k1_ge_set_xo_iso_var(&pt, &k, &t)) { + secp256k1_point_multiply(&res, &pt, &s); + if (!res.infinity) { + secp256k1_fe_sqr(&t, &res.z); + secp256k1_fe_mul(&t, &t, &k); + secp256k1_fe_inv(&k, &t); + secp256k1_fe_mul(&t, &res.x, &k); + secp256k1_fe_normalize(&t); + + /* secp256k1_fe_get_b32(result, &t); */ + secp256k1_fe_get_b32(input, &t); + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, input, sizeof(input)); + secp256k1_sha256_finalize(&sha, result); + ret = 1; + } + } else { + ret = -3; + } + secp256k1_scalar_clear(&s); + return ret; +} + int secp256k1_ec_seckey_verify(const secp256k1_context_t* ctx, const unsigned char *seckey) { secp256k1_scalar_t sec; int ret; diff --git a/src/tests.c b/src/tests.c index 3eb59a5662..ba5762e10c 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1381,6 +1381,19 @@ void ecdh_generator_basepoint(void) { secp256k1_sha256_finalize(&sha, output_ser); /* compare */ CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0); + + memset(output_ecdh, 0, sizeof(output_ecdh)); + memset(output_ser, 0, sizeof(output_ser)); + + /* compute using x-only ECDH function */ + secp256k1_eckey_pubkey_serialize(&gen, point, &pointlen, 1); + CHECK(secp256k1_ecdh_xo(output_ecdh, point+1, s_b32) == 1); + /* compute "explicitly" */ + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, point2+1, sizeof(point2)-1); + secp256k1_sha256_finalize(&sha, output_ser); + /* compare */ + CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0); } }