From d68906ec4eeddf59bb9c67e0a8f8077991cd17c3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 May 2016 18:10:23 +0100 Subject: [PATCH 1/7] Use proper option for USE_KECCAK via options.h --- options.h | 5 +++++ sha3.c | 2 +- sha3.h | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/options.h b/options.h index d5ac4ab33e..4baadf0bd5 100644 --- a/options.h +++ b/options.h @@ -56,4 +56,9 @@ #define BIP39_CACHE_SIZE 4 #endif +// support Keccak hashing +#ifndef USE_KECCAK +#define USE_KECCAK 0 +#endif + #endif diff --git a/sha3.c b/sha3.c index 9f23b89d35..fc6d1c8bf7 100644 --- a/sha3.c +++ b/sha3.c @@ -331,7 +331,7 @@ void sha3_Final(SHA3_CTX *ctx, unsigned char* result) if (result) me64_to_le_str(result, ctx->hash, digest_length); } -#ifdef USE_KECCAK +#if USE_KECCAK /** * Store calculated hash into the given array. * diff --git a/sha3.h b/sha3.h index b809f33590..b605663ae0 100644 --- a/sha3.h +++ b/sha3.h @@ -21,6 +21,7 @@ #define __SHA3_H__ #include +#include "options.h" #ifdef __cplusplus extern "C" { @@ -57,7 +58,7 @@ void sha3_512_Init(SHA3_CTX *ctx); void sha3_Update(SHA3_CTX *ctx, const unsigned char* msg, size_t size); void sha3_Final(SHA3_CTX *ctx, unsigned char* result); -#ifdef USE_KECCAK +#if USE_KECCAK #define keccak_224_Init sha3_224_Init #define keccak_256_Init sha3_256_Init #define keccak_384_Init sha3_384_Init From 7d68a6ee1744972a9115e88f4213f99232d63acf Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 May 2016 18:11:31 +0100 Subject: [PATCH 2/7] Add ecdsa_uncompress_pubkey() Code based on @Arachnid's PR, but has more strict checks --- ecdsa.c | 21 +++++++++++++++++++++ ecdsa.h | 1 + 2 files changed, 22 insertions(+) diff --git a/ecdsa.c b/ecdsa.c index 9cbd928498..2394101f4f 100644 --- a/ecdsa.c +++ b/ecdsa.c @@ -815,6 +815,27 @@ void ecdsa_get_public_key65(const ecdsa_curve *curve, const uint8_t *priv_key, u MEMSET_BZERO(&k, sizeof(k)); } +int ecdsa_uncompress_pubkey(const ecdsa_curve *curve, const uint8_t *pub_key, uint8_t *uncompressed) +{ + if (pub_key[0] == 2 || pub_key[0] == 3) { + bignum256 x, y; + + bn_read_be(pub_key + 1, &x); + // uncompress_coords will check for pub_key[0] & 1 + uncompress_coords(curve, pub_key[0], &x, &y); + + uncompressed[0] = 4; + memcpy(uncompressed + 1, pub_key + 1, 32); + bn_write_be(&y, uncompressed + 33); + return 1; + } else if (pub_key[0] == 4) { + memcpy(uncompressed, pub_key, 65); + return 1; + } + + return 0; +} + void ecdsa_get_pubkeyhash(const uint8_t *pub_key, uint8_t *pubkeyhash) { uint8_t h[32]; diff --git a/ecdsa.h b/ecdsa.h index 45cbb81d55..b7c22aa4d8 100644 --- a/ecdsa.h +++ b/ecdsa.h @@ -58,6 +58,7 @@ int point_is_equal(const curve_point *p, const curve_point *q); int point_is_negative_of(const curve_point *p, const curve_point *q); void scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, curve_point *res); void uncompress_coords(const ecdsa_curve *curve, uint8_t odd, const bignum256 *x, bignum256 *y); +int ecdsa_uncompress_pubkey(const ecdsa_curve *curve, const uint8_t *pub_key, uint8_t *uncompressed); int ecdsa_sign(const ecdsa_curve *curve, const uint8_t *priv_key, const uint8_t *msg, uint32_t msg_len, uint8_t *sig, uint8_t *pby); int ecdsa_sign_double(const ecdsa_curve *curve, const uint8_t *priv_key, const uint8_t *msg, uint32_t msg_len, uint8_t *sig, uint8_t *pby); From 1b8e3d557f4d40559cd710b2d49fd7faf7d0c545 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 May 2016 18:12:13 +0100 Subject: [PATCH 3/7] Implement ecdsa_get_ethereum_pubkeyhash() --- ecdsa.c | 24 ++++++++++++++++++++++++ ecdsa.h | 1 + options.h | 7 ++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ecdsa.c b/ecdsa.c index 2394101f4f..3cb52ce44d 100644 --- a/ecdsa.c +++ b/ecdsa.c @@ -36,6 +36,9 @@ #include "base58.h" #include "macros.h" #include "secp256k1.h" +#if USE_ETHEREUM +#include "sha3.h" +#endif // Set cp2 = cp1 void point_copy(const curve_point *cp1, curve_point *cp2) @@ -850,6 +853,27 @@ void ecdsa_get_pubkeyhash(const uint8_t *pub_key, uint8_t *pubkeyhash) MEMSET_BZERO(h, sizeof(h)); } +#if USE_ETHEREUM +int ecdsa_get_ethereum_pubkeyhash(const uint8_t *pub_key, uint8_t *pubkeyhash) +{ + uint8_t h[65]; + SHA3_CTX ctx; + + if (!ecdsa_uncompress_pubkey(&secp256k1, pub_key, h)) { + return 0; + } + + sha3_256_Init(&ctx); + sha3_Update(&ctx, h + 1, 64); + keccak_Final(&ctx, h); + + // least significant 160 bits + memcpy(pubkeyhash, h + 12, 20); + + return 1; +} +#endif + void ecdsa_get_address_raw(const uint8_t *pub_key, uint8_t version, uint8_t *addr_raw) { addr_raw[0] = version; diff --git a/ecdsa.h b/ecdsa.h index b7c22aa4d8..2950a314f7 100644 --- a/ecdsa.h +++ b/ecdsa.h @@ -66,6 +66,7 @@ int ecdsa_sign_digest(const ecdsa_curve *curve, const uint8_t *priv_key, const u void ecdsa_get_public_key33(const ecdsa_curve *curve, const uint8_t *priv_key, uint8_t *pub_key); void ecdsa_get_public_key65(const ecdsa_curve *curve, const uint8_t *priv_key, uint8_t *pub_key); void ecdsa_get_pubkeyhash(const uint8_t *pub_key, uint8_t *pubkeyhash); +int ecdsa_get_ethereum_pubkeyhash(const uint8_t *pub_key, uint8_t *pubkeyhash); void ecdsa_get_address_raw(const uint8_t *pub_key, uint8_t version, uint8_t *addr_raw); void ecdsa_get_address(const uint8_t *pub_key, uint8_t version, char *addr, int addrsize); void ecdsa_get_wif(const uint8_t *priv_key, uint8_t version, char *wif, int wifsize); diff --git a/options.h b/options.h index 4baadf0bd5..fabca9f9b3 100644 --- a/options.h +++ b/options.h @@ -56,9 +56,14 @@ #define BIP39_CACHE_SIZE 4 #endif +// support Ethereum operations +#ifndef USE_ETHEREUM +#define USE_ETHEREUM 0 +#endif + // support Keccak hashing #ifndef USE_KECCAK -#define USE_KECCAK 0 +#define USE_KECCAK USE_ETHEREUM #endif #endif From aecf8671a126adfd3065199f2ba27339f8b756db Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 May 2016 18:42:54 +0100 Subject: [PATCH 4/7] Add sha3 to cmake --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 165d7aeb01..2f256bb4ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 2.6) -set(SOURCES aescrypt.c aeskey.c aes_modes.c aestab.c base58.c bignum.c bip32.c bip39.c ecdsa.c hmac.c nist256p1.c pbkdf2.c rand.c ripemd160.c secp256k1.c sha2.c ed25519-donna/ed25519.c) +set(SOURCES aescrypt.c aeskey.c aes_modes.c aestab.c base58.c bignum.c bip32.c bip39.c ecdsa.c hmac.c nist256p1.c pbkdf2.c rand.c ripemd160.c secp256k1.c sha2.c ed25519-donna/ed25519.c sha3.c) include_directories(ed25519-donna) # disable sequence point warnings where they are expected From 4e7da75c6e3bfd071edb0feef22db9958c936da0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 17 May 2016 19:37:29 +0100 Subject: [PATCH 5/7] Rewrite ecdsa_uncompress_pubkey() using ecdsa_read_pubkey() --- ecdsa.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/ecdsa.c b/ecdsa.c index 3cb52ce44d..cbb114b8a1 100644 --- a/ecdsa.c +++ b/ecdsa.c @@ -820,23 +820,17 @@ void ecdsa_get_public_key65(const ecdsa_curve *curve, const uint8_t *priv_key, u int ecdsa_uncompress_pubkey(const ecdsa_curve *curve, const uint8_t *pub_key, uint8_t *uncompressed) { - if (pub_key[0] == 2 || pub_key[0] == 3) { - bignum256 x, y; + curve_point pub; - bn_read_be(pub_key + 1, &x); - // uncompress_coords will check for pub_key[0] & 1 - uncompress_coords(curve, pub_key[0], &x, &y); - - uncompressed[0] = 4; - memcpy(uncompressed + 1, pub_key + 1, 32); - bn_write_be(&y, uncompressed + 33); - return 1; - } else if (pub_key[0] == 4) { - memcpy(uncompressed, pub_key, 65); - return 1; + if (!ecdsa_read_pubkey(curve, pub_key, &pub)) { + return 0; } - return 0; + uncompressed[0] = 4; + bn_write_be(&pub.x, uncompressed + 1); + bn_write_be(&pub.y, uncompressed + 33); + + return 1; } void ecdsa_get_pubkeyhash(const uint8_t *pub_key, uint8_t *pubkeyhash) From ca2fcbf3e3ad13186607f42662d7b980400d5b30 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 13 Jun 2016 21:59:52 +0100 Subject: [PATCH 6/7] Add tests for ecdsa_uncompress_pubkey() --- tests.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests.c b/tests.c index cd931e165b..99ae3d4e79 100644 --- a/tests.c +++ b/tests.c @@ -1507,6 +1507,34 @@ START_TEST(test_pubkey_validity) } END_TEST +START_TEST(test_pubkey_uncompress) +{ + uint8_t pub_key[65]; + uint8_t uncompressed[65]; + int res; + const ecdsa_curve *curve = &secp256k1; + + memcpy(pub_key, fromhex("0226659c1cf7321c178c07437150639ff0c5b7679c7ea195253ed9abda2e081a37"), 33); + res = ecdsa_uncompress_pubkey(curve, pub_key, uncompressed); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(uncompressed, fromhex("0426659c1cf7321c178c07437150639ff0c5b7679c7ea195253ed9abda2e081a37b3cfbad6b39a8ce8cb3a675f53b7b57e120fe067b8035d771fd99e3eba7cf4de"), 65); + + memcpy(pub_key, fromhex("03433f246a12e6486a51ff08802228c61cf895175a9b49ed4766ea9a9294a3c7fe"), 33); + res = ecdsa_uncompress_pubkey(curve, pub_key, uncompressed); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(uncompressed, fromhex("04433f246a12e6486a51ff08802228c61cf895175a9b49ed4766ea9a9294a3c7feeb4c25bcb840f720a16e8857a011e6b91e0ab2d03dbb5f9762844bb21a7b8ca7"), 65); + + memcpy(pub_key, fromhex("0496e8f2093f018aff6c2e2da5201ee528e2c8accbf9cac51563d33a7bb74a016054201c025e2a5d96b1629b95194e806c63eb96facaedc733b1a4b70ab3b33e3a"), 65); + res = ecdsa_uncompress_pubkey(curve, pub_key, uncompressed); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(uncompressed, fromhex("0496e8f2093f018aff6c2e2da5201ee528e2c8accbf9cac51563d33a7bb74a016054201c025e2a5d96b1629b95194e806c63eb96facaedc733b1a4b70ab3b33e3a"), 65); + + memcpy(pub_key, fromhex("00"), 1); + res = ecdsa_uncompress_pubkey(curve, pub_key, uncompressed); + ck_assert_int_eq(res, 0); +} +END_TEST + START_TEST(test_wif) { uint8_t priv_key[32]; @@ -1974,6 +2002,10 @@ Suite *test_suite(void) tcase_add_test(tc, test_pubkey_validity); suite_add_tcase(s, tc); + tc = tcase_create("pubkey_uncompress"); + tcase_add_test(tc, test_pubkey_uncompress); + suite_add_tcase(s, tc); + tc = tcase_create("codepoints"); tcase_add_test(tc, test_codepoints); suite_add_tcase(s, tc); From ec7bea43084d7d966beb6385ce1b9ae0e1e95bcb Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 13 Jun 2016 22:13:37 +0100 Subject: [PATCH 7/7] Add tests for ecdsa_get_ethereum_pubkeyhash() --- Makefile | 1 + tests.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/Makefile b/Makefile index 81ba177853..9c91c97947 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ CFLAGS += -DED25519_CUSTOMHASH=1 CFLAGS += -DED25519_NO_INLINE_ASM CFLAGS += -DED25519_FORCE_32BIT=1 CFLAGS += -Ied25519-donna -I. +CFLAGS += -DUSE_ETHEREUM=1 # disable certain optimizations and features when small footprint is required ifdef SMALL diff --git a/tests.c b/tests.c index 99ae3d4e79..051f52b9b6 100644 --- a/tests.c +++ b/tests.c @@ -1923,6 +1923,53 @@ START_TEST(test_output_script) { } END_TEST +START_TEST(test_ethereum_pubkeyhash) +{ + uint8_t pubkeyhash[20]; + uint8_t pub_key[65]; + int res; + + memcpy(pub_key, fromhex("0226659c1cf7321c178c07437150639ff0c5b7679c7ea195253ed9abda2e081a37"), 33); + res = ecdsa_get_ethereum_pubkeyhash(pub_key, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, fromhex("dfec07843c46f3fb5e5ef8b70b845231a97ed2c8"), 20); + + memcpy(pub_key, fromhex("025b1654a0e78d28810094f6c5a96b8efb8a65668b578f170ac2b1f83bc63ba856"), 33); + res = ecdsa_get_ethereum_pubkeyhash(pub_key, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, fromhex("aa2685dbc0a1820fc4bb03c3154d1cc0b26411ee"), 20); + + memcpy(pub_key, fromhex("03433f246a12e6486a51ff08802228c61cf895175a9b49ed4766ea9a9294a3c7fe"), 33); + res = ecdsa_get_ethereum_pubkeyhash(pub_key, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, fromhex("86193afd976244f39f0e9d42a1d2c090080754f1"), 20); + + memcpy(pub_key, fromhex("03aeb03abeee0f0f8b4f7a5d65ce31f9570cef9f72c2dd8a19b4085a30ab033d48"), 33); + res = ecdsa_get_ethereum_pubkeyhash(pub_key, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, fromhex("330b7636bff2c8ea728170042ab6af0b826cbdc0"), 20); + + memcpy(pub_key, fromhex("0496e8f2093f018aff6c2e2da5201ee528e2c8accbf9cac51563d33a7bb74a016054201c025e2a5d96b1629b95194e806c63eb96facaedc733b1a4b70ab3b33e3a"), 65); + res = ecdsa_get_ethereum_pubkeyhash(pub_key, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, fromhex("10027a9bef3e98be693f8fb4c02f4a7421cdf384"), 20); + + memcpy(pub_key, fromhex("0498010f8a687439ff497d3074beb4519754e72c4b6220fb669224749591dde416f3961f8ece18f8689bb32235e436874d2174048b86118a00afbd5a4f33a24f0f"), 65); + res = ecdsa_get_ethereum_pubkeyhash(pub_key, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, fromhex("6d9baaf3db174b9ea498081707289c228108aa9e"), 20); + + memcpy(pub_key, fromhex("04f80490839af36d13701ec3f9eebdac901b51c362119d74553a3c537faff31b17e2a59ebddbdac9e87b816307a7ed5b826b8f40b92719086238e1bebf19b77a4d"), 65); + res = ecdsa_get_ethereum_pubkeyhash(pub_key, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, fromhex("addcb46a5ae157c837682689fcd4f80a76bb7740"), 20); + + memcpy(pub_key, fromhex("00"), 1); + res = ecdsa_get_ethereum_pubkeyhash(pub_key, pubkeyhash); + ck_assert_int_eq(res, 0); +} +END_TEST + // define test suite and cases Suite *test_suite(void) @@ -2034,6 +2081,10 @@ Suite *test_suite(void) tcase_add_test(tc, test_output_script); suite_add_tcase(s, tc); + tc = tcase_create("ethereum_pubkeyhash"); + tcase_add_test(tc, test_ethereum_pubkeyhash); + suite_add_tcase(s, tc); + return s; }