aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2013-07-15 01:05:25 +0200
committerPieter Wuille <pieter.wuille@gmail.com>2013-07-15 01:05:25 +0200
commiteb2c999050cc17f8926bfa7a3dad74801c7ac4af (patch)
treec5adba99014c72f7f465f4732b137309fb11bf58
parent8388289eb6dfc9add233f8f8b536f84caf771fa6 (diff)
BIP32 derivation implementation
-rw-r--r--src/base58.h26
-rw-r--r--src/chainparams.cpp4
-rw-r--r--src/chainparams.h2
-rw-r--r--src/key.cpp180
-rw-r--r--src/key.h46
5 files changed, 258 insertions, 0 deletions
diff --git a/src/base58.h b/src/base58.h
index 1d4c3ee294..aabae8de88 100644
--- a/src/base58.h
+++ b/src/base58.h
@@ -390,4 +390,30 @@ public:
}
};
+
+template<typename K, int Size, CChainParams::Base58Type Type> class CBitcoinExtKeyBase : public CBase58Data
+{
+public:
+ void SetKey(const K &key) {
+ unsigned char vch[Size];
+ key.Encode(vch);
+ SetData(Params().Base58Prefix(Type), vch, vch+Size);
+ }
+
+ K GetKey() {
+ K ret;
+ ret.Decode(&vchData[0], &vchData[Size]);
+ return ret;
+ }
+
+ CBitcoinExtKeyBase(const K &key) {
+ SetKey(key);
+ }
+
+ CBitcoinExtKeyBase() {}
+};
+
+typedef CBitcoinExtKeyBase<CExtKey, 74, CChainParams::EXT_SECRET_KEY> CBitcoinExtKey;
+typedef CBitcoinExtKeyBase<CExtPubKey, 74, CChainParams::EXT_PUBLIC_KEY> CBitcoinExtPubKey;
+
#endif // BITCOIN_BASE58_H
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 69636e5be5..49873023ae 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -147,6 +147,8 @@ public:
base58Prefixes[PUBKEY_ADDRESS] = list_of(0);
base58Prefixes[SCRIPT_ADDRESS] = list_of(5);
base58Prefixes[SECRET_KEY] = list_of(128);
+ base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x88)(0xB2)(0x1E);
+ base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x88)(0xAD)(0xE4);
// Convert the pnSeeds array into usable address objects.
for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++)
@@ -209,6 +211,8 @@ public:
base58Prefixes[PUBKEY_ADDRESS] = list_of(111);
base58Prefixes[SCRIPT_ADDRESS] = list_of(196);
base58Prefixes[SECRET_KEY] = list_of(239);
+ base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x35)(0x87)(0xCF);
+ base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x35)(0x83)(0x94);
}
virtual Network NetworkID() const { return CChainParams::TESTNET; }
};
diff --git a/src/chainparams.h b/src/chainparams.h
index 9f4c079ae2..1930e49afb 100644
--- a/src/chainparams.h
+++ b/src/chainparams.h
@@ -45,6 +45,8 @@ public:
PUBKEY_ADDRESS,
SCRIPT_ADDRESS,
SECRET_KEY,
+ EXT_PUBLIC_KEY,
+ EXT_SECRET_KEY,
MAX_BASE58_TYPES
};
diff --git a/src/key.cpp b/src/key.cpp
index f73708199a..7092b0e0c8 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <openssl/bn.h>
#include <openssl/ecdsa.h>
#include <openssl/rand.h>
#include <openssl/obj_mac.h>
@@ -253,6 +254,57 @@ public:
ECDSA_SIG_free(sig);
return ret;
}
+
+ static bool TweakSecret(unsigned char vchSecretOut[32], const unsigned char vchSecretIn[32], const unsigned char vchTweak[32])
+ {
+ bool ret = true;
+ BN_CTX *ctx = BN_CTX_new();
+ BN_CTX_start(ctx);
+ BIGNUM *bnSecret = BN_CTX_get(ctx);
+ BIGNUM *bnTweak = BN_CTX_get(ctx);
+ BIGNUM *bnOrder = BN_CTX_get(ctx);
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp256k1);
+ EC_GROUP_get_order(group, bnOrder, ctx); // what a grossly inefficient way to get the (constant) group order...
+ BN_bin2bn(vchTweak, 32, bnTweak);
+ if (BN_cmp(bnTweak, bnOrder) >= 0)
+ ret = false; // extremely unlikely
+ BN_bin2bn(vchSecretIn, 32, bnSecret);
+ BN_add(bnSecret, bnSecret, bnTweak);
+ BN_nnmod(bnSecret, bnSecret, bnOrder, ctx);
+ if (BN_is_zero(bnSecret))
+ ret = false; // ridiculously unlikely
+ int nBits = BN_num_bits(bnSecret);
+ memset(vchSecretOut, 0, 32);
+ BN_bn2bin(bnSecret, &vchSecretOut[32-(nBits+7)/8]);
+ EC_GROUP_free(group);
+ BN_CTX_end(ctx);
+ BN_CTX_free(ctx);
+ return ret;
+ }
+
+ bool TweakPublic(const unsigned char vchTweak[32]) {
+ bool ret = true;
+ BN_CTX *ctx = BN_CTX_new();
+ BN_CTX_start(ctx);
+ BIGNUM *bnTweak = BN_CTX_get(ctx);
+ BIGNUM *bnOrder = BN_CTX_get(ctx);
+ BIGNUM *bnOne = BN_CTX_get(ctx);
+ const EC_GROUP *group = EC_KEY_get0_group(pkey);
+ EC_GROUP_get_order(group, bnOrder, ctx); // what a grossly inefficient way to get the (constant) group order...
+ BN_bin2bn(vchTweak, 32, bnTweak);
+ if (BN_cmp(bnTweak, bnOrder) >= 0)
+ ret = false; // extremely unlikely
+ EC_POINT *point = EC_POINT_dup(EC_KEY_get0_public_key(pkey), group);
+ BN_one(bnOne);
+ EC_POINT_mul(group, point, bnTweak, point, bnOne, ctx);
+ if (EC_POINT_is_at_infinity(group, point))
+ ret = false; // ridiculously unlikely
+ EC_KEY_set_public_key(pkey, point);
+ EC_POINT_free(point);
+ BN_CTX_end(ctx);
+ BN_CTX_free(ctx);
+ return ret;
+ }
};
}; // end of anonymous namespace
@@ -393,3 +445,131 @@ bool CPubKey::Decompress() {
key.GetPubKey(*this, false);
return true;
}
+
+void static BIP32Hash(const unsigned char chainCode[32], unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]) {
+ unsigned char num[4];
+ num[0] = (nChild >> 24) & 0xFF;
+ num[1] = (nChild >> 16) & 0xFF;
+ num[2] = (nChild >> 8) & 0xFF;
+ num[3] = (nChild >> 0) & 0xFF;
+ HMAC_SHA512_CTX ctx;
+ HMAC_SHA512_Init(&ctx, chainCode, 32);
+ HMAC_SHA512_Update(&ctx, &header, 1);
+ HMAC_SHA512_Update(&ctx, data, 32);
+ HMAC_SHA512_Update(&ctx, num, 4);
+ HMAC_SHA512_Final(output, &ctx);
+}
+
+bool CKey::Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const {
+ assert(IsValid());
+ assert(IsCompressed());
+ unsigned char out[64];
+ LockObject(out);
+ if ((nChild >> 31) == 0) {
+ CPubKey pubkey = GetPubKey();
+ assert(pubkey.begin() + 33 == pubkey.end());
+ BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, out);
+ } else {
+ assert(begin() + 32 == end());
+ BIP32Hash(cc, nChild, 0, begin(), out);
+ }
+ memcpy(ccChild, out+32, 32);
+ bool ret = CECKey::TweakSecret((unsigned char*)keyChild.begin(), begin(), out);
+ UnlockObject(out);
+ keyChild.fCompressed = true;
+ keyChild.fValid = ret;
+ return ret;
+}
+
+bool CPubKey::Derive(CPubKey& pubkeyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const {
+ assert(IsValid());
+ assert((nChild >> 31) == 0);
+ assert(begin() + 33 == end());
+ unsigned char out[64];
+ BIP32Hash(cc, nChild, *begin(), begin()+1, out);
+ memcpy(ccChild, out+32, 32);
+ CECKey key;
+ bool ret = key.SetPubKey(*this);
+ ret &= key.TweakPublic(out);
+ key.GetPubKey(pubkeyChild, true);
+ return ret;
+}
+
+bool CExtKey::Derive(CExtKey &out, unsigned int nChild) const {
+ out.nDepth = nDepth + 1;
+ CKeyID id = key.GetPubKey().GetID();
+ memcpy(&out.vchFingerprint[0], &id, 4);
+ out.nChild = nChild;
+ return key.Derive(out.key, out.vchChainCode, nChild, vchChainCode);
+}
+
+void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) {
+ static const char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
+ HMAC_SHA512_CTX ctx;
+ HMAC_SHA512_Init(&ctx, hashkey, sizeof(hashkey));
+ HMAC_SHA512_Update(&ctx, seed, nSeedLen);
+ unsigned char out[64];
+ LockObject(out);
+ HMAC_SHA512_Final(out, &ctx);
+ key.Set(&out[0], &out[32], true);
+ memcpy(vchChainCode, &out[32], 32);
+ UnlockObject(out);
+ nDepth = 0;
+ nChild = 0;
+ memset(vchFingerprint, 0, sizeof(vchFingerprint));
+}
+
+CExtPubKey CExtKey::Neuter() const {
+ CExtPubKey ret;
+ ret.nDepth = nDepth;
+ memcpy(&ret.vchFingerprint[0], &vchFingerprint[0], 4);
+ ret.nChild = nChild;
+ ret.pubkey = key.GetPubKey();
+ memcpy(&ret.vchChainCode[0], &vchChainCode[0], 32);
+ return ret;
+}
+
+void CExtKey::Encode(unsigned char code[74]) const {
+ code[0] = nDepth;
+ memcpy(code+1, vchFingerprint, 4);
+ code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
+ code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF;
+ memcpy(code+9, vchChainCode, 32);
+ code[41] = 0;
+ assert(key.size() == 32);
+ memcpy(code+42, key.begin(), 32);
+}
+
+void CExtKey::Decode(const unsigned char code[74]) {
+ nDepth = code[0];
+ memcpy(vchFingerprint, code+1, 4);
+ nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
+ memcpy(vchChainCode, code+9, 32);
+ key.Set(code+42, code+74, true);
+}
+
+void CExtPubKey::Encode(unsigned char code[74]) const {
+ code[0] = nDepth;
+ memcpy(code+1, vchFingerprint, 4);
+ code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
+ code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF;
+ memcpy(code+9, vchChainCode, 32);
+ assert(pubkey.size() == 33);
+ memcpy(code+41, pubkey.begin(), 33);
+}
+
+void CExtPubKey::Decode(const unsigned char code[74]) {
+ nDepth = code[0];
+ memcpy(vchFingerprint, code+1, 4);
+ nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8];
+ memcpy(vchChainCode, code+9, 32);
+ pubkey.Set(code+41, code+74);
+}
+
+bool CExtPubKey::Derive(CExtPubKey &out, unsigned int nChild) const {
+ out.nDepth = nDepth + 1;
+ CKeyID id = pubkey.GetID();
+ memcpy(&out.vchFingerprint[0], &id, 4);
+ out.nChild = nChild;
+ return pubkey.Derive(out.pubkey, out.vchChainCode, nChild, vchChainCode);
+}
diff --git a/src/key.h b/src/key.h
index ce469ad298..75431e944f 100644
--- a/src/key.h
+++ b/src/key.h
@@ -161,6 +161,9 @@ public:
// Turn this public key into an uncompressed public key.
bool Decompress();
+
+ // Derive BIP32 child pubkey.
+ bool Derive(CPubKey& pubkeyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const;
};
@@ -201,6 +204,10 @@ public:
UnlockObject(vch);
}
+ friend bool operator==(const CKey &a, const CKey &b) {
+ return a.fCompressed == b.fCompressed && memcmp(&a.vch[0], &b.vch[0], 32);
+ }
+
// Initialize using begin and end iterators to byte data.
template<typename T>
void Set(const T pbegin, const T pend, bool fCompressedIn) {
@@ -251,6 +258,45 @@ public:
// 0x1D = second key with even y, 0x1E = second key with odd y,
// add 0x04 for compressed keys.
bool SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) const;
+
+ // Derive BIP32 child key.
+ bool Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const;
+};
+
+struct CExtPubKey {
+ unsigned char nDepth;
+ unsigned char vchFingerprint[4];
+ unsigned int nChild;
+ unsigned char vchChainCode[32];
+ CPubKey pubkey;
+
+ friend bool operator==(const CExtPubKey &a, const CExtPubKey &b) {
+ return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild &&
+ memcmp(&a.vchChainCode[0], &b.vchChainCode[0], 32) == 0 && a.pubkey == b.pubkey;
+ }
+
+ void Encode(unsigned char code[74]) const;
+ void Decode(const unsigned char code[74]);
+ bool Derive(CExtPubKey &out, unsigned int nChild) const;
+};
+
+struct CExtKey {
+ unsigned char nDepth;
+ unsigned char vchFingerprint[4];
+ unsigned int nChild;
+ unsigned char vchChainCode[32];
+ CKey key;
+
+ friend bool operator==(const CExtKey &a, const CExtKey &b) {
+ return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild &&
+ memcmp(&a.vchChainCode[0], &b.vchChainCode[0], 32) == 0 && a.key == b.key;
+ }
+
+ void Encode(unsigned char code[74]) const;
+ void Decode(const unsigned char code[74]);
+ bool Derive(CExtKey &out, unsigned int nChild) const;
+ CExtPubKey Neuter() const;
+ void SetMaster(const unsigned char *seed, unsigned int nSeedLen);
};
#endif