X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fkey.h;h=ba68f0dd7025e59a3d9355dd02cd881e9c535a92;hb=d11488abd05cb39a9f481e7c4c35f780197a3d28;hp=c0fce18bf3c5e62a15b1554c701ae9ba4b1393e9;hpb=acd6501610817eee0bd1c8ea9c591f043affbaec;p=novacoin.git diff --git a/src/key.h b/src/key.h index c0fce18..ba68f0d 100644 --- a/src/key.h +++ b/src/key.h @@ -1,13 +1,20 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto +// 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. #ifndef BITCOIN_KEY_H #define BITCOIN_KEY_H +#include +#include + #include #include #include +#include "serialize.h" +#include "uint256.h" + // secp160k1 // const unsigned int PRIVATE_KEY_SIZE = 192; // const unsigned int PUBLIC_KEY_SIZE = 41; @@ -31,42 +38,8 @@ // see www.keylength.com // script supports up to 75 for single byte push -int static inline 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); -} - +int extern EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key); +int extern ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check); class key_error : public std::runtime_error { @@ -76,24 +49,41 @@ public: // secure_allocator is defined in serialize.h +// CPrivKey is a serialized private key, with all parameters included (279 bytes) typedef std::vector > CPrivKey; +// CSecret is a serialization of just the secret parameter (32 bytes) typedef std::vector > CSecret; +/** An encapsulated OpenSSL Elliptic Curve key (public and/or private) */ class CKey { protected: EC_KEY* pkey; bool fSet; + bool fCompressedPubKey; + + void SetCompressedPubKey() + { + EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED); + fCompressedPubKey = true; + } public: - CKey() + + void Reset() { + fCompressedPubKey = false; pkey = EC_KEY_new_by_curve_name(NID_secp256k1); if (pkey == NULL) throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed"); fSet = false; } + CKey() + { + Reset(); + } + CKey(const CKey& b) { pkey = EC_KEY_dup(b.pkey); @@ -120,10 +110,17 @@ public: return !fSet; } - void MakeNewKey() + bool IsCompressed() const + { + return fCompressedPubKey; + } + + void MakeNewKey(bool fCompressed) { if (!EC_KEY_generate_key(pkey)) throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed"); + if (fCompressed) + SetCompressedPubKey(); fSet = true; } @@ -136,7 +133,7 @@ public: return true; } - bool SetSecret(const CSecret& vchSecret) + bool SetSecret(const CSecret& vchSecret, bool fCompressed = false) { EC_KEY_free(pkey); pkey = EC_KEY_new_by_curve_name(NID_secp256k1); @@ -145,16 +142,21 @@ public: if (vchSecret.size() != 32) throw key_error("CKey::SetSecret() : secret must be 32 bytes"); BIGNUM *bn = BN_bin2bn(&vchSecret[0],32,BN_new()); - if (bn == NULL) + if (bn == NULL) throw key_error("CKey::SetSecret() : BN_bin2bn failed"); if (!EC_KEY_regenerate_key(pkey,bn)) + { + BN_clear_free(bn); throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed"); + } BN_clear_free(bn); fSet = true; + if (fCompressed || fCompressedPubKey) + SetCompressedPubKey(); return true; } - CSecret GetSecret() const + CSecret GetSecret(bool &fCompressed) const { CSecret vchRet; vchRet.resize(32); @@ -165,12 +167,13 @@ public: int n=BN_bn2bin(bn,&vchRet[32 - nBytes]); if (n != nBytes) throw key_error("CKey::GetSecret(): BN_bn2bin failed"); + fCompressed = fCompressedPubKey; return vchRet; } CPrivKey GetPrivKey() const { - unsigned int nSize = i2d_ECPrivateKey(pkey, NULL); + int nSize = i2d_ECPrivateKey(pkey, NULL); if (!nSize) throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed"); CPrivKey vchPrivKey(nSize, 0); @@ -186,12 +189,14 @@ public: if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size())) return false; fSet = true; + if (vchPubKey.size() == 33) + SetCompressedPubKey(); return true; } std::vector GetPubKey() const { - unsigned int nSize = i2o_ECPublicKey(pkey, NULL); + int nSize = i2o_ECPublicKey(pkey, NULL); if (!nSize) throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed"); std::vector vchPubKey(nSize, 0); @@ -203,16 +208,91 @@ public: bool Sign(uint256 hash, std::vector& vchSig) { - vchSig.clear(); - unsigned char pchSig[10000]; - unsigned int nSize = 0; - if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), pchSig, &nSize, pkey)) + 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)) + { + vchSig.clear(); return false; - vchSig.resize(nSize); - memcpy(&vchSig[0], pchSig, nSize); + } + vchSig.resize(nSize); // Shrink to fit actual size return true; } + // create a compact signature (65 bytes), which allows reconstructing the used public key + // The format is one header byte, followed by two times 32 bytes for the serialized r and s values. + // The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, + // 0x1D = second key with even y, 0x1E = second key with odd y + bool SignCompact(uint256 hash, std::vector& vchSig) + { + bool fOk = false; + ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); + if (sig==NULL) + return false; + 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++) + { + CKey keyRec; + keyRec.fSet = true; + if (fCompressedPubKey) + keyRec.SetCompressedPubKey(); + if (ECDSA_SIG_recover_key_GFp(keyRec.pkey, sig, (unsigned char*)&hash, sizeof(hash), i, 1) == 1) + if (keyRec.GetPubKey() == this->GetPubKey()) + { + nRecId = i; + break; + } + } + + if (nRecId == -1) + 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]); + BN_bn2bin(sig->s,&vchSig[65-(nBitsS+7)/8]); + fOk = true; + } + 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 SetCompactSignature(uint256 hash, const std::vector& vchSig) + { + if (vchSig.size() != 65) + return false; + int nV = vchSig[0]; + if (nV<27 || nV>=35) + return false; + ECDSA_SIG *sig = ECDSA_SIG_new(); + BN_bin2bn(&vchSig[1],32,sig->r); + BN_bin2bn(&vchSig[33],32,sig->s); + + EC_KEY_free(pkey); + pkey = EC_KEY_new_by_curve_name(NID_secp256k1); + if (nV >= 31) + { + SetCompressedPubKey(); + nV -= 4; + } + if (ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), nV - 27, 0) == 1) + { + fSet = true; + ECDSA_SIG_free(sig); + return true; + } + return false; + } + bool Verify(uint256 hash, const std::vector& vchSig) { // -1 = error, 0 = bad sig, 1 = good @@ -221,20 +301,27 @@ public: return true; } - static bool Sign(const CPrivKey& vchPrivKey, uint256 hash, std::vector& vchSig) + // Verify a compact signature + bool VerifyCompact(uint256 hash, const std::vector& vchSig) { CKey key; - if (!key.SetPrivKey(vchPrivKey)) + if (!key.SetCompactSignature(hash, vchSig)) + return false; + if (GetPubKey() != key.GetPubKey()) return false; - return key.Sign(hash, vchSig); + return true; } - static bool Verify(const std::vector& vchPubKey, uint256 hash, const std::vector& vchSig) + bool IsValid() { - CKey key; - if (!key.SetPubKey(vchPubKey)) + if (!fSet) return false; - return key.Verify(hash, vchSig); + + bool fCompr; + CSecret secret = GetSecret(fCompr); + CKey key2; + key2.SetSecret(secret, fCompr); + return GetPubKey() == key2.GetPubKey(); } };