// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2012 The Bitcoin developers
+// Copyright (c) 2009-2013 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 <stdexcept>
#include <vector>
-#include <openssl/ec.h>
-#include <openssl/ecdsa.h>
-#include <openssl/obj_mac.h>
-
-// #include "serialize.h"
+#include "allocators.h"
+#include "serialize.h"
#include "uint256.h"
+#include "hash.h"
-// secp160k1
-// const unsigned int PRIVATE_KEY_SIZE = 192;
-// const unsigned int PUBLIC_KEY_SIZE = 41;
-// const unsigned int SIGNATURE_SIZE = 48;
-//
-// secp192k1
-// const unsigned int PRIVATE_KEY_SIZE = 222;
-// const unsigned int PUBLIC_KEY_SIZE = 49;
-// const unsigned int SIGNATURE_SIZE = 57;
-//
-// secp224k1
-// const unsigned int PRIVATE_KEY_SIZE = 250;
-// const unsigned int PUBLIC_KEY_SIZE = 57;
-// const unsigned int SIGNATURE_SIZE = 66;
-//
// secp256k1:
// const unsigned int PRIVATE_KEY_SIZE = 279;
// const unsigned int PUBLIC_KEY_SIZE = 65;
// see www.keylength.com
// script supports up to 75 for single byte push
-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
+/** A reference to a CKey: the Hash160 of its serialized public key */
+class CKeyID : public uint160
{
public:
- explicit key_error(const std::string& str) : std::runtime_error(str) {}
+ CKeyID() : uint160(0) { }
+ CKeyID(const uint160 &in) : uint160(in) { }
};
-
-// secure_allocator is defined in serialize.h
-// CPrivKey is a serialized private key, with all parameters included (279 bytes)
-typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
-// CSecret is a serialization of just the secret parameter (32 bytes)
-typedef std::vector<unsigned char, secure_allocator<unsigned char> > CSecret;
-
-/** An encapsulated OpenSSL Elliptic Curve key (public and/or private) */
-class CKey
+/** A reference to a CScript: the Hash160 of its serialization (see script.h) */
+class CScriptID : public uint160
{
-protected:
- EC_KEY* pkey;
- bool fSet;
- bool fCompressedPubKey;
-
- void SetCompressedPubKey()
- {
- EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
- fCompressedPubKey = true;
- }
-
public:
+ CScriptID() : uint160(0) { }
+ CScriptID(const uint160 &in) : uint160(in) { }
+};
- 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;
+/** An encapsulated public key. */
+class CPubKey {
+private:
+ // Just store the serialized data.
+ // Its length can very cheaply be computed from the first byte.
+ unsigned char vch[65];
+
+ // Compute the length of a pubkey with a given first byte.
+ unsigned int static GetLen(unsigned char chHeader) {
+ if (chHeader == 2 || chHeader == 3)
+ return 33;
+ if (chHeader == 4 || chHeader == 6 || chHeader == 7)
+ return 65;
+ return 0;
}
- CKey()
- {
- Reset();
+ // Set this key data to be invalid
+ void Invalidate() {
+ vch[0] = 0xFF;
}
- CKey(const CKey& b)
- {
- pkey = EC_KEY_dup(b.pkey);
- if (pkey == NULL)
- throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed");
- fSet = b.fSet;
+public:
+ // Construct an invalid public key.
+ CPubKey() {
+ Invalidate();
}
- 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;
- return (*this);
+ // Initialize a public key using begin/end iterators to byte data.
+ template<typename T>
+ void Set(const T pbegin, const T pend) {
+ int len = pend == pbegin ? 0 : GetLen(pbegin[0]);
+ if (len && len == (pend-pbegin))
+ memcpy(vch, (unsigned char*)&pbegin[0], len);
+ else
+ Invalidate();
}
- ~CKey()
- {
- EC_KEY_free(pkey);
+ // Construct a public key using begin/end iterators to byte data.
+ template<typename T>
+ CPubKey(const T pbegin, const T pend) {
+ Set(pbegin, pend);
}
- bool IsNull() const
- {
- return !fSet;
+ // Construct a public key from a byte vector.
+ CPubKey(const std::vector<unsigned char> &vch) {
+ Set(vch.begin(), vch.end());
}
- bool IsCompressed() const
- {
- return fCompressedPubKey;
- }
+ // Simple read-only vector-like interface to the pubkey data.
+ unsigned int size() const { return GetLen(vch[0]); }
+ const unsigned char *begin() const { return vch; }
+ const unsigned char *end() const { return vch+size(); }
+ const unsigned char &operator[](unsigned int pos) const { return vch[pos]; }
- 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;
+ // Comparator implementation.
+ friend bool operator==(const CPubKey &a, const CPubKey &b) {
+ return a.vch[0] == b.vch[0] &&
+ memcmp(a.vch, b.vch, a.size()) == 0;
}
-
- bool SetPrivKey(const CPrivKey& vchPrivKey)
- {
- const unsigned char* pbegin = &vchPrivKey[0];
- if (!d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size()))
- return false;
- fSet = true;
- return true;
+ friend bool operator!=(const CPubKey &a, const CPubKey &b) {
+ return !(a == b);
+ }
+ friend bool operator<(const CPubKey &a, const CPubKey &b) {
+ return a.vch[0] < b.vch[0] ||
+ (a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) < 0);
}
- bool SetSecret(const CSecret& vchSecret, bool fCompressed = false)
- {
- EC_KEY_free(pkey);
- pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
- if (pkey == NULL)
- throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed");
- 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)
- 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");
+ // Implement serialization, as if this was a byte vector.
+ unsigned int GetSerializeSize(int nType, int nVersion) const {
+ return size() + 1;
+ }
+ template<typename Stream> void Serialize(Stream &s, int nType, int nVersion) const {
+ unsigned int len = size();
+ ::WriteCompactSize(s, len);
+ s.write((char*)vch, len);
+ }
+ template<typename Stream> void Unserialize(Stream &s, int nType, int nVersion) {
+ unsigned int len = ::ReadCompactSize(s);
+ if (len <= 65) {
+ s.read((char*)vch, len);
+ } else {
+ // invalid pubkey, skip available data
+ char dummy;
+ while (len--)
+ s.read(&dummy, 1);
+ Invalidate();
}
- BN_clear_free(bn);
- fSet = true;
- if (fCompressed || fCompressedPubKey)
- SetCompressedPubKey();
- return true;
}
- CSecret GetSecret(bool &fCompressed) const
- {
- CSecret vchRet;
- vchRet.resize(32);
- const BIGNUM *bn = EC_KEY_get0_private_key(pkey);
- int nBytes = BN_num_bytes(bn);
- if (bn == NULL)
- throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed");
- int n=BN_bn2bin(bn,&vchRet[32 - nBytes]);
- if (n != nBytes)
- throw key_error("CKey::GetSecret(): BN_bn2bin failed");
- fCompressed = fCompressedPubKey;
- return vchRet;
+ // Get the KeyID of this public key (hash of its serialization)
+ CKeyID GetID() const {
+ return CKeyID(Hash160(vch, vch+size()));
}
- CPrivKey GetPrivKey() const
- {
- int nSize = i2d_ECPrivateKey(pkey, NULL);
- if (!nSize)
- throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed");
- CPrivKey vchPrivKey(nSize, 0);
- unsigned char* pbegin = &vchPrivKey[0];
- if (i2d_ECPrivateKey(pkey, &pbegin) != nSize)
- throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size");
- return vchPrivKey;
+ // Get the 256-bit hash of this public key.
+ uint256 GetHash() const {
+ return Hash(vch, vch+size());
}
- bool SetPubKey(const std::vector<unsigned char>& 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;
+ // just check syntactic correctness.
+ bool IsValid() const {
+ return size() > 0;
}
- std::vector<unsigned char> GetPubKey() const
- {
- int nSize = i2o_ECPublicKey(pkey, NULL);
- if (!nSize)
- throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed");
- std::vector<unsigned char> vchPubKey(nSize, 0);
- unsigned char* pbegin = &vchPubKey[0];
- if (i2o_ECPublicKey(pkey, &pbegin) != nSize)
- throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size");
- return vchPubKey;
+ // fully validate whether this is a valid public key (more expensive than IsValid())
+ bool IsFullyValid() const;
+
+ // Check whether this is a compressed public key.
+ bool IsCompressed() const {
+ return size() == 33;
}
- bool Sign(uint256 hash, std::vector<unsigned char>& vchSig)
- {
- 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); // Shrink to fit actual size
- return true;
+ // Verify a DER signature (~72 bytes).
+ // If this public key is not fully valid, the return value will be false.
+ bool Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const;
+
+ // Verify a compact signature (~65 bytes).
+ // See CKey::SignCompact.
+ bool VerifyCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig) const;
+
+ // Recover a public key from a compact signature.
+ bool RecoverCompact(const uint256 &hash, const std::vector<unsigned char>& vchSig);
+
+ // Turn this public key into an uncompressed public key.
+ bool Decompress();
+};
+
+
+// secure_allocator is defined in allocators.h
+// CPrivKey is a serialized private key, with all parameters included (279 bytes)
+typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
+
+/** An encapsulated private key. */
+class CKey {
+private:
+ // Whether this private key is valid. We check for correctness when modifying the key
+ // data, so fValid should always correspond to the actual state.
+ bool fValid;
+
+ // Whether the public key corresponding to this private key is (to be) compressed.
+ bool fCompressed;
+
+ // The actual byte data
+ unsigned char vch[32];
+
+ // Check whether the 32-byte array pointed to be vch is valid keydata.
+ bool static Check(const unsigned char *vch);
+public:
+
+ // Construct an invalid private key.
+ CKey() : fValid(false) {
+ LockObject(vch);
}
- // 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<unsigned char>& 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;
+ // Copy constructor. This is necessary because of memlocking.
+ CKey(const CKey &secret) : fValid(secret.fValid), fCompressed(secret.fCompressed) {
+ LockObject(vch);
+ memcpy(vch, secret.vch, sizeof(vch));
}
- // 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<unsigned char>& 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;
+ // Destructor (again necessary because of memlocking).
+ ~CKey() {
+ UnlockObject(vch);
+ }
+
+ // Initialize using begin and end iterators to byte data.
+ template<typename T>
+ void Set(const T pbegin, const T pend, bool fCompressedIn) {
+ if (pend - pbegin != 32) {
+ fValid = false;
+ return;
}
- 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;
+ if (Check(&pbegin[0])) {
+ memcpy(vch, (unsigned char*)&pbegin[0], 32);
+ fValid = true;
+ fCompressed = fCompressedIn;
+ } else {
+ fValid = false;
}
- return false;
}
- bool Verify(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;
- }
+ // Simple read-only vector-like interface.
+ unsigned int size() const { return (fValid ? 32 : 0); }
+ const unsigned char *begin() const { return vch; }
+ const unsigned char *end() const { return vch + size(); }
- // Verify a compact signature
- bool VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig)
- {
- CKey key;
- if (!key.SetCompactSignature(hash, vchSig))
- return false;
- if (GetPubKey() != key.GetPubKey())
- return false;
- return true;
- }
+ // Check whether this private key is valid.
+ bool IsValid() const { return fValid; }
- bool IsValid()
- {
- if (!fSet)
- return false;
+ // Check whether the public key corresponding to this private key is (to be) compressed.
+ bool IsCompressed() const { return fCompressed; }
- bool fCompr;
- CSecret secret = GetSecret(fCompr);
- CKey key2;
- key2.SetSecret(secret, fCompr);
- return GetPubKey() == key2.GetPubKey();
- }
+ // Initialize from a CPrivKey (serialized OpenSSL private key data).
+ bool SetPrivKey(const CPrivKey &vchPrivKey, bool fCompressed);
+
+ // Generate a new private key using a cryptographic PRNG.
+ void MakeNewKey(bool fCompressed);
+
+ // Convert the private key to a CPrivKey (serialized OpenSSL private key data).
+ // This is expensive.
+ CPrivKey GetPrivKey() const;
+
+ // Compute the public key from a private key.
+ // This is expensive.
+ CPubKey GetPubKey() const;
+
+ // Create a DER-serialized signature.
+ bool Sign(const uint256 &hash, std::vector<unsigned char>& vchSig) const;
+
+ // 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,
+ // add 0x04 for compressed keys.
+ bool SignCompact(const uint256 &hash, std::vector<unsigned char>& vchSig) const;
};
#endif