X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fkey.cpp;h=e95fb1764c843563145f4455b890dfc515cb490c;hb=13369afc225fd435b7d3649b048e7233341f292a;hp=b6c3f2868825f7b506aaf3b240a822eb09c50483;hpb=fcbeaff8d049e414284631989b950e56b909525c;p=novacoin.git diff --git a/src/key.cpp b/src/key.cpp index b6c3f28..e95fb17 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying -// file license.txt or http://www.opensource.org/licenses/mit-license.php. +// file COPYING or http://www.opensource.org/licenses/mit-license.php. #include @@ -8,7 +8,6 @@ #include #include "key.h" -#include "util.h" // Generate a private key from just the secret parameter int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key) @@ -49,7 +48,7 @@ err: // Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields // recid selects which key is recovered -// if check is nonzero, additional checks are performed +// 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; @@ -121,6 +120,51 @@ err: 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 = NULL; + + + void CKey::SetCompressedPubKey() { EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED); @@ -130,6 +174,8 @@ void CKey::SetCompressedPubKey() void CKey::Reset() { fCompressedPubKey = false; + if (pkey != NULL) + EC_KEY_free(pkey); pkey = EC_KEY_new_by_curve_name(NID_secp256k1); if (pkey == NULL) throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed"); @@ -138,6 +184,7 @@ void CKey::Reset() CKey::CKey() { + pkey = NULL; Reset(); } @@ -147,6 +194,7 @@ CKey::CKey(const CKey& b) if (pkey == NULL) throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed"); fSet = b.fSet; + fCompressedPubKey = b.fCompressedPubKey; } CKey& CKey::operator=(const CKey& b) @@ -154,6 +202,7 @@ CKey& CKey::operator=(const CKey& b) if (!EC_KEY_copy(pkey, b.pkey)) throw key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed"); fSet = b.fSet; + fCompressedPubKey = b.fCompressedPubKey; return (*this); } @@ -172,6 +221,37 @@ bool CKey::IsCompressed() const return fCompressedPubKey; } +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; +} + +bool CKey::ReserealizeSignature(std::vector& vchSig) +{ + if (vchSig.empty()) + return false; + + unsigned char *pos = &vchSig[0]; + ECDSA_SIG *sig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&pos, vchSig.size()); + if (sig == NULL) + return false; + + bool ret = false; + int nSize = i2d_ECDSA_SIG(sig, NULL); + if (nSize > 0) { + vchSig.resize(nSize); // grow or shrink as needed + + pos = &vchSig[0]; + i2d_ECDSA_SIG(sig, &pos); + + ret = true; + } + + ECDSA_SIG_free(sig); + + return ret; +} + void CKey::MakeNewKey(bool fCompressed) { if (!EC_KEY_generate_key(pkey)) @@ -184,10 +264,24 @@ void CKey::MakeNewKey(bool fCompressed) bool CKey::SetPrivKey(const CPrivKey& vchPrivKey) { const unsigned char* pbegin = &vchPrivKey[0]; - if (!d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) - return false; - fSet = true; - return true; + if (d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size())) + { + // In testing, d2i_ECPrivateKey can return true + // but fill in pkey with a key that fails + // EC_KEY_check_key, so: + if (EC_KEY_check_key(pkey)) + { + fSet = true; + return true; + } + } + // If vchPrivKey data is bad d2i_ECPrivateKey() can + // leave pkey in a state where calling EC_KEY_free() + // crashes. To avoid that, set pkey to NULL and + // leak the memory (a leak is better than a crash) + pkey = NULL; + Reset(); + return false; } bool CKey::SetSecret(const CSecret& vchSecret, bool fCompressed) @@ -240,18 +334,22 @@ CPrivKey CKey::GetPrivKey() const return vchPrivKey; } -bool CKey::SetPubKey(const std::vector& vchPubKey) +bool CKey::SetPubKey(const CPubKey& vchPubKey) { - const unsigned char* pbegin = &vchPubKey[0]; - if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size())) - return false; - fSet = true; - if (vchPubKey.size() == 33) - SetCompressedPubKey(); - return true; + const unsigned char* pbegin = &vchPubKey.vchPubKey[0]; + if (o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.vchPubKey.size())) + { + fSet = true; + if (vchPubKey.vchPubKey.size() == 33) + SetCompressedPubKey(); + return true; + } + pkey = NULL; + Reset(); + return false; } -std::vector CKey::GetPubKey() const +CPubKey CKey::GetPubKey() const { int nSize = i2o_ECPublicKey(pkey, NULL); if (!nSize) @@ -260,19 +358,34 @@ std::vector CKey::GetPubKey() const unsigned char* pbegin = &vchPubKey[0]; if (i2o_ECPublicKey(pkey, &pbegin) != nSize) throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size"); - return vchPubKey; + return CPubKey(vchPubKey); } bool CKey::Sign(uint256 hash, std::vector& vchSig) { + vchSig.clear(); + ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); + if (sig==NULL) + return false; + const EC_GROUP *group = EC_KEY_get0_group(pkey); + CBigNum order, halforder; + EC_GROUP_get_order(group, &order, NULL); + BN_rshift1(&halforder, &order); + // enforce low S values, by negating the value (modulo the order) if above order/2. + if (BN_cmp(sig->s, &halforder) > 0) { + BN_sub(sig->s, &order, sig->s); + } unsigned int nSize = ECDSA_size(pkey); vchSig.resize(nSize); // Make sure it is big enough - if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], &nSize, pkey)) - { + unsigned char *pos = &vchSig[0]; + nSize = i2d_ECDSA_SIG(sig, &pos); + ECDSA_SIG_free(sig); + vchSig.resize(nSize); // Shrink to fit actual size + // Testing our new signature + if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) { vchSig.clear(); return false; } - vchSig.resize(nSize); // Shrink to fit actual size return true; } @@ -286,14 +399,22 @@ bool CKey::SignCompact(uint256 hash, std::vector& vchSig) ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); if (sig==NULL) return false; + const EC_GROUP *group = EC_KEY_get0_group(pkey); + CBigNum order, halforder; + EC_GROUP_get_order(group, &order, NULL); + BN_rshift1(&halforder, &order); + // enforce low S values, by negating the value (modulo the order) if above order/2. + if (BN_cmp(sig->s, &halforder) > 0) { + BN_sub(sig->s, &order, sig->s); + } vchSig.clear(); vchSig.resize(65,0); int nBitsR = BN_num_bits(sig->r); int nBitsS = BN_num_bits(sig->s); if (nBitsR <= 256 && nBitsS <= 256) { - int nRecId = -1; - for (int i=0; i<4; i++) + int8_t nRecId = -1; + for (int8_t i=0; i<4; i++) { CKey keyRec; keyRec.fSet = true; @@ -308,7 +429,10 @@ bool CKey::SignCompact(uint256 hash, std::vector& vchSig) } if (nRecId == -1) + { + ECDSA_SIG_free(sig); throw key_error("CKey::SignCompact() : unable to construct recoverable key"); + } vchSig[0] = nRecId+27+(fCompressedPubKey ? 4 : 0); BN_bn2bin(sig->r,&vchSig[33-(nBitsR+7)/8]); @@ -347,16 +471,40 @@ bool CKey::SetCompactSignature(uint256 hash, const std::vector& v ECDSA_SIG_free(sig); return true; } + ECDSA_SIG_free(sig); return false; } bool CKey::Verify(uint256 hash, const std::vector& vchSig) { - // -1 = error, 0 = bad sig, 1 = good - if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) + if (vchSig.empty()) return false; - return true; + // New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first. + unsigned char *norm_der = NULL; + ECDSA_SIG *norm_sig = ECDSA_SIG_new(); + const unsigned char* sigptr = &vchSig[0]; + assert(norm_sig); + if (d2i_ECDSA_SIG(&norm_sig, &sigptr, vchSig.size()) == NULL) + { + /* As of OpenSSL 1.0.0p d2i_ECDSA_SIG frees and nulls the pointer on + * error. But OpenSSL's own use of this function redundantly frees the + * result. As ECDSA_SIG_free(NULL) is a no-op, and in the absence of a + * clear contract for the function behaving the same way is more + * conservative. + */ + ECDSA_SIG_free(norm_sig); + return false; + } + int derlen = i2d_ECDSA_SIG(norm_sig, &norm_der); + ECDSA_SIG_free(norm_sig); + if (derlen <= 0) + return false; + + // -1 = error, 0 = bad sig, 1 = good + bool ret = ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), norm_der, derlen, pkey) == 1; + OPENSSL_free(norm_der); + return ret; } bool CKey::VerifyCompact(uint256 hash, const std::vector& vchSig) @@ -375,6 +523,9 @@ bool CKey::IsValid() if (!fSet) return false; + if (!EC_KEY_check_key(pkey)) + return false; + bool fCompr; CSecret secret = GetSecret(fCompr); CKey key2;