X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fkey.cpp;h=6239ba5712e1c848c7f0d8d23fa71b804c53cecd;hb=afc2bcf340f842111978138a9e04e0b14e5de9fe;hp=ece835d1472412b02841dcedc03043b02f4bb24f;hpb=4aa8021a9651257605e8c16df2973eee8aebe575;p=novacoin.git diff --git a/src/key.cpp b/src/key.cpp index ece835d..6239ba5 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,6 +1,8 @@ // 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 #include #include @@ -46,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; @@ -118,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); @@ -127,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"); @@ -135,6 +184,7 @@ void CKey::Reset() CKey::CKey() { + pkey = NULL; Reset(); } @@ -169,6 +219,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)) @@ -181,10 +262,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) @@ -237,18 +332,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) @@ -257,19 +356,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; } @@ -283,14 +397,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; @@ -305,7 +427,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]); @@ -344,15 +469,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) @@ -362,6 +512,7 @@ bool CKey::VerifyCompact(uint256 hash, const std::vector& vchSig) return false; if (GetPubKey() != key.GetPubKey()) return false; + return true; } @@ -370,6 +521,9 @@ bool CKey::IsValid() if (!fSet) return false; + if (!EC_KEY_check_key(pkey)) + return false; + bool fCompr; CSecret secret = GetSecret(fCompr); CKey key2;