4a09e1df51
Previously if bitcoind is linked with an OpenSSL which is compiled without EC support, this is seen as an assertion failure "pKey != NULL" at key.cpp:134, which occurs after several seconds. It is an esoteric piece of knowledge to interpret this as "oops, I linked with the wrong OpenSSL", and because of the delay it may not even be noticed. The new output is : OpenSSL appears to lack support for elliptic curve cryptography. For more information, visit https://en.bitcoin.it/wiki/OpenSSL_and_EC_Libraries : Initialization sanity check failed. Bitcoin Core is shutting down. which occurs immediately after attempted startup. This also blocks in an InitSanityCheck() function which currently only checks for EC support but should eventually do more. See #4081.
645 lines
20 KiB
C++
645 lines
20 KiB
C++
// Copyright (c) 2009-2013 The Bitcoin developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "key.h"
|
|
|
|
#include <openssl/bn.h>
|
|
#include <openssl/ecdsa.h>
|
|
#include <openssl/obj_mac.h>
|
|
#include <openssl/rand.h>
|
|
|
|
// anonymous namespace with local implementation code (OpenSSL interaction)
|
|
namespace {
|
|
|
|
// Generate a private key from just the secret parameter
|
|
int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
|
|
{
|
|
int ok = 0;
|
|
BN_CTX *ctx = NULL;
|
|
EC_POINT *pub_key = NULL;
|
|
|
|
if (!eckey) return 0;
|
|
|
|
const EC_GROUP *group = EC_KEY_get0_group(eckey);
|
|
|
|
if ((ctx = BN_CTX_new()) == NULL)
|
|
goto err;
|
|
|
|
pub_key = EC_POINT_new(group);
|
|
|
|
if (pub_key == NULL)
|
|
goto err;
|
|
|
|
if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
|
|
goto err;
|
|
|
|
EC_KEY_set_private_key(eckey,priv_key);
|
|
EC_KEY_set_public_key(eckey,pub_key);
|
|
|
|
ok = 1;
|
|
|
|
err:
|
|
|
|
if (pub_key)
|
|
EC_POINT_free(pub_key);
|
|
if (ctx != NULL)
|
|
BN_CTX_free(ctx);
|
|
|
|
return(ok);
|
|
}
|
|
|
|
// Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields
|
|
// recid selects which key is recovered
|
|
// if check is non-zero, additional checks are performed
|
|
int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check)
|
|
{
|
|
if (!eckey) return 0;
|
|
|
|
int ret = 0;
|
|
BN_CTX *ctx = NULL;
|
|
|
|
BIGNUM *x = NULL;
|
|
BIGNUM *e = NULL;
|
|
BIGNUM *order = NULL;
|
|
BIGNUM *sor = NULL;
|
|
BIGNUM *eor = NULL;
|
|
BIGNUM *field = NULL;
|
|
EC_POINT *R = NULL;
|
|
EC_POINT *O = NULL;
|
|
EC_POINT *Q = NULL;
|
|
BIGNUM *rr = NULL;
|
|
BIGNUM *zero = NULL;
|
|
int n = 0;
|
|
int i = recid / 2;
|
|
|
|
const EC_GROUP *group = EC_KEY_get0_group(eckey);
|
|
if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; }
|
|
BN_CTX_start(ctx);
|
|
order = BN_CTX_get(ctx);
|
|
if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; }
|
|
x = BN_CTX_get(ctx);
|
|
if (!BN_copy(x, order)) { ret=-1; goto err; }
|
|
if (!BN_mul_word(x, i)) { ret=-1; goto err; }
|
|
if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; }
|
|
field = BN_CTX_get(ctx);
|
|
if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; }
|
|
if (BN_cmp(x, field) >= 0) { ret=0; goto err; }
|
|
if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
|
|
if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; }
|
|
if (check)
|
|
{
|
|
if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
|
|
if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; }
|
|
if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; }
|
|
}
|
|
if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; }
|
|
n = EC_GROUP_get_degree(group);
|
|
e = BN_CTX_get(ctx);
|
|
if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; }
|
|
if (8*msglen > n) BN_rshift(e, e, 8-(n & 7));
|
|
zero = BN_CTX_get(ctx);
|
|
if (!BN_zero(zero)) { ret=-1; goto err; }
|
|
if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; }
|
|
rr = BN_CTX_get(ctx);
|
|
if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; }
|
|
sor = BN_CTX_get(ctx);
|
|
if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; }
|
|
eor = BN_CTX_get(ctx);
|
|
if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; }
|
|
if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; }
|
|
if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; }
|
|
|
|
ret = 1;
|
|
|
|
err:
|
|
if (ctx) {
|
|
BN_CTX_end(ctx);
|
|
BN_CTX_free(ctx);
|
|
}
|
|
if (R != NULL) EC_POINT_free(R);
|
|
if (O != NULL) EC_POINT_free(O);
|
|
if (Q != NULL) EC_POINT_free(Q);
|
|
return ret;
|
|
}
|
|
|
|
// RAII Wrapper around OpenSSL's EC_KEY
|
|
class CECKey {
|
|
private:
|
|
EC_KEY *pkey;
|
|
|
|
public:
|
|
CECKey() {
|
|
pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
|
|
assert(pkey != NULL);
|
|
}
|
|
|
|
~CECKey() {
|
|
EC_KEY_free(pkey);
|
|
}
|
|
|
|
void GetSecretBytes(unsigned char vch[32]) const {
|
|
const BIGNUM *bn = EC_KEY_get0_private_key(pkey);
|
|
assert(bn);
|
|
int nBytes = BN_num_bytes(bn);
|
|
int n=BN_bn2bin(bn,&vch[32 - nBytes]);
|
|
assert(n == nBytes);
|
|
memset(vch, 0, 32 - nBytes);
|
|
}
|
|
|
|
void SetSecretBytes(const unsigned char vch[32]) {
|
|
bool ret;
|
|
BIGNUM bn;
|
|
BN_init(&bn);
|
|
ret = BN_bin2bn(vch, 32, &bn);
|
|
assert(ret);
|
|
ret = EC_KEY_regenerate_key(pkey, &bn);
|
|
assert(ret);
|
|
BN_clear_free(&bn);
|
|
}
|
|
|
|
void GetPrivKey(CPrivKey &privkey, bool fCompressed) {
|
|
EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED);
|
|
int nSize = i2d_ECPrivateKey(pkey, NULL);
|
|
assert(nSize);
|
|
privkey.resize(nSize);
|
|
unsigned char* pbegin = &privkey[0];
|
|
int nSize2 = i2d_ECPrivateKey(pkey, &pbegin);
|
|
assert(nSize == nSize2);
|
|
}
|
|
|
|
bool SetPrivKey(const CPrivKey &privkey, bool fSkipCheck=false) {
|
|
const unsigned char* pbegin = &privkey[0];
|
|
if (d2i_ECPrivateKey(&pkey, &pbegin, privkey.size())) {
|
|
if(fSkipCheck)
|
|
return true;
|
|
|
|
// d2i_ECPrivateKey returns true if parsing succeeds.
|
|
// This doesn't necessarily mean the key is valid.
|
|
if (EC_KEY_check_key(pkey))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void GetPubKey(CPubKey &pubkey, bool fCompressed) {
|
|
EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED);
|
|
int nSize = i2o_ECPublicKey(pkey, NULL);
|
|
assert(nSize);
|
|
assert(nSize <= 65);
|
|
unsigned char c[65];
|
|
unsigned char *pbegin = c;
|
|
int nSize2 = i2o_ECPublicKey(pkey, &pbegin);
|
|
assert(nSize == nSize2);
|
|
pubkey.Set(&c[0], &c[nSize]);
|
|
}
|
|
|
|
bool SetPubKey(const CPubKey &pubkey) {
|
|
const unsigned char* pbegin = pubkey.begin();
|
|
return o2i_ECPublicKey(&pkey, &pbegin, pubkey.size());
|
|
}
|
|
|
|
bool Sign(const uint256 &hash, std::vector<unsigned char>& vchSig) {
|
|
vchSig.clear();
|
|
ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey);
|
|
if (sig == NULL)
|
|
return false;
|
|
BN_CTX *ctx = BN_CTX_new();
|
|
BN_CTX_start(ctx);
|
|
const EC_GROUP *group = EC_KEY_get0_group(pkey);
|
|
BIGNUM *order = BN_CTX_get(ctx);
|
|
BIGNUM *halforder = BN_CTX_get(ctx);
|
|
EC_GROUP_get_order(group, order, ctx);
|
|
BN_rshift1(halforder, order);
|
|
if (BN_cmp(sig->s, halforder) > 0) {
|
|
// enforce low S values, by negating the value (modulo the order) if above order/2.
|
|
BN_sub(sig->s, order, sig->s);
|
|
}
|
|
BN_CTX_end(ctx);
|
|
BN_CTX_free(ctx);
|
|
unsigned int nSize = ECDSA_size(pkey);
|
|
vchSig.resize(nSize); // Make sure it is big enough
|
|
unsigned char *pos = &vchSig[0];
|
|
nSize = i2d_ECDSA_SIG(sig, &pos);
|
|
ECDSA_SIG_free(sig);
|
|
vchSig.resize(nSize); // Shrink to fit actual size
|
|
return true;
|
|
}
|
|
|
|
bool Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) {
|
|
// -1 = error, 0 = bad sig, 1 = good
|
|
if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool SignCompact(const uint256 &hash, unsigned char *p64, int &rec) {
|
|
bool fOk = false;
|
|
ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey);
|
|
if (sig==NULL)
|
|
return false;
|
|
memset(p64, 0, 64);
|
|
int nBitsR = BN_num_bits(sig->r);
|
|
int nBitsS = BN_num_bits(sig->s);
|
|
if (nBitsR <= 256 && nBitsS <= 256) {
|
|
CPubKey pubkey;
|
|
GetPubKey(pubkey, true);
|
|
for (int i=0; i<4; i++) {
|
|
CECKey keyRec;
|
|
if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) {
|
|
CPubKey pubkeyRec;
|
|
keyRec.GetPubKey(pubkeyRec, true);
|
|
if (pubkeyRec == pubkey) {
|
|
rec = i;
|
|
fOk = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
assert(fOk);
|
|
BN_bn2bin(sig->r,&p64[32-(nBitsR+7)/8]);
|
|
BN_bn2bin(sig->s,&p64[64-(nBitsS+7)/8]);
|
|
}
|
|
ECDSA_SIG_free(sig);
|
|
return fOk;
|
|
}
|
|
|
|
// reconstruct public key from a compact signature
|
|
// This is only slightly more CPU intensive than just verifying it.
|
|
// If this function succeeds, the recovered public key is guaranteed to be valid
|
|
// (the signature is a valid signature of the given data for that key)
|
|
bool Recover(const uint256 &hash, const unsigned char *p64, int rec)
|
|
{
|
|
if (rec<0 || rec>=3)
|
|
return false;
|
|
ECDSA_SIG *sig = ECDSA_SIG_new();
|
|
BN_bin2bn(&p64[0], 32, sig->r);
|
|
BN_bin2bn(&p64[32], 32, sig->s);
|
|
bool ret = ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), rec, 0) == 1;
|
|
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;
|
|
}
|
|
};
|
|
|
|
int CompareBigEndian(const unsigned char *c1, size_t c1len, const unsigned char *c2, size_t c2len) {
|
|
while (c1len > c2len) {
|
|
if (*c1)
|
|
return 1;
|
|
c1++;
|
|
c1len--;
|
|
}
|
|
while (c2len > c1len) {
|
|
if (*c2)
|
|
return -1;
|
|
c2++;
|
|
c2len--;
|
|
}
|
|
while (c1len > 0) {
|
|
if (*c1 > *c2)
|
|
return 1;
|
|
if (*c2 > *c1)
|
|
return -1;
|
|
c1++;
|
|
c2++;
|
|
c1len--;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Order of secp256k1's generator minus 1.
|
|
const unsigned char vchMaxModOrder[32] = {
|
|
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
|
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
|
|
0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,
|
|
0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40
|
|
};
|
|
|
|
// Half of the order of secp256k1's generator minus 1.
|
|
const unsigned char vchMaxModHalfOrder[32] = {
|
|
0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
|
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
|
|
0x5D,0x57,0x6E,0x73,0x57,0xA4,0x50,0x1D,
|
|
0xDF,0xE9,0x2F,0x46,0x68,0x1B,0x20,0xA0
|
|
};
|
|
|
|
const unsigned char vchZero[0] = {};
|
|
|
|
|
|
}; // end of anonymous namespace
|
|
|
|
bool CKey::Check(const unsigned char *vch) {
|
|
return CompareBigEndian(vch, 32, vchZero, 0) > 0 &&
|
|
CompareBigEndian(vch, 32, vchMaxModOrder, 32) <= 0;
|
|
}
|
|
|
|
bool CKey::CheckSignatureElement(const unsigned char *vch, int len, bool half) {
|
|
return CompareBigEndian(vch, len, vchZero, 0) > 0 &&
|
|
CompareBigEndian(vch, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0;
|
|
}
|
|
|
|
void CKey::MakeNewKey(bool fCompressedIn) {
|
|
do {
|
|
RAND_bytes(vch, sizeof(vch));
|
|
} while (!Check(vch));
|
|
fValid = true;
|
|
fCompressed = fCompressedIn;
|
|
}
|
|
|
|
bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) {
|
|
CECKey key;
|
|
if (!key.SetPrivKey(privkey))
|
|
return false;
|
|
key.GetSecretBytes(vch);
|
|
fCompressed = fCompressedIn;
|
|
fValid = true;
|
|
return true;
|
|
}
|
|
|
|
CPrivKey CKey::GetPrivKey() const {
|
|
assert(fValid);
|
|
CECKey key;
|
|
key.SetSecretBytes(vch);
|
|
CPrivKey privkey;
|
|
key.GetPrivKey(privkey, fCompressed);
|
|
return privkey;
|
|
}
|
|
|
|
CPubKey CKey::GetPubKey() const {
|
|
assert(fValid);
|
|
CECKey key;
|
|
key.SetSecretBytes(vch);
|
|
CPubKey pubkey;
|
|
key.GetPubKey(pubkey, fCompressed);
|
|
return pubkey;
|
|
}
|
|
|
|
bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig) const {
|
|
if (!fValid)
|
|
return false;
|
|
CECKey key;
|
|
key.SetSecretBytes(vch);
|
|
return key.Sign(hash, vchSig);
|
|
}
|
|
|
|
bool CKey::SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) const {
|
|
if (!fValid)
|
|
return false;
|
|
CECKey key;
|
|
key.SetSecretBytes(vch);
|
|
vchSig.resize(65);
|
|
int rec = -1;
|
|
if (!key.SignCompact(hash, &vchSig[1], rec))
|
|
return false;
|
|
assert(rec != -1);
|
|
vchSig[0] = 27 + rec + (fCompressed ? 4 : 0);
|
|
return true;
|
|
}
|
|
|
|
bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) {
|
|
CECKey key;
|
|
if (!key.SetPrivKey(privkey, fSkipCheck))
|
|
return false;
|
|
|
|
key.GetSecretBytes(vch);
|
|
fCompressed = vchPubKey.IsCompressed();
|
|
fValid = true;
|
|
|
|
if (fSkipCheck)
|
|
return true;
|
|
|
|
if (GetPubKey() != vchPubKey)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
|
|
if (!IsValid())
|
|
return false;
|
|
CECKey key;
|
|
if (!key.SetPubKey(*this))
|
|
return false;
|
|
if (!key.Verify(hash, vchSig))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) {
|
|
if (vchSig.size() != 65)
|
|
return false;
|
|
CECKey key;
|
|
if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4))
|
|
return false;
|
|
key.GetPubKey(*this, (vchSig[0] - 27) & 4);
|
|
return true;
|
|
}
|
|
|
|
bool CPubKey::IsFullyValid() const {
|
|
if (!IsValid())
|
|
return false;
|
|
CECKey key;
|
|
if (!key.SetPubKey(*this))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool CPubKey::Decompress() {
|
|
if (!IsValid())
|
|
return false;
|
|
CECKey key;
|
|
if (!key.SetPubKey(*this))
|
|
return false;
|
|
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);
|
|
}
|
|
|
|
bool ECC_InitSanityCheck() {
|
|
EC_KEY *pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
|
|
if(pkey == NULL)
|
|
return false;
|
|
EC_KEY_free(pkey);
|
|
|
|
// TODO Is there more EC functionality that could be missing?
|
|
return true;
|
|
}
|
|
|
|
|