From 900078aeb4088b63ee271e774a00ccd126628528 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Tue, 21 Oct 2014 16:05:51 -0400 Subject: [PATCH 1/3] boost: moveonly: create eccryptoverify.h|cpp and move helper functions there Eventually (after 0.10) these files will hold the logic for crypto verification routines, and CKey/CPubKey will call into them. --- src/Makefile.am | 2 ++ src/eccryptoverify.cpp | 63 ++++++++++++++++++++++++++++++++++++++ src/eccryptoverify.h | 19 ++++++++++++ src/key.cpp | 53 ++------------------------------ src/script/interpreter.cpp | 3 +- 5 files changed, 88 insertions(+), 52 deletions(-) create mode 100644 src/eccryptoverify.cpp create mode 100644 src/eccryptoverify.h diff --git a/src/Makefile.am b/src/Makefile.am index 8253c4ab1..c9adf859f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -86,6 +86,7 @@ BITCOIN_CORE_H = \ core_io.h \ crypter.h \ db.h \ + eccryptoverify.h \ ecwrapper.h \ hash.h \ init.h \ @@ -220,6 +221,7 @@ libbitcoin_common_a_SOURCES = \ core/transaction.cpp \ core_read.cpp \ core_write.cpp \ + eccryptoverify.cpp \ ecwrapper.cpp \ hash.cpp \ key.cpp \ diff --git a/src/eccryptoverify.cpp b/src/eccryptoverify.cpp new file mode 100644 index 000000000..0a904f44b --- /dev/null +++ b/src/eccryptoverify.cpp @@ -0,0 +1,63 @@ +#include "eccryptoverify.h" + +namespace { + +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[1] = {0}; +} // anon namespace + +namespace eccrypto { + +bool Check(const unsigned char *vch) { + return vch && + CompareBigEndian(vch, 32, vchZero, 0) > 0 && + CompareBigEndian(vch, 32, vchMaxModOrder, 32) <= 0; +} + +bool CheckSignatureElement(const unsigned char *vch, int len, bool half) { + return vch && + CompareBigEndian(vch, len, vchZero, 0) > 0 && + CompareBigEndian(vch, len, half ? vchMaxModHalfOrder : vchMaxModOrder, 32) <= 0; +} + +} // namespace eccrypto diff --git a/src/eccryptoverify.h b/src/eccryptoverify.h new file mode 100644 index 000000000..7740e31db --- /dev/null +++ b/src/eccryptoverify.h @@ -0,0 +1,19 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// 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. + +#ifndef BITCOIN_EC_CRYPTO_VERIFY_H +#define BITCOIN_EC_CRYPTO_VERIFY_H + +#include +#include +class uint256; + +namespace eccrypto { + +bool Check(const unsigned char *vch); +bool CheckSignatureElement(const unsigned char *vch, int len, bool half); + +} // eccrypto namespace +#endif diff --git a/src/key.cpp b/src/key.cpp index c466e84f2..925b80ba0 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -5,6 +5,7 @@ #include "key.h" #include "crypto/sha2.h" +#include "eccryptoverify.h" #include "random.h" #ifdef USE_SECP256K1 @@ -30,60 +31,10 @@ public: static CSecp256k1Init instance_of_csecp256k1; #endif - -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[1] = {0}; - } // anon 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; + return eccrypto::Check(vch); } void CKey::MakeNewKey(bool fCompressedIn) { diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 3625972eb..e1e242882 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -9,6 +9,7 @@ #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha2.h" +#include "eccryptoverify.h" #include "key.h" #include "script/script.h" #include "uint256.h" @@ -122,7 +123,7 @@ bool static IsLowDERSignature(const valtype &vchSig) { // If the S value is above the order of the curve divided by two, its // complement modulo the order could have been used instead, which is // one byte shorter when encoded correctly. - if (!CKey::CheckSignatureElement(S, nLenS, true)) + if (!eccrypto::CheckSignatureElement(S, nLenS, true)) return error("Non-canonical signature: S value is unnecessarily high"); return true; From 78c228c6e5f35b6f2e1917d8677694779f837618 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Tue, 28 Oct 2014 17:35:24 -0400 Subject: [PATCH 2/3] boost: moveonly: move BIP32Hash to hash.h --- src/hash.cpp | 13 +++++++++++++ src/hash.h | 1 + src/key.cpp | 12 ------------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/hash.cpp b/src/hash.cpp index 218607a6f..29376b45a 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -63,3 +63,16 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector> 24) & 0xFF; + num[1] = (nChild >> 16) & 0xFF; + num[2] = (nChild >> 8) & 0xFF; + num[3] = (nChild >> 0) & 0xFF; + CHMAC_SHA512(chainCode, 32).Write(&header, 1) + .Write(data, 32) + .Write(num, 4) + .Finalize(output); +} diff --git a/src/hash.h b/src/hash.h index bdcd4afb4..53a7672a8 100644 --- a/src/hash.h +++ b/src/hash.h @@ -159,4 +159,5 @@ uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector& vDataToHash); +void BIP32Hash(const unsigned char chainCode[32], unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]); #endif // BITCOIN_HASH_H diff --git a/src/key.cpp b/src/key.cpp index 925b80ba0..2369aa252 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -237,18 +237,6 @@ bool CPubKey::Decompress() { 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; - CHMAC_SHA512(chainCode, 32).Write(&header, 1) - .Write(data, 32) - .Write(num, 4) - .Finalize(output); -} - bool CKey::Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const { assert(IsValid()); assert(IsCompressed()); From d2e74c55bdd8cee6a0cca49aca0e2ab1a182c9b5 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Tue, 28 Oct 2014 17:47:18 -0400 Subject: [PATCH 3/3] boost: moveonly: split CPubKey and friends to new files --- src/Makefile.am | 2 + src/alert.cpp | 2 +- src/base58.h | 1 + src/bloom.cpp | 1 + src/compressor.cpp | 2 +- src/key.cpp | 118 +------------------ src/key.h | 185 +----------------------------- src/keystore.h | 1 + src/pubkey.cpp | 131 +++++++++++++++++++++ src/pubkey.h | 206 ++++++++++++++++++++++++++++++++++ src/script/interpreter.cpp | 2 +- src/script/sigcache.cpp | 2 +- src/script/standard.cpp | 1 + src/script/standard.h | 4 +- src/test/miner_tests.cpp | 1 + src/test/sigopcount_tests.cpp | 1 + src/wallet_ismine.cpp | 1 + 17 files changed, 356 insertions(+), 305 deletions(-) create mode 100644 src/pubkey.cpp create mode 100644 src/pubkey.h diff --git a/src/Makefile.am b/src/Makefile.am index c9adf859f..3089b2ff4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -102,6 +102,7 @@ BITCOIN_CORE_H = \ noui.h \ pow.h \ protocol.h \ + pubkey.h \ random.h \ rpcclient.h \ rpcprotocol.h \ @@ -228,6 +229,7 @@ libbitcoin_common_a_SOURCES = \ keystore.cpp \ netbase.cpp \ protocol.cpp \ + pubkey.cpp \ script/interpreter.cpp \ script/script.cpp \ script/sigcache.cpp \ diff --git a/src/alert.cpp b/src/alert.cpp index f16898dc3..ee8c49669 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -7,7 +7,7 @@ #include "chainparams.h" #include "clientversion.h" -#include "key.h" +#include "pubkey.h" #include "net.h" #include "timedata.h" #include "ui_interface.h" diff --git a/src/base58.h b/src/base58.h index c5e230c72..7cd2d651a 100644 --- a/src/base58.h +++ b/src/base58.h @@ -16,6 +16,7 @@ #include "chainparams.h" #include "key.h" +#include "pubkey.h" #include "script/script.h" #include "script/standard.h" diff --git a/src/bloom.cpp b/src/bloom.cpp index c1e7aeb3b..df8cedaf6 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -5,6 +5,7 @@ #include "bloom.h" #include "core/transaction.h" +#include "hash.h" #include "script/script.h" #include "script/standard.h" #include "streams.h" diff --git a/src/compressor.cpp b/src/compressor.cpp index 806175dd3..c47a0f6f8 100644 --- a/src/compressor.cpp +++ b/src/compressor.cpp @@ -6,7 +6,7 @@ #include "compressor.h" #include "hash.h" -#include "key.h" +#include "pubkey.h" #include "script/standard.h" bool CScriptCompressor::IsToKeyID(CKeyID &hash) const diff --git a/src/key.cpp b/src/key.cpp index 2369aa252..1b539d073 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -6,6 +6,7 @@ #include "crypto/sha2.h" #include "eccryptoverify.h" +#include "pubkey.h" #include "random.h" #ifdef USE_SECP256K1 @@ -167,76 +168,6 @@ bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) { return true; } -bool CPubKey::Verify(const uint256 &hash, const std::vector& vchSig) const { - if (!IsValid()) - return false; -#ifdef USE_SECP256K1 - if (secp256k1_ecdsa_verify((const unsigned char*)&hash, 32, &vchSig[0], vchSig.size(), begin(), size()) != 1) - return false; -#else - CECKey key; - if (!key.SetPubKey(begin(), size())) - return false; - if (!key.Verify(hash, vchSig)) - return false; -#endif - return true; -} - -bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector& vchSig) { - if (vchSig.size() != 65) - return false; - int recid = (vchSig[0] - 27) & 3; - bool fComp = ((vchSig[0] - 27) & 4) != 0; -#ifdef USE_SECP256K1 - int pubkeylen = 65; - if (!secp256k1_ecdsa_recover_compact((const unsigned char*)&hash, 32, &vchSig[1], (unsigned char*)begin(), &pubkeylen, fComp, recid)) - return false; - assert((int)size() == pubkeylen); -#else - CECKey key; - if (!key.Recover(hash, &vchSig[1], recid)) - return false; - std::vector pubkey; - key.GetPubKey(pubkey, fComp); - Set(pubkey.begin(), pubkey.end()); -#endif - return true; -} - -bool CPubKey::IsFullyValid() const { - if (!IsValid()) - return false; -#ifdef USE_SECP256K1 - if (!secp256k1_ecdsa_pubkey_verify(begin(), size())) - return false; -#else - CECKey key; - if (!key.SetPubKey(begin(), size())) - return false; -#endif - return true; -} - -bool CPubKey::Decompress() { - if (!IsValid()) - return false; -#ifdef USE_SECP256K1 - int clen = size(); - int ret = secp256k1_ecdsa_pubkey_decompress((unsigned char*)begin(), &clen); - assert(ret); - assert(clen == (int)size()); -#else - CECKey key; - if (!key.SetPubKey(begin(), size())) - return false; - std::vector pubkey; - key.GetPubKey(pubkey, false); - Set(pubkey.begin(), pubkey.end()); -#endif - return true; -} - bool CKey::Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const { assert(IsValid()); assert(IsCompressed()); @@ -263,27 +194,6 @@ bool CKey::Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild 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); -#ifdef USE_SECP256K1 - pubkeyChild = *this; - bool ret = secp256k1_ecdsa_pubkey_tweak_add((unsigned char*)pubkeyChild.begin(), pubkeyChild.size(), out); -#else - CECKey key; - bool ret = key.SetPubKey(begin(), size()); - ret &= key.TweakPublic(out); - std::vector pubkey; - key.GetPubKey(pubkey, true); - pubkeyChild.Set(pubkey.begin(), pubkey.end()); -#endif - return ret; -} - bool CExtKey::Derive(CExtKey &out, unsigned int nChild) const { out.nDepth = nDepth + 1; CKeyID id = key.GetPubKey().GetID(); @@ -334,32 +244,6 @@ void CExtKey::Decode(const unsigned char code[74]) { 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); -} - bool ECC_InitSanityCheck() { #ifdef USE_SECP256K1 return true; diff --git a/src/key.h b/src/key.h index b4cb64768..b35cf0cad 100644 --- a/src/key.h +++ b/src/key.h @@ -7,13 +7,15 @@ #define BITCOIN_KEY_H #include "allocators.h" -#include "hash.h" #include "serialize.h" #include "uint256.h" #include #include +class CPubKey; +class CExtPubKey; + /** * secp256k1: * const unsigned int PRIVATE_KEY_SIZE = 279; @@ -24,169 +26,6 @@ * script supports up to 75 for single byte push */ -/** A reference to a CKey: the Hash160 of its serialized public key */ -class CKeyID : public uint160 -{ -public: - CKeyID() : uint160(0) {} - CKeyID(const uint160& in) : uint160(in) {} -}; - -/** 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; - } - - //! Set this key data to be invalid - void Invalidate() - { - vch[0] = 0xFF; - } - -public: - //! Construct an invalid public key. - CPubKey() - { - Invalidate(); - } - - //! Initialize a public key using begin/end iterators to byte data. - template - 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(); - } - - //! Construct a public key using begin/end iterators to byte data. - template - CPubKey(const T pbegin, const T pend) - { - Set(pbegin, pend); - } - - //! Construct a public key from a byte vector. - CPubKey(const std::vector& vch) - { - Set(vch.begin(), vch.end()); - } - - //! 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]; } - - //! 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; - } - 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); - } - - //! Implement serialization, as if this was a byte vector. - unsigned int GetSerializeSize(int nType, int nVersion) const - { - return size() + 1; - } - template - void Serialize(Stream& s, int nType, int nVersion) const - { - unsigned int len = size(); - ::WriteCompactSize(s, len); - s.write((char*)vch, len); - } - template - 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(); - } - } - - //! Get the KeyID of this public key (hash of its serialization) - CKeyID GetID() const - { - return CKeyID(Hash160(vch, vch + size())); - } - - //! Get the 256-bit hash of this public key. - uint256 GetHash() const - { - return Hash(vch, vch + size()); - } - - /* - * Check syntactic correctness. - * - * Note that this is consensus critical as CheckSig() calls it! - */ - bool IsValid() const - { - return size() > 0; - } - - //! 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; - } - - /** - * 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& vchSig) const; - - //! Recover a public key from a compact signature. - bool RecoverCompact(const uint256& hash, const std::vector& vchSig); - - //! Turn this public key into an uncompressed public key. - bool Decompress(); - - //! Derive BIP32 child pubkey. - bool Derive(CPubKey& pubkeyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const; -}; - - /** * secure_allocator is defined in allocators.h * CPrivKey is a serialized private key, with all parameters included (279 bytes) @@ -304,24 +143,6 @@ public: static bool CheckSignatureElement(const unsigned char* vch, int len, bool half); }; -struct CExtPubKey { - unsigned char nDepth; - unsigned char vchFingerprint[4]; - unsigned int nChild; - unsigned char vchChainCode[32]; - CPubKey pubkey; - - friend bool operator==(const CExtPubKey& a, const CExtPubKey& b) - { - return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild && - memcmp(&a.vchChainCode[0], &b.vchChainCode[0], 32) == 0 && a.pubkey == b.pubkey; - } - - void Encode(unsigned char code[74]) const; - void Decode(const unsigned char code[74]); - bool Derive(CExtPubKey& out, unsigned int nChild) const; -}; - struct CExtKey { unsigned char nDepth; unsigned char vchFingerprint[4]; diff --git a/src/keystore.h b/src/keystore.h index 66f8771d4..60502e9a2 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -7,6 +7,7 @@ #define BITCOIN_KEYSTORE_H #include "key.h" +#include "pubkey.h" #include "sync.h" #include diff --git a/src/pubkey.cpp b/src/pubkey.cpp new file mode 100644 index 000000000..3f16a4b4b --- /dev/null +++ b/src/pubkey.cpp @@ -0,0 +1,131 @@ +// Copyright (c) 2009-2014 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 "pubkey.h" + +#include "crypto/sha2.h" +#include "eccryptoverify.h" + +#ifdef USE_SECP256K1 +#include +#else +#include "ecwrapper.h" +#endif + +bool CPubKey::Verify(const uint256 &hash, const std::vector& vchSig) const { + if (!IsValid()) + return false; +#ifdef USE_SECP256K1 + if (secp256k1_ecdsa_verify((const unsigned char*)&hash, 32, &vchSig[0], vchSig.size(), begin(), size()) != 1) + return false; +#else + CECKey key; + if (!key.SetPubKey(begin(), size())) + return false; + if (!key.Verify(hash, vchSig)) + return false; +#endif + return true; +} + +bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector& vchSig) { + if (vchSig.size() != 65) + return false; + int recid = (vchSig[0] - 27) & 3; + bool fComp = ((vchSig[0] - 27) & 4) != 0; +#ifdef USE_SECP256K1 + int pubkeylen = 65; + if (!secp256k1_ecdsa_recover_compact((const unsigned char*)&hash, 32, &vchSig[1], (unsigned char*)begin(), &pubkeylen, fComp, recid)) + return false; + assert((int)size() == pubkeylen); +#else + CECKey key; + if (!key.Recover(hash, &vchSig[1], recid)) + return false; + std::vector pubkey; + key.GetPubKey(pubkey, fComp); + Set(pubkey.begin(), pubkey.end()); +#endif + return true; +} + +bool CPubKey::IsFullyValid() const { + if (!IsValid()) + return false; +#ifdef USE_SECP256K1 + if (!secp256k1_ecdsa_pubkey_verify(begin(), size())) + return false; +#else + CECKey key; + if (!key.SetPubKey(begin(), size())) + return false; +#endif + return true; +} + +bool CPubKey::Decompress() { + if (!IsValid()) + return false; +#ifdef USE_SECP256K1 + int clen = size(); + int ret = secp256k1_ecdsa_pubkey_decompress((unsigned char*)begin(), &clen); + assert(ret); + assert(clen == (int)size()); +#else + CECKey key; + if (!key.SetPubKey(begin(), size())) + return false; + std::vector pubkey; + key.GetPubKey(pubkey, false); + Set(pubkey.begin(), pubkey.end()); +#endif + return true; +} + +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); +#ifdef USE_SECP256K1 + pubkeyChild = *this; + bool ret = secp256k1_ecdsa_pubkey_tweak_add((unsigned char*)pubkeyChild.begin(), pubkeyChild.size(), out); +#else + CECKey key; + bool ret = key.SetPubKey(begin(), size()); + ret &= key.TweakPublic(out); + std::vector pubkey; + key.GetPubKey(pubkey, true); + pubkeyChild.Set(pubkey.begin(), pubkey.end()); +#endif + return ret; +} + +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); +} diff --git a/src/pubkey.h b/src/pubkey.h new file mode 100644 index 000000000..ccf967345 --- /dev/null +++ b/src/pubkey.h @@ -0,0 +1,206 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// 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. + +#ifndef BITCOIN_PUBKEY_H +#define BITCOIN_PUBKEY_H + +#include "hash.h" +#include "serialize.h" +#include "uint256.h" + +#include +#include + +/** + * secp256k1: + * const unsigned int PRIVATE_KEY_SIZE = 279; + * const unsigned int PUBLIC_KEY_SIZE = 65; + * const unsigned int SIGNATURE_SIZE = 72; + * + * see www.keylength.com + * script supports up to 75 for single byte push + */ + +/** A reference to a CKey: the Hash160 of its serialized public key */ +class CKeyID : public uint160 +{ +public: + CKeyID() : uint160(0) {} + CKeyID(const uint160& in) : uint160(in) {} +}; + +/** 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; + } + + //! Set this key data to be invalid + void Invalidate() + { + vch[0] = 0xFF; + } + +public: + //! Construct an invalid public key. + CPubKey() + { + Invalidate(); + } + + //! Initialize a public key using begin/end iterators to byte data. + template + 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(); + } + + //! Construct a public key using begin/end iterators to byte data. + template + CPubKey(const T pbegin, const T pend) + { + Set(pbegin, pend); + } + + //! Construct a public key from a byte vector. + CPubKey(const std::vector& vch) + { + Set(vch.begin(), vch.end()); + } + + //! 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]; } + + //! 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; + } + 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); + } + + //! Implement serialization, as if this was a byte vector. + unsigned int GetSerializeSize(int nType, int nVersion) const + { + return size() + 1; + } + template + void Serialize(Stream& s, int nType, int nVersion) const + { + unsigned int len = size(); + ::WriteCompactSize(s, len); + s.write((char*)vch, len); + } + template + 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(); + } + } + + //! Get the KeyID of this public key (hash of its serialization) + CKeyID GetID() const + { + return CKeyID(Hash160(vch, vch + size())); + } + + //! Get the 256-bit hash of this public key. + uint256 GetHash() const + { + return Hash(vch, vch + size()); + } + + /* + * Check syntactic correctness. + * + * Note that this is consensus critical as CheckSig() calls it! + */ + bool IsValid() const + { + return size() > 0; + } + + //! 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; + } + + /** + * 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& vchSig) const; + + //! Recover a public key from a compact signature. + bool RecoverCompact(const uint256& hash, const std::vector& vchSig); + + //! Turn this public key into an uncompressed public key. + bool Decompress(); + + //! Derive BIP32 child pubkey. + bool Derive(CPubKey& pubkeyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const; +}; + +struct CExtPubKey { + unsigned char nDepth; + unsigned char vchFingerprint[4]; + unsigned int nChild; + unsigned char vchChainCode[32]; + CPubKey pubkey; + + friend bool operator==(const CExtPubKey& a, const CExtPubKey& b) + { + return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], 4) == 0 && a.nChild == b.nChild && + memcmp(&a.vchChainCode[0], &b.vchChainCode[0], 32) == 0 && a.pubkey == b.pubkey; + } + + void Encode(unsigned char code[74]) const; + void Decode(const unsigned char code[74]); + bool Derive(CExtPubKey& out, unsigned int nChild) const; +}; + +#endif // BITCOIN_PUBKEY_H diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index e1e242882..54c2847f7 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -10,7 +10,7 @@ #include "crypto/sha1.h" #include "crypto/sha2.h" #include "eccryptoverify.h" -#include "key.h" +#include "pubkey.h" #include "script/script.h" #include "uint256.h" #include "util.h" diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index ab366898d..d76a5acd6 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -5,7 +5,7 @@ #include "sigcache.h" -#include "key.h" +#include "pubkey.h" #include "random.h" #include "uint256.h" #include "util.h" diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 05938961b..9cae0508e 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -5,6 +5,7 @@ #include "script/standard.h" +#include "pubkey.h" #include "script/script.h" #include "util.h" #include "utilstrencodings.h" diff --git a/src/script/standard.h b/src/script/standard.h index 248b941a6..171178ce3 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -6,8 +6,7 @@ #ifndef H_BITCOIN_SCRIPT_STANDARD #define H_BITCOIN_SCRIPT_STANDARD -#include "key.h" -#include "script/script.h" +#include "uint256.h" #include "script/interpreter.h" #include @@ -15,6 +14,7 @@ #include class CScript; +class CKeyID; /** A reference to a CScript: the Hash160 of its serialization (see script.h) */ class CScriptID : public uint160 diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 93b7fe189..fa10ca129 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -4,6 +4,7 @@ #include "main.h" #include "miner.h" +#include "pubkey.h" #include "uint256.h" #include "util.h" diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index 7b27703b6..5bf0862c7 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "pubkey.h" #include "key.h" #include "script/script.h" #include "script/standard.h" diff --git a/src/wallet_ismine.cpp b/src/wallet_ismine.cpp index 07149ebd0..05dc40aae 100644 --- a/src/wallet_ismine.cpp +++ b/src/wallet_ismine.cpp @@ -7,6 +7,7 @@ #include "key.h" #include "keystore.h" +#include "script/script.h" #include "script/standard.h" #include