From afc9bcfe30fb26ca3611486eaf2c08e75a7b3571 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 12 Sep 2013 19:21:24 +0200 Subject: [PATCH] implement bip32 - https://en.bitcoin.it/wiki/BIP_0032 --- .gitignore | 2 ++ Makefile | 12 ++++++--- bignum.c | 30 ++++++++++++++++----- bignum.h | 10 ++++--- bip32.c | 47 ++++++++++++++++++++++++++++++++ bip32.h | 22 +++++++++++++++ ecdsa.c | 16 ++++++++++- ecdsa.h | 3 ++- test-bip32.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ test-pubkey.c | 58 ++++++++++++++++++++++++++++++++++++++++ test-verify.c | 2 +- 11 files changed, 260 insertions(+), 16 deletions(-) create mode 100644 bip32.c create mode 100644 bip32.h create mode 100644 test-bip32.c create mode 100644 test-pubkey.c diff --git a/.gitignore b/.gitignore index 96e5615047..25ad0464d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ *.o +test-bip32 +test-pubkey test-rfc6979 test-speed test-verify diff --git a/Makefile b/Makefile index b790094a3e..652a858e08 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,18 @@ CC = gcc CFLAGS = -Wall -Os -OBJS = bignum.o ecdsa.o secp256k1.o sha2.o rand.o hmac.o +OBJS = bignum.o ecdsa.o secp256k1.o sha2.o rand.o hmac.o bip32.o -all: test-rfc6979 test-speed test-verify +all: test-bip32 test-pubkey test-rfc6979 test-speed test-verify %.o: %.c $(CC) $(CFLAGS) -o $@ -c $< +test-bip32: test-bip32.o $(OBJS) + gcc test-bip32.o $(OBJS) -o test-bip32 + +test-pubkey: test-pubkey.o $(OBJS) + gcc test-pubkey.o $(OBJS) -o test-pubkey + test-rfc6979: test-rfc6979.o $(OBJS) gcc test-rfc6979.o $(OBJS) -o test-rfc6979 @@ -17,4 +23,4 @@ test-verify: test-verify.o $(OBJS) gcc test-verify.o $(OBJS) -o test-verify -lcrypto clean: - rm -f $(OBJS) test-rfc6979 test-speed test-verify + rm -f *.o test-bip32 test-pubkey test-rfc6979 test-speed test-verify diff --git a/bignum.c b/bignum.c index d4b341fb2c..6910153d4d 100644 --- a/bignum.c +++ b/bignum.c @@ -83,8 +83,8 @@ int bn_is_less(const bignum256 *a, const bignum256 *b) return 0; } -// assumes x < 2*prime -void bn_mod(bignum256 *x, bignum256 const *prime) +// assumes x < 2*prime, result < prime +void bn_mod(bignum256 *x, const bignum256 *prime) { int i = 8; uint32_t temp; @@ -113,7 +113,7 @@ void bn_mod(bignum256 *x, bignum256 const *prime) // x = k * x // both inputs and result may be bigger than prime but not bigger than 2 * prime -void bn_multiply(const bignum256 *k, bignum256 *x, bignum256 const *prime) +void bn_multiply(const bignum256 *k, bignum256 *x, const bignum256 *prime) { int i, j; uint64_t temp = 0; @@ -157,7 +157,8 @@ void bn_multiply(const bignum256 *k, bignum256 *x, bignum256 const *prime) } } -void bn_fast_mod(bignum256 *x, bignum256 const *prime) +// result is smaller than 2*prime +void bn_fast_mod(bignum256 *x, const bignum256 *prime) { int j; uint32_t coef; @@ -182,7 +183,7 @@ void bn_fast_mod(bignum256 *x, bignum256 const *prime) #endif // in field G_prime, small but slow -void bn_inverse(bignum256 *x, bignum256 const *prime) +void bn_inverse(bignum256 *x, const bignum256 *prime) { uint32_t i, j, limb; bignum256 res; @@ -210,7 +211,7 @@ void bn_inverse(bignum256 *x, bignum256 const *prime) #else // in field G_prime, big but fast -void bn_inverse(bignum256 *x, bignum256 const *prime) +void bn_inverse(bignum256 *x, const bignum256 *prime) { int i, j, k, len1, len2, mask; uint32_t u[9], v[9], s[10], r[10], temp, temp2; @@ -382,6 +383,23 @@ void bn_inverse(bignum256 *x, bignum256 const *prime) } #endif + +void bn_addmod(bignum256 *a, const bignum256 *b, const bignum256 *prime) +{ + int i; + uint32_t carry = 0; + for (i = 0; i < 9; i++) { + a->val[i] += b->val[i] + carry; + if (a->val[i] > 0x3FFFFFFF) { + carry = a->val[i] >> 30; + a->val[i] &= 0x3FFFFFFF; + } else { + carry = 0; + } + } + bn_mod(a, prime); +} + // res = a - b // b < 2*prime; result not normalized void bn_substract(const bignum256 *a, const bignum256 *b, bignum256 *res) diff --git a/bignum.h b/bignum.h index 62a5f6e786..5a292f7edc 100644 --- a/bignum.h +++ b/bignum.h @@ -54,13 +54,15 @@ int bn_is_zero(const bignum256 *a); int bn_is_less(const bignum256 *a, const bignum256 *b); -void bn_mod(bignum256 *x, bignum256 const *prime); +void bn_mod(bignum256 *x, const bignum256 *prime); -void bn_multiply(const bignum256 *k, bignum256 *x, bignum256 const *prime); +void bn_multiply(const bignum256 *k, bignum256 *x, const bignum256 *prime); -void bn_fast_mod(bignum256 *x, bignum256 const *prime); +void bn_fast_mod(bignum256 *x, const bignum256 *prime); -void bn_inverse(bignum256 *x, bignum256 const *prime); +void bn_inverse(bignum256 *x, const bignum256 *prime); + +void bn_addmod(bignum256 *a, const bignum256 *b, const bignum256 *prime); void bn_substract(const bignum256 *a, const bignum256 *b, bignum256 *res); diff --git a/bip32.c b/bip32.c new file mode 100644 index 0000000000..566a2d1860 --- /dev/null +++ b/bip32.c @@ -0,0 +1,47 @@ +#include + +#include "bignum.h" +#include "hmac.h" +#include "ecdsa.h" +#include "bip32.h" + +void xprv_from_seed(uint8_t *seed, int seed_len, xprv *out) +{ + out->version = 0x0488ADE4; // main-net + out->depth = 0; + out->fingerprint = 0x00000000; + out->child_num = 0; + // this can be done because private_key[32] and chain_code[32] + // form a continuous 64 byte block in the memory + hmac_sha512((uint8_t *)"Bitcoin seed", 12, seed, seed_len, out->private_key); + ecdsa_get_public_key_compressed(out->private_key, out->public_key); +} + +void xprv_descent(xprv *inout, uint32_t i) +{ + uint8_t data[1 + 32 + 4]; + bignum256 a, b; + + if (i & 0x80000000) { + data[0] = 0; + memcpy(data + 1, inout->private_key, 32); + } else { + ecdsa_get_public_key_compressed(inout->private_key, data); + } + write_be(data + 33, i); + + bn_read_be(inout->private_key, &a); + + // this can be done because private_key[32] and chain_code[32] + // form a continuous 64 byte block in the memory + hmac_sha512(inout->chain_code, 32, data, sizeof(data), inout->private_key); + + bn_read_be(inout->private_key, &b); + bn_addmod(&a, &b, &order256k1); + + inout->depth++; + inout->child_num = i; + bn_write_be(&a, inout->private_key); + + ecdsa_get_public_key_compressed(inout->private_key, inout->public_key); +} diff --git a/bip32.h b/bip32.h new file mode 100644 index 0000000000..497f244a6e --- /dev/null +++ b/bip32.h @@ -0,0 +1,22 @@ +#ifndef __BIP32_H__ +#define __BIP32_H__ + +#include + +typedef struct { + uint32_t version; + uint32_t depth; + uint32_t fingerprint; + uint32_t child_num; + uint8_t private_key[32]; // private_key + chain_code have to + uint8_t chain_code[32]; // form a continuous 64 byte block + uint8_t public_key[33]; +} xprv; + +void xprv_from_seed(uint8_t *seed, int seed_len, xprv *out); + +#define xprv_descent_prime(X, I) xprv_descent((X), ((I) | 0x80000000)) + +void xprv_descent(xprv *inout, uint32_t i); + +#endif diff --git a/ecdsa.c b/ecdsa.c index 4757f733e6..7b33d92222 100644 --- a/ecdsa.c +++ b/ecdsa.c @@ -306,7 +306,7 @@ void ecdsa_sign(const uint8_t *priv_key, const uint8_t *msg, uint32_t msg_len, u // uses secp256k1 curve // priv_key is a 32 byte big endian stored number // pub_key is at least 70 bytes long array for the public key -void ecdsa_get_public_key(const uint8_t *priv_key, uint8_t *pub_key, uint32_t *pub_key_len) +void ecdsa_get_public_key_der(const uint8_t *priv_key, uint8_t *pub_key, uint32_t *pub_key_len) { uint32_t i; curve_point R; @@ -324,6 +324,20 @@ void ecdsa_get_public_key(const uint8_t *priv_key, uint8_t *pub_key, uint32_t *p *pub_key_len = i + 2; } + +// pub_key is always 33 bytes long +void ecdsa_get_public_key_compressed(const uint8_t *priv_key, uint8_t *pub_key) +{ + curve_point R; + bignum256 k; + + bn_read_be(priv_key, &k); + // compute k*G + scalar_multiply(&k, &R); + pub_key[0] = 0x02 | (R.y.val[0] & 0x01); + bn_write_be(&R.x, pub_key + 1); +} + // uses secp256k1 curve // pub_key and signature are DER encoded // msg is a data that was signed diff --git a/ecdsa.h b/ecdsa.h index f993fde080..77def17122 100644 --- a/ecdsa.h +++ b/ecdsa.h @@ -30,7 +30,8 @@ // all functions use secp256k1 curve void ecdsa_sign(const uint8_t *priv_key, const uint8_t *msg, uint32_t msg_len, uint8_t *sig, uint32_t *sig_len); -void ecdsa_get_public_key(const uint8_t *priv_key, uint8_t *pub_key, uint32_t *pub_key_len); +void ecdsa_get_public_key_der(const uint8_t *priv_key, uint8_t *pub_key, uint32_t *pub_key_len); +void ecdsa_get_public_key_compressed(const uint8_t *priv_key, uint8_t *pub_key); int ecdsa_verify(const uint8_t *pub_key, const uint8_t *signature, const uint8_t *msg, uint32_t msg_len); #endif diff --git a/test-bip32.c b/test-bip32.c new file mode 100644 index 0000000000..3d01e06d06 --- /dev/null +++ b/test-bip32.c @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2013 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "bip32.h" + +// test vectors from https://en.bitcoin.it/wiki/BIP_0032_TestVectors + +void xprv_print(xprv *in) +{ + int i; + + printf("chain : "); for (i = 0; i < 32; i++) printf("%02x", in->chain_code[i]); printf("\n"); + printf("priv : "); for (i = 0; i < 32; i++) printf("%02x", in->private_key[i]); printf("\n"); + printf("pub : "); for (i = 0; i < 33; i++) printf("%02x", in->public_key[i]); printf("\n"); + printf("\n"); +} + +int main() +{ + xprv node; + + xprv_from_seed((uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16, &node); + + printf("[Chain m] got\n"); + xprv_print(&node); + printf("[Chain m] expected\n"); + printf("chain : 873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508\n"); + printf("priv : e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35\n"); + printf("pub : 0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2\n"); + printf("\n"); + + xprv_descent_prime(&node, 0); + + printf("[Chain m/0'] got\n"); + xprv_print(&node); + printf("[Chain m/0'] expected\n"); + printf("chain : 47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141\n"); + printf("priv : edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea\n"); + printf("pub : 035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56\n"); + printf("\n"); + + xprv_descent(&node, 1); + + printf("[Chain m/0'/1] got\n"); + xprv_print(&node); + printf("[Chain m/0'/1] expected\n"); + printf("chain : 2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19\n"); + printf("priv : 3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368\n"); + printf("pub : 03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c\n"); + printf("\n"); + + return 0; +} diff --git a/test-pubkey.c b/test-pubkey.c new file mode 100644 index 0000000000..28797e475e --- /dev/null +++ b/test-pubkey.c @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2013 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "ecdsa.h" + +// vectors from https://en.bitcoin.it/wiki/BIP_0032_TestVectors + +const char *privs[] = { +"\xe8\xf3\x2e\x72\x3d\xec\xf4\x05\x1a\xef\xac\x8e\x2c\x93\xc9\xc5\xb2\x14\x31\x38\x17\xcd\xb0\x1a\x14\x94\xb9\x17\xc8\x43\x6b\x35", +"\xed\xb2\xe1\x4f\x9e\xe7\x7d\x26\xdd\x93\xb4\xec\xed\xe8\xd1\x6e\xd4\x08\xce\x14\x9b\x6c\xd8\x0b\x07\x15\xa2\xd9\x11\xa0\xaf\xea", +"\x3c\x6c\xb8\xd0\xf6\xa2\x64\xc9\x1e\xa8\xb5\x03\x0f\xad\xaa\x8e\x53\x8b\x02\x0f\x0a\x38\x74\x21\xa1\x2d\xe9\x31\x9d\xc9\x33\x68", +"\xcb\xce\x0d\x71\x9e\xcf\x74\x31\xd8\x8e\x6a\x89\xfa\x14\x83\xe0\x2e\x35\x09\x2a\xf6\x0c\x04\x2b\x1d\xf2\xff\x59\xfa\x42\x4d\xca", +"\x0f\x47\x92\x45\xfb\x19\xa3\x8a\x19\x54\xc5\xc7\xc0\xeb\xab\x2f\x9b\xdf\xd9\x6a\x17\x56\x3e\xf2\x8a\x6a\x4b\x1a\x2a\x76\x4e\xf4", +"\x47\x1b\x76\xe3\x89\xe5\x28\xd6\xde\x6d\x81\x68\x57\xe0\x12\xc5\x45\x50\x51\xca\xd6\x66\x08\x50\xe5\x83\x72\xa6\xc3\xe6\xe7\xc8", +}; + +const char *pubs[] = { +"0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2", +"035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56", +"03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c", +"0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2", +"02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29", +"022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011", +}; + +int main() +{ + int i, k; + uint8_t pub[33]; + + for (k = 0; k < 6; k++) { + ecdsa_get_public_key_compressed((uint8_t *)privs[k], pub); + printf("got : "); for (i = 0; i < 33; i++) printf("%02x", pub[i]); printf("\n"); + printf("expected : %s\n", pubs[k]); + } + + return 0; +} diff --git a/test-verify.c b/test-verify.c index 4bb4e52e3e..cd894c3057 100644 --- a/test-verify.c +++ b/test-verify.c @@ -78,7 +78,7 @@ int main() ecdsa_sign(priv_key, msg, msg_len, sig, &sig_len); // generate public key from private key - ecdsa_get_public_key(priv_key, pub_key, &pub_key_len); + ecdsa_get_public_key_der(priv_key, pub_key, &pub_key_len); // use our ECDSA verifier to verify the message signature if (ecdsa_verify(pub_key, sig, msg, msg_len) != 0) {