From c983afd72f40a8c65355af076afd9c132878e8a5 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Tue, 19 Apr 2016 18:21:56 +0200 Subject: [PATCH 1/5] Added curve type to HDNode Every curve gets it's own hierarchy and the curve is remembered in HD node. Fixed the private/public key derivations to use the right modulus. --- bip32.c | 36 +++++++++++++++++++----------------- bip32.h | 7 ++++--- nist256p1.c | 1 + nist256p1.h | 1 + secp256k1.c | 2 ++ secp256k1.h | 1 + 6 files changed, 28 insertions(+), 20 deletions(-) diff --git a/bip32.c b/bip32.c index 3aa6d27df5..18c96252fb 100644 --- a/bip32.c +++ b/bip32.c @@ -34,9 +34,7 @@ #include "macros.h" #include "secp256k1.h" -static const ecdsa_curve *default_curve = &secp256k1; - -int hdnode_from_xpub(uint32_t depth, uint32_t fingerprint, uint32_t child_num, const uint8_t *chain_code, const uint8_t *public_key, HDNode *out) +int hdnode_from_xpub(uint32_t depth, uint32_t fingerprint, uint32_t child_num, const uint8_t *chain_code, const uint8_t *public_key, const char* curve, HDNode *out) { if (public_key[0] != 0x02 && public_key[0] != 0x03) { // invalid pubkey return 0; @@ -47,19 +45,21 @@ int hdnode_from_xpub(uint32_t depth, uint32_t fingerprint, uint32_t child_num, c memcpy(out->chain_code, chain_code, 32); MEMSET_BZERO(out->private_key, 32); memcpy(out->public_key, public_key, 33); + out->curve = get_curve_by_name(curve); return 1; } -int hdnode_from_xprv(uint32_t depth, uint32_t fingerprint, uint32_t child_num, const uint8_t *chain_code, const uint8_t *private_key, HDNode *out) +int hdnode_from_xprv(uint32_t depth, uint32_t fingerprint, uint32_t child_num, const uint8_t *chain_code, const uint8_t *private_key, const char* curve, HDNode *out) { bignum256 a; bn_read_be(private_key, &a); + out->curve = get_curve_by_name(curve); bool failed = false; if (bn_is_zero(&a)) { // == 0 failed = true; } else { - if (!bn_is_less(&a, &default_curve->order)) { // >= order + if (!bn_is_less(&a, &out->curve->order)) { // >= order failed = true; } MEMSET_BZERO(&a, sizeof(a)); @@ -78,13 +78,14 @@ int hdnode_from_xprv(uint32_t depth, uint32_t fingerprint, uint32_t child_num, c return 1; } -int hdnode_from_seed(const uint8_t *seed, int seed_len, HDNode *out) +int hdnode_from_seed(const uint8_t *seed, int seed_len, const char* curve, HDNode *out) { uint8_t I[32 + 32]; memset(out, 0, sizeof(HDNode)); out->depth = 0; out->fingerprint = 0x00000000; out->child_num = 0; + out->curve = get_curve_by_name(curve); hmac_sha512((uint8_t *)"Bitcoin seed", 12, seed, seed_len, I); memcpy(out->private_key, I, 32); bignum256 a; @@ -94,7 +95,7 @@ int hdnode_from_seed(const uint8_t *seed, int seed_len, HDNode *out) if (bn_is_zero(&a)) { // == 0 failed = true; } else { - if (!bn_is_less(&a, &default_curve->order)) { // >= order + if (!bn_is_less(&a, &out->curve->order)) { // >= order failed = true; } MEMSET_BZERO(&a, sizeof(a)); @@ -138,12 +139,12 @@ int hdnode_private_ckd(HDNode *inout, uint32_t i) bool failed = false; - if (!bn_is_less(&b, &default_curve->order)) { // >= order + if (!bn_is_less(&b, &inout->curve->order)) { // >= order failed = true; } if (!failed) { - bn_addmod(&a, &b, &default_curve->order); - bn_mod(&a, &default_curve->order); + bn_addmod(&a, &b, &inout->curve->order); + bn_mod(&a, &inout->curve->order); if (bn_is_zero(&a)) { failed = true; } @@ -186,7 +187,7 @@ int hdnode_public_ckd(HDNode *inout, uint32_t i) memset(inout->private_key, 0, 32); bool failed = false; - if (!ecdsa_read_pubkey(default_curve, inout->public_key, &a)) { + if (!ecdsa_read_pubkey(inout->curve, inout->public_key, &a)) { failed = true; } @@ -194,15 +195,15 @@ int hdnode_public_ckd(HDNode *inout, uint32_t i) hmac_sha512(inout->chain_code, 32, data, sizeof(data), I); memcpy(inout->chain_code, I + 32, 32); bn_read_be(I, &c); - if (!bn_is_less(&c, &default_curve->order)) { // >= order + if (!bn_is_less(&c, &inout->curve->order)) { // >= order failed = true; } } if (!failed) { - scalar_multiply(default_curve, &c, &b); // b = c * G - point_add(default_curve, &a, &b); // b = a + b - if (!ecdsa_validate_pubkey(default_curve, &b)) { + scalar_multiply(inout->curve, &c, &b); // b = c * G + point_add(inout->curve, &a, &b); // b = a + b + if (!ecdsa_validate_pubkey(inout->curve, &b)) { failed = true; } } @@ -263,7 +264,8 @@ int hdnode_private_ckd_cached(HDNode *inout, const uint32_t *i, size_t i_count) for (j = 0; j < BIP32_CACHE_SIZE; j++) { if (private_ckd_cache[j].set && private_ckd_cache[j].depth == i_count - 1 && - memcmp(private_ckd_cache[j].i, i, (i_count - 1) * sizeof(uint32_t)) == 0) { + memcmp(private_ckd_cache[j].i, i, (i_count - 1) * sizeof(uint32_t)) == 0 && + private_ckd_cache[j].node.curve == inout->curve) { memcpy(inout, &(private_ckd_cache[j].node), sizeof(HDNode)); found = true; break; @@ -295,7 +297,7 @@ int hdnode_private_ckd_cached(HDNode *inout, const uint32_t *i, size_t i_count) void hdnode_fill_public_key(HDNode *node) { - ecdsa_get_public_key33(default_curve, node->private_key, node->public_key); + ecdsa_get_public_key33(node->curve, node->private_key, node->public_key); } void hdnode_serialize(const HDNode *node, uint32_t version, char use_public, char *str, int strsize) diff --git a/bip32.h b/bip32.h index 765084fd0c..2d2e46b6f1 100644 --- a/bip32.h +++ b/bip32.h @@ -36,13 +36,14 @@ typedef struct { uint8_t chain_code[32]; uint8_t private_key[32]; uint8_t public_key[33]; + const ecdsa_curve *curve; } HDNode; -int hdnode_from_xpub(uint32_t depth, uint32_t fingerprint, uint32_t child_num, const uint8_t *chain_code, const uint8_t *public_key, HDNode *out); +int hdnode_from_xpub(uint32_t depth, uint32_t fingerprint, uint32_t child_num, const uint8_t *chain_code, const uint8_t *public_key, const char *curve, HDNode *out); -int hdnode_from_xprv(uint32_t depth, uint32_t fingerprint, uint32_t child_num, const uint8_t *chain_code, const uint8_t *private_key, HDNode *out); +int hdnode_from_xprv(uint32_t depth, uint32_t fingerprint, uint32_t child_num, const uint8_t *chain_code, const uint8_t *private_key, const char *curve, HDNode *out); -int hdnode_from_seed(const uint8_t *seed, int seed_len, HDNode *out); +int hdnode_from_seed(const uint8_t *seed, int seed_len, const char *curve, HDNode *out); #define hdnode_private_ckd_prime(X, I) hdnode_private_ckd((X), ((I) | 0x80000000)) diff --git a/nist256p1.c b/nist256p1.c index 28d5714b5f..deb457737b 100644 --- a/nist256p1.c +++ b/nist256p1.c @@ -23,6 +23,7 @@ #include "nist256p1.h" +const char NIST256P1_NAME[] = "nist256p1"; const ecdsa_curve nist256p1 = { /* .prime */ { /*.val =*/ {0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3f, 0x0, 0x0, 0x1000, 0x3fffc000, 0xffff} diff --git a/nist256p1.h b/nist256p1.h index 6afd910051..22bd895b8a 100644 --- a/nist256p1.h +++ b/nist256p1.h @@ -28,6 +28,7 @@ #include "ecdsa.h" +extern const char NIST256P1_NAME[]; extern const ecdsa_curve nist256p1; #endif diff --git a/secp256k1.c b/secp256k1.c index b2711e6506..b157bae6a4 100644 --- a/secp256k1.c +++ b/secp256k1.c @@ -23,6 +23,8 @@ #include "secp256k1.h" +const char SECP256K1_NAME[] = "secp256k1"; + const ecdsa_curve secp256k1 = { /* .prime */ { /*.val =*/ {0x3ffffc2f, 0x3ffffffb, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0x3fffffff, 0xffff} diff --git a/secp256k1.h b/secp256k1.h index 349ca24353..c07a2823e3 100644 --- a/secp256k1.h +++ b/secp256k1.h @@ -28,6 +28,7 @@ #include "ecdsa.h" +extern const char SECP256K1_NAME[]; extern const ecdsa_curve secp256k1; #endif From 0bc1b70c4aa32b730da41ca16e3b11f570e49ffc Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 20 Apr 2016 10:30:33 +0200 Subject: [PATCH 2/5] Use different seed modifier for different curves --- bip32.c | 4 +++- ecdsa.c | 4 ++-- ecdsa.h | 1 + nist256p1.c | 6 +++++- secp256k1.c | 6 +++++- 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/bip32.c b/bip32.c index 18c96252fb..0fdc28e425 100644 --- a/bip32.c +++ b/bip32.c @@ -86,7 +86,8 @@ int hdnode_from_seed(const uint8_t *seed, int seed_len, const char* curve, HDNod out->fingerprint = 0x00000000; out->child_num = 0; out->curve = get_curve_by_name(curve); - hmac_sha512((uint8_t *)"Bitcoin seed", 12, seed, seed_len, I); + hmac_sha512((const uint8_t*) out->curve->bip32_name, + strlen(out->curve->bip32_name), seed, seed_len, I); memcpy(out->private_key, I, 32); bignum256 a; bn_read_be(out->private_key, &a); @@ -337,6 +338,7 @@ int hdnode_deserialize(const char *str, HDNode *node) if (!base58_decode_check(str, node_data, sizeof(node_data))) { return -1; } + node->curve = get_curve_by_name(SECP256K1_NAME); uint32_t version = read_be(node_data); if (version == 0x0488B21E) { // public node memcpy(node->public_key, node_data + 45, 33); diff --git a/ecdsa.c b/ecdsa.c index 8d4e8df3ef..4b8a36bdbc 100644 --- a/ecdsa.c +++ b/ecdsa.c @@ -1050,10 +1050,10 @@ const ecdsa_curve *get_curve_by_name(const char *curve_name) { if (curve_name == 0) { return 0; } - if (strcmp(curve_name, "secp256k1") == 0) { + if (strcmp(curve_name, SECP256K1_NAME) == 0) { return &secp256k1; } - if (strcmp(curve_name, "nist256p1") == 0) { + if (strcmp(curve_name, NIST256P1_NAME) == 0) { return &nist256p1; } return 0; diff --git a/ecdsa.h b/ecdsa.h index 4bdc041151..85b6cd058d 100644 --- a/ecdsa.h +++ b/ecdsa.h @@ -41,6 +41,7 @@ typedef struct { bignum256 order_half; // order of G divided by 2 int a; // coefficient 'a' of the elliptic curve bignum256 b; // coefficient 'b' of the elliptic curve + const char *bip32_name;// string used for generating BIP32 xprv from seed #if USE_PRECOMPUTED_CP const curve_point cp[64][8]; diff --git a/nist256p1.c b/nist256p1.c index deb457737b..5ec461776c 100644 --- a/nist256p1.c +++ b/nist256p1.c @@ -46,7 +46,11 @@ const ecdsa_curve nist256p1 = { /* b */ { /*.val =*/{0x27d2604b, 0x2f38f0f8, 0x53b0f63, 0x741ac33, 0x1886bc65, 0x2ef555da, 0x293e7b3e, 0xd762a8e, 0x5ac6} - } + }, + + /* bip32_name */ + "Nist256p1 seed" + #if USE_PRECOMPUTED_CP , /* cp */ { diff --git a/secp256k1.c b/secp256k1.c index b157bae6a4..8048376976 100644 --- a/secp256k1.c +++ b/secp256k1.c @@ -47,7 +47,11 @@ const ecdsa_curve secp256k1 = { /* b */ { /*.val =*/{7} - } + }, + + /* bip32_name */ + "Bitcoin seed" + #if USE_PRECOMPUTED_CP , /* cp */ { From b34be66a9c9be623630ce55ba0773b2edba4ca77 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 20 Apr 2016 10:43:53 +0200 Subject: [PATCH 3/5] Updated tests --- tests.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests.c b/tests.c index 8c34b48e5c..8b89b94934 100644 --- a/tests.c +++ b/tests.c @@ -168,7 +168,7 @@ START_TEST(test_bip32_vector_1) int r; // init m - hdnode_from_seed(fromhex("000102030405060708090a0b0c0d0e0f"), 16, &node); + hdnode_from_seed(fromhex("000102030405060708090a0b0c0d0e0f"), 16, SECP256K1_NAME, &node); // [Chain m] ck_assert_int_eq(node.fingerprint, 0x00000000); @@ -281,7 +281,7 @@ START_TEST(test_bip32_vector_2) int r; // init m - hdnode_from_seed(fromhex("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), 64, &node); + hdnode_from_seed(fromhex("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), 64, SECP256K1_NAME, &node); // [Chain m] ck_assert_int_eq(node.fingerprint, 0x00000000); @@ -390,7 +390,7 @@ START_TEST(test_bip32_vector_2) ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); // init m - hdnode_from_seed(fromhex("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), 64, &node); + hdnode_from_seed(fromhex("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), 64, SECP256K1_NAME, &node); // test public derivation // [Chain m/0] @@ -407,8 +407,8 @@ START_TEST(test_bip32_compare) { HDNode node1, node2, node3; int i, r; - hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, &node1); - hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, &node2); + hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, SECP256K1_NAME, &node1); + hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, SECP256K1_NAME, &node2); for (i = 0; i < 100; i++) { memcpy(&node3, &node1, sizeof(HDNode)); r = hdnode_private_ckd(&node1, i); ck_assert_int_eq(r, 1); @@ -436,8 +436,8 @@ START_TEST(test_bip32_cache_1) int i, r; // test 1 .. 8 - hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, &node1); - hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, &node2); + hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, SECP256K1_NAME, &node1); + hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, SECP256K1_NAME, &node2); uint32_t ii[] = {0x80000001, 0x80000002, 0x80000003, 0x80000004, 0x80000005, 0x80000006, 0x80000007, 0x80000008}; @@ -447,8 +447,8 @@ START_TEST(test_bip32_cache_1) r = hdnode_private_ckd_cached(&node2, ii, 8); ck_assert_int_eq(r, 1); ck_assert_mem_eq(&node1, &node2, sizeof(HDNode)); - hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, &node1); - hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, &node2); + hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, SECP256K1_NAME, &node1); + hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, SECP256K1_NAME, &node2); // test 1 .. 7, 20 ii[7] = 20; @@ -459,8 +459,8 @@ START_TEST(test_bip32_cache_1) ck_assert_mem_eq(&node1, &node2, sizeof(HDNode)); // test different root node - hdnode_from_seed(fromhex("000000002ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, &node1); - hdnode_from_seed(fromhex("000000002ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, &node2); + hdnode_from_seed(fromhex("000000002ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, SECP256K1_NAME, &node1); + hdnode_from_seed(fromhex("000000002ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, SECP256K1_NAME, &node2); for (i = 0; i < 8; i++) { r = hdnode_private_ckd(&node1, ii[i]); ck_assert_int_eq(r, 1); @@ -476,8 +476,8 @@ START_TEST(test_bip32_cache_2) int i, j, r; for (j = 0; j < 9; j++) { - hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, &(nodea[j])); - hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, &(nodeb[j])); + hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, SECP256K1_NAME, &(nodea[j])); + hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, SECP256K1_NAME, &(nodeb[j])); } uint32_t ii[] = {0x80000001, 0x80000002, 0x80000003, 0x80000004, 0x80000005, 0x80000006, 0x80000007, 0x80000008}; From 533c3beb63af0daf1856627dc0e800eb93d028fd Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 20 Apr 2016 15:09:11 +0200 Subject: [PATCH 4/5] Fixed uncompress_coords for NIST curve The bn_sqrti was broken. It didn't handle primes where all bits are set in the lowest limb. --- bignum.c | 2 +- ecdsa.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bignum.c b/bignum.c index 05a867ab55..6de4a3c229 100644 --- a/bignum.c +++ b/bignum.c @@ -371,7 +371,7 @@ void bn_sqrt(bignum256 *x, const bignum256 *prime) bn_zero(&res); res.val[0] = 1; // compute p = (prime+1)/4 memcpy(&p, prime, sizeof(bignum256)); - p.val[0] += 1; + bn_addi(&p, 1); bn_rshift(&p); bn_rshift(&p); for (i = 0; i < 9; i++) { diff --git a/ecdsa.c b/ecdsa.c index 4b8a36bdbc..14637da151 100644 --- a/ecdsa.c +++ b/ecdsa.c @@ -862,7 +862,7 @@ int ecdsa_address_decode(const char *addr, uint8_t *out) void uncompress_coords(const ecdsa_curve *curve, uint8_t odd, const bignum256 *x, bignum256 *y) { - // y^2 = x^3 + 0*x + 7 + // y^2 = x^3 + a*x + b memcpy(y, x, sizeof(bignum256)); // y is x bn_multiply(x, y, &curve->prime); // y is x^2 bn_subi(y, -curve->a, &curve->prime); // y is x^2 + a From d577410fc4a87262c107fb25657158f8f8ba720e Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 20 Apr 2016 15:13:40 +0200 Subject: [PATCH 5/5] Unit tests for the NIST256P1 curve --- tests.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/tests.c b/tests.c index 8b89b94934..1e69e824e3 100644 --- a/tests.c +++ b/tests.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "aes.h" @@ -502,7 +503,152 @@ START_TEST(test_bip32_cache_2) } END_TEST -#define test_deterministic(KEY, MSG, K) do { \ +START_TEST(test_bip32_nist_vector_1) +{ + HDNode node; + + // init m + hdnode_from_seed(fromhex("000102030405060708090a0b0c0d0e0f"), 16, NIST256P1_NAME, &node); + + // [Chain m] + ck_assert_int_eq(node.fingerprint, 0x00000000); + ck_assert_mem_eq(node.chain_code, fromhex("beeb672fe4621673f722f38529c07392fecaa61015c80c34f29ce8b41b3cb6ea"), 32); + ck_assert_mem_eq(node.private_key, fromhex("612091aaa12e22dd2abef664f8a01a82cae99ad7441b7ef8110424915c268bc2"), 32); + ck_assert_mem_eq(node.public_key, fromhex("0266874dc6ade47b3ecd096745ca09bcd29638dd52c2c12117b11ed3e458cfa9e8"), 33); + + // [Chain m/0'] + hdnode_private_ckd_prime(&node, 0); + ck_assert_int_eq(node.fingerprint, 0xbe6105b5); + ck_assert_mem_eq(node.chain_code, fromhex("3460cea53e6a6bb5fb391eeef3237ffd8724bf0a40e94943c98b83825342ee11"), 32); + ck_assert_mem_eq(node.private_key, fromhex("6939694369114c67917a182c59ddb8cafc3004e63ca5d3b84403ba8613debc0c"), 32); + ck_assert_mem_eq(node.public_key, fromhex("0384610f5ecffe8fda089363a41f56a5c7ffc1d81b59a612d0d649b2d22355590c"), 33); + + // [Chain m/0'/1] + hdnode_private_ckd(&node, 1); + ck_assert_int_eq(node.fingerprint, 0x9b02312f); + ck_assert_mem_eq(node.chain_code, fromhex("4187afff1aafa8445010097fb99d23aee9f599450c7bd140b6826ac22ba21d0c"), 32); + ck_assert_mem_eq(node.private_key, fromhex("284e9d38d07d21e4e281b645089a94f4cf5a5a81369acf151a1c3a57f18b2129"), 32); + ck_assert_mem_eq(node.public_key, fromhex("03526c63f8d0b4bbbf9c80df553fe66742df4676b241dabefdef67733e070f6844"), 33); + + // [Chain m/0'/1/2'] + hdnode_private_ckd_prime(&node, 2); + ck_assert_int_eq(node.fingerprint, 0xb98005c1); + ck_assert_mem_eq(node.chain_code, fromhex("98c7514f562e64e74170cc3cf304ee1ce54d6b6da4f880f313e8204c2a185318"), 32); + ck_assert_mem_eq(node.private_key, fromhex("694596e8a54f252c960eb771a3c41e7e32496d03b954aeb90f61635b8e092aa7"), 32); + ck_assert_mem_eq(node.public_key, fromhex("0359cf160040778a4b14c5f4d7b76e327ccc8c4a6086dd9451b7482b5a4972dda0"), 33); + + // [Chain m/0'/1/2'/2] + hdnode_private_ckd(&node, 2); + ck_assert_int_eq(node.fingerprint, 0x0e9f3274); + ck_assert_mem_eq(node.chain_code, fromhex("ba96f776a5c3907d7fd48bde5620ee374d4acfd540378476019eab70790c63a0"), 32); + ck_assert_mem_eq(node.private_key, fromhex("5996c37fd3dd2679039b23ed6f70b506c6b56b3cb5e424681fb0fa64caf82aaa"), 32); + ck_assert_mem_eq(node.public_key, fromhex("029f871f4cb9e1c97f9f4de9ccd0d4a2f2a171110c61178f84430062230833ff20"), 33); + + // [Chain m/0'/1/2'/2/1000000000] + hdnode_private_ckd(&node, 1000000000); + ck_assert_int_eq(node.fingerprint, 0x8b2b5c4b); + ck_assert_mem_eq(node.chain_code, fromhex("b9b7b82d326bb9cb5b5b121066feea4eb93d5241103c9e7a18aad40f1dde8059"), 32); + ck_assert_mem_eq(node.private_key, fromhex("21c4f269ef0a5fd1badf47eeacebeeaa3de22eb8e5b0adcd0f27dd99d34d0119"), 32); + ck_assert_mem_eq(node.public_key, fromhex("02216cd26d31147f72427a453c443ed2cde8a1e53c9cc44e5ddf739725413fe3f4"), 33); +} +END_TEST + +START_TEST(test_bip32_nist_vector_2) +{ + HDNode node; + int r; + + // init m + hdnode_from_seed(fromhex("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), 64, NIST256P1_NAME, &node); + + // [Chain m] + ck_assert_int_eq(node.fingerprint, 0x00000000); + ck_assert_mem_eq(node.chain_code, fromhex("96cd4465a9644e31528eda3592aa35eb39a9527769ce1855beafc1b81055e75d"), 32); + ck_assert_mem_eq(node.private_key, fromhex("eaa31c2e46ca2962227cf21d73a7ef0ce8b31c756897521eb6c7b39796633357"), 32); + ck_assert_mem_eq(node.public_key, fromhex("02c9e16154474b3ed5b38218bb0463e008f89ee03e62d22fdcc8014beab25b48fa"), 33); + + // [Chain m/0] + r = hdnode_private_ckd(&node, 0); + ck_assert_int_eq(r, 1); + ck_assert_int_eq(node.fingerprint, 0x607f628f); + ck_assert_mem_eq(node.chain_code, fromhex("84e9c258bb8557a40e0d041115b376dd55eda99c0042ce29e81ebe4efed9b86a"), 32); + ck_assert_mem_eq(node.private_key, fromhex("d7d065f63a62624888500cdb4f88b6d59c2927fee9e6d0cdff9cad555884df6e"), 32); + ck_assert_mem_eq(node.public_key, fromhex("039b6df4bece7b6c81e2adfeea4bcf5c8c8a6e40ea7ffa3cf6e8494c61a1fc82cc"), 33); + + // [Chain m/0/2147483647'] + r = hdnode_private_ckd_prime(&node, 2147483647); + ck_assert_int_eq(r, 1); + ck_assert_int_eq(node.fingerprint, 0x946d2a54); + ck_assert_mem_eq(node.chain_code, fromhex("f235b2bc5c04606ca9c30027a84f353acf4e4683edbd11f635d0dcc1cd106ea6"), 32); + ck_assert_mem_eq(node.private_key, fromhex("96d2ec9316746a75e7793684ed01e3d51194d81a42a3276858a5b7376d4b94b9"), 32); + ck_assert_mem_eq(node.public_key, fromhex("02f89c5deb1cae4fedc9905f98ae6cbf6cbab120d8cb85d5bd9a91a72f4c068c76"), 33); + + // [Chain m/0/2147483647'/1] + r = hdnode_private_ckd(&node, 1); + ck_assert_int_eq(r, 1); + ck_assert_int_eq(node.fingerprint, 0x218182d8); + ck_assert_mem_eq(node.chain_code, fromhex("7c0b833106235e452eba79d2bdd58d4086e663bc8cc55e9773d2b5eeda313f3b"), 32); + ck_assert_mem_eq(node.private_key, fromhex("974f9096ea6873a915910e82b29d7c338542ccde39d2064d1cc228f371542bbc"), 32); + ck_assert_mem_eq(node.public_key, fromhex("03abe0ad54c97c1d654c1852dfdc32d6d3e487e75fa16f0fd6304b9ceae4220c64"), 33); + + // [Chain m/0/2147483647'/1/2147483646'] + r = hdnode_private_ckd_prime(&node, 2147483646); + ck_assert_int_eq(r, 1); + ck_assert_int_eq(node.fingerprint, 0x931223e4); + ck_assert_mem_eq(node.chain_code, fromhex("5794e616eadaf33413aa309318a26ee0fd5163b70466de7a4512fd4b1a5c9e6a"), 32); + ck_assert_mem_eq(node.private_key, fromhex("da29649bbfaff095cd43819eda9a7be74236539a29094cd8336b07ed8d4eff63"), 32); + ck_assert_mem_eq(node.public_key, fromhex("03cb8cb067d248691808cd6b5a5a06b48e34ebac4d965cba33e6dc46fe13d9b933"), 33); + + // [Chain m/0/2147483647'/1/2147483646'/2] + r = hdnode_private_ckd(&node, 2); + ck_assert_int_eq(r, 1); + ck_assert_int_eq(node.fingerprint, 0x956c4629); + ck_assert_mem_eq(node.chain_code, fromhex("3bfb29ee8ac4484f09db09c2079b520ea5616df7820f071a20320366fbe226a7"), 32); + ck_assert_mem_eq(node.private_key, fromhex("bb0a77ba01cc31d77205d51d08bd313b979a71ef4de9b062f8958297e746bd67"), 32); + ck_assert_mem_eq(node.public_key, fromhex("020ee02e18967237cf62672983b253ee62fa4dd431f8243bfeccdf39dbe181387f"), 33); + + // init m + hdnode_from_seed(fromhex("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), 64, NIST256P1_NAME, &node); + + // test public derivation + // [Chain m/0] + r = hdnode_public_ckd(&node, 0); + ck_assert_int_eq(r, 1); + ck_assert_int_eq(node.fingerprint, 0x607f628f); + ck_assert_mem_eq(node.chain_code, fromhex("84e9c258bb8557a40e0d041115b376dd55eda99c0042ce29e81ebe4efed9b86a"), 32); + ck_assert_mem_eq(node.private_key, fromhex("0000000000000000000000000000000000000000000000000000000000000000"), 32); + ck_assert_mem_eq(node.public_key, fromhex("039b6df4bece7b6c81e2adfeea4bcf5c8c8a6e40ea7ffa3cf6e8494c61a1fc82cc"), 33); +} +END_TEST + +START_TEST(test_bip32_nist_compare) +{ + HDNode node1, node2, node3; + int i, r; + hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, NIST256P1_NAME, &node1); + hdnode_from_seed(fromhex("301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, NIST256P1_NAME, &node2); + for (i = 0; i < 100; i++) { + memcpy(&node3, &node1, sizeof(HDNode)); + r = hdnode_private_ckd(&node1, i); ck_assert_int_eq(r, 1); + r = hdnode_public_ckd(&node2, i); ck_assert_int_eq(r, 1); + r = hdnode_public_ckd(&node3, i); ck_assert_int_eq(r, 1); + ck_assert_int_eq(node1.depth, node2.depth); + ck_assert_int_eq(node1.depth, node3.depth); + ck_assert_int_eq(node1.fingerprint, node2.fingerprint); + ck_assert_int_eq(node1.fingerprint, node3.fingerprint); + ck_assert_int_eq(node1.child_num, node2.child_num); + ck_assert_int_eq(node1.child_num, node3.child_num); + ck_assert_mem_eq(node1.chain_code, node2.chain_code, 32); + ck_assert_mem_eq(node1.chain_code, node3.chain_code, 32); + ck_assert_mem_eq(node2.private_key, fromhex("0000000000000000000000000000000000000000000000000000000000000000"), 32); + ck_assert_mem_eq(node3.private_key, fromhex("0000000000000000000000000000000000000000000000000000000000000000"), 32); + ck_assert_mem_eq(node1.public_key, node2.public_key, 33); + ck_assert_mem_eq(node1.public_key, node3.public_key, 33); + } +} +END_TEST + +#define test_deterministic(KEY, MSG, K) do { \ sha256_Raw((uint8_t *)MSG, strlen(MSG), buf); \ res = generate_k_rfc6979(curve, &k, fromhex(KEY), buf); \ ck_assert_int_eq(res, 0); \ @@ -1419,6 +1565,12 @@ Suite *test_suite(void) tcase_add_test(tc, test_bip32_cache_2); suite_add_tcase(s, tc); + tc = tcase_create("bip32-nist"); + tcase_add_test(tc, test_bip32_nist_vector_1); + tcase_add_test(tc, test_bip32_nist_vector_2); + tcase_add_test(tc, test_bip32_nist_compare); + suite_add_tcase(s, tc); + tc = tcase_create("rfc6979"); tcase_add_test(tc, test_rfc6979); suite_add_tcase(s, tc);