dogecoin/src/key.cpp
Pieter Wuille 4644e65561 Also switch the (unused) verification code to low-s instead of even-s.
a81cd968 introduced a malleability breaker for signatures
(using an even value for S). In e0e14e43 this was changed to
the lower of two potential values, rather than the even one.
Only the signing code was changed though, the (for now unused)
verification code wasn't adapted.
2014-06-29 15:38:41 +02:00

649 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::VerifyCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
if (!IsValid())
return false;
if (vchSig.size() != 65)
return false;
CECKey key;
if (!key.Recover(hash, &vchSig[1], (vchSig[0] - 27) & ~4))
return false;
CPubKey pubkeyRec;
key.GetPubKey(pubkeyRec, IsCompressed());
if (*this != pubkeyRec)
return false;
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);
}