diff options
-rw-r--r-- | src/base58.h | 72 | ||||
-rw-r--r-- | src/bitcoinrpc.cpp | 2 | ||||
-rw-r--r-- | src/chainparams.cpp | 25 | ||||
-rw-r--r-- | src/chainparams.h | 10 | ||||
-rw-r--r-- | src/hash.cpp | 41 | ||||
-rw-r--r-- | src/hash.h | 10 | ||||
-rw-r--r-- | src/init.cpp | 2 | ||||
-rw-r--r-- | src/key.cpp | 199 | ||||
-rw-r--r-- | src/key.h | 46 | ||||
-rw-r--r-- | src/leveldb.cpp | 10 | ||||
-rw-r--r-- | src/main.cpp | 62 | ||||
-rw-r--r-- | src/main.h | 2 | ||||
-rw-r--r-- | src/script.cpp | 26 | ||||
-rw-r--r-- | src/script.h | 11 | ||||
-rw-r--r-- | src/test/bip32_tests.cpp | 116 | ||||
-rw-r--r-- | src/test/canonical_tests.cpp | 4 | ||||
-rw-r--r-- | src/test/data/script_valid.json | 9 | ||||
-rw-r--r-- | src/test/hmac_tests.cpp | 125 | ||||
-rw-r--r-- | src/test/util_tests.cpp | 11 | ||||
-rw-r--r-- | src/util.h | 15 |
20 files changed, 729 insertions, 69 deletions
diff --git a/src/base58.h b/src/base58.h index 630d6fe9aa..aabae8de88 100644 --- a/src/base58.h +++ b/src/base58.h @@ -177,8 +177,8 @@ inline bool DecodeBase58Check(const std::string& str, std::vector<unsigned char> class CBase58Data { protected: - // the version byte - unsigned char nVersion; + // the version byte(s) + std::vector<unsigned char> vchVersion; // the actually encoded data typedef std::vector<unsigned char, zero_after_free_allocator<unsigned char> > vector_uchar; @@ -186,38 +186,38 @@ protected: CBase58Data() { - nVersion = 0; + vchVersion.clear(); vchData.clear(); } - void SetData(int nVersionIn, const void* pdata, size_t nSize) + void SetData(const std::vector<unsigned char> &vchVersionIn, const void* pdata, size_t nSize) { - nVersion = nVersionIn; + vchVersion = vchVersionIn; vchData.resize(nSize); if (!vchData.empty()) memcpy(&vchData[0], pdata, nSize); } - void SetData(int nVersionIn, const unsigned char *pbegin, const unsigned char *pend) + void SetData(const std::vector<unsigned char> &vchVersionIn, const unsigned char *pbegin, const unsigned char *pend) { - SetData(nVersionIn, (void*)pbegin, pend - pbegin); + SetData(vchVersionIn, (void*)pbegin, pend - pbegin); } public: - bool SetString(const char* psz) + bool SetString(const char* psz, unsigned int nVersionBytes = 1) { std::vector<unsigned char> vchTemp; DecodeBase58Check(psz, vchTemp); - if (vchTemp.empty()) + if (vchTemp.size() < nVersionBytes) { vchData.clear(); - nVersion = 0; + vchVersion.clear(); return false; } - nVersion = vchTemp[0]; - vchData.resize(vchTemp.size() - 1); + vchVersion.assign(vchTemp.begin(), vchTemp.begin() + nVersionBytes); + vchData.resize(vchTemp.size() - nVersionBytes); if (!vchData.empty()) - memcpy(&vchData[0], &vchTemp[1], vchData.size()); + memcpy(&vchData[0], &vchTemp[nVersionBytes], vchData.size()); OPENSSL_cleanse(&vchTemp[0], vchData.size()); return true; } @@ -229,15 +229,15 @@ public: std::string ToString() const { - std::vector<unsigned char> vch(1, nVersion); + std::vector<unsigned char> vch = vchVersion; vch.insert(vch.end(), vchData.begin(), vchData.end()); return EncodeBase58Check(vch); } int CompareTo(const CBase58Data& b58) const { - if (nVersion < b58.nVersion) return -1; - if (nVersion > b58.nVersion) return 1; + if (vchVersion < b58.vchVersion) return -1; + if (vchVersion > b58.vchVersion) return 1; if (vchData < b58.vchData) return -1; if (vchData > b58.vchData) return 1; return 0; @@ -289,8 +289,8 @@ public: bool IsValid() const { bool fCorrectSize = vchData.size() == 20; - bool fKnownVersion = nVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS) || - nVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); + bool fKnownVersion = vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS) || + vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); return fCorrectSize && fKnownVersion; } @@ -318,16 +318,16 @@ public: return CNoDestination(); uint160 id; memcpy(&id, &vchData[0], 20); - if (nVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) + if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) return CKeyID(id); - else if (nVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) + else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) return CScriptID(id); else return CNoDestination(); } bool GetKeyID(CKeyID &keyID) const { - if (!IsValid() || nVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) + if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) return false; uint160 id; memcpy(&id, &vchData[0], 20); @@ -336,7 +336,7 @@ public: } bool IsScript() const { - return IsValid() && nVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); + return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); } }; @@ -366,7 +366,7 @@ public: bool IsValid() const { bool fExpectedFormat = vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1); - bool fCorrectVersion = nVersion == Params().Base58Prefix(CChainParams::SECRET_KEY); + bool fCorrectVersion = vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY); return fExpectedFormat && fCorrectVersion; } @@ -390,4 +390,30 @@ public: } }; + +template<typename K, int Size, CChainParams::Base58Type Type> class CBitcoinExtKeyBase : public CBase58Data +{ +public: + void SetKey(const K &key) { + unsigned char vch[Size]; + key.Encode(vch); + SetData(Params().Base58Prefix(Type), vch, vch+Size); + } + + K GetKey() { + K ret; + ret.Decode(&vchData[0], &vchData[Size]); + return ret; + } + + CBitcoinExtKeyBase(const K &key) { + SetKey(key); + } + + CBitcoinExtKeyBase() {} +}; + +typedef CBitcoinExtKeyBase<CExtKey, 74, CChainParams::EXT_SECRET_KEY> CBitcoinExtKey; +typedef CBitcoinExtKeyBase<CExtPubKey, 74, CChainParams::EXT_PUBLIC_KEY> CBitcoinExtPubKey; + #endif // BITCOIN_BASE58_H diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index a14f5e7060..d22809ce69 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -476,7 +476,7 @@ bool HTTPAuthorized(map<string, string>& mapHeaders) return false; string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); string strUserPass = DecodeBase64(strUserPass64); - return strUserPass == strRPCUserColonPass; + return TimingResistantEqual(strUserPass, strRPCUserColonPass); } // diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 6b83624040..0795f09765 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -10,6 +10,10 @@ #include "protocol.h" #include "util.h" +#include <boost/assign/list_of.hpp> + +using namespace boost::assign; + // // Main network // @@ -141,9 +145,11 @@ public: vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org")); vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org")); - base58Prefixes[PUBKEY_ADDRESS] = 0; - base58Prefixes[SCRIPT_ADDRESS] = 5; - base58Prefixes[SECRET_KEY] = 128; + base58Prefixes[PUBKEY_ADDRESS] = list_of(0); + base58Prefixes[SCRIPT_ADDRESS] = list_of(5); + base58Prefixes[SECRET_KEY] = list_of(128); + base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x88)(0xB2)(0x1E); + base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x88)(0xAD)(0xE4); // Convert the pnSeeds array into usable address objects. for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++) @@ -203,10 +209,11 @@ public: vSeeds.push_back(CDNSSeedData("bitcoin.petertodd.org", "testnet-seed.bitcoin.petertodd.org")); vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me")); - base58Prefixes[PUBKEY_ADDRESS] = 111; - base58Prefixes[SCRIPT_ADDRESS] = 196; - base58Prefixes[SECRET_KEY] = 239; - + base58Prefixes[PUBKEY_ADDRESS] = list_of(111); + base58Prefixes[SCRIPT_ADDRESS] = list_of(196); + base58Prefixes[SECRET_KEY] = list_of(239); + base58Prefixes[EXT_PUBLIC_KEY] = list_of(0x04)(0x35)(0x87)(0xCF); + base58Prefixes[EXT_SECRET_KEY] = list_of(0x04)(0x35)(0x83)(0x94); } virtual Network NetworkID() const { return CChainParams::TESTNET; } }; @@ -234,10 +241,6 @@ public: assert(hashGenesisBlock == uint256("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); vSeeds.clear(); // Regtest mode doesn't have any DNS seeds. - - base58Prefixes[PUBKEY_ADDRESS] = 0; - base58Prefixes[SCRIPT_ADDRESS] = 5; - base58Prefixes[SECRET_KEY] = 128; } virtual bool RequireRPCPassword() const { return false; } diff --git a/src/chainparams.h b/src/chainparams.h index 572712b589..0dac79aed4 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -45,6 +45,8 @@ public: PUBKEY_ADDRESS, SCRIPT_ADDRESS, SECRET_KEY, + EXT_PUBLIC_KEY, + EXT_SECRET_KEY, MAX_BASE58_TYPES }; @@ -60,7 +62,7 @@ public: const string& DataDir() const { return strDataDir; } virtual Network NetworkID() const = 0; const vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; } - int Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } + const std::vector<unsigned char> &Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } virtual const vector<CAddress>& FixedSeeds() const = 0; int RPCPort() const { return nRPCPort; } protected: @@ -76,7 +78,7 @@ protected: int nSubsidyHalvingInterval; string strDataDir; vector<CDNSSeedData> vSeeds; - int base58Prefixes[MAX_BASE58_TYPES]; + std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; }; /** @@ -99,4 +101,8 @@ inline bool TestNet() { return Params().NetworkID() == CChainParams::TESTNET; } +inline bool RegTest() { + return Params().NetworkID() == CChainParams::REGTEST; +} + #endif diff --git a/src/hash.cpp b/src/hash.cpp index bddd8abf38..7b054bd154 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -56,3 +56,44 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char return h1; } + +int HMAC_SHA512_Init(HMAC_SHA512_CTX *pctx, const void *pkey, size_t len) +{ + unsigned char key[128]; + if (len <= 128) + { + memcpy(key, pkey, len); + memset(key + len, 0, 128-len); + } + else + { + SHA512_CTX ctxKey; + SHA512_Init(&ctxKey); + SHA512_Update(&ctxKey, pkey, len); + SHA512_Final(key, &ctxKey); + memset(key + 64, 0, 64); + } + + for (int n=0; n<128; n++) + key[n] ^= 0x5c; + SHA512_Init(&pctx->ctxOuter); + SHA512_Update(&pctx->ctxOuter, key, 128); + + for (int n=0; n<128; n++) + key[n] ^= 0x5c ^ 0x36; + SHA512_Init(&pctx->ctxInner); + return SHA512_Update(&pctx->ctxInner, key, 128); +} + +int HMAC_SHA512_Update(HMAC_SHA512_CTX *pctx, const void *pdata, size_t len) +{ + return SHA512_Update(&pctx->ctxInner, pdata, len); +} + +int HMAC_SHA512_Final(unsigned char *pmd, HMAC_SHA512_CTX *pctx) +{ + unsigned char buf[64]; + SHA512_Final(buf, &pctx->ctxInner); + SHA512_Update(&pctx->ctxOuter, buf, 64); + return SHA512_Final(pmd, &pctx->ctxOuter); +} diff --git a/src/hash.h b/src/hash.h index 536ab71165..880468a2d2 100644 --- a/src/hash.h +++ b/src/hash.h @@ -123,4 +123,14 @@ inline uint160 Hash160(const std::vector<unsigned char>& vch) unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char>& vDataToHash); +typedef struct +{ + SHA512_CTX ctxInner; + SHA512_CTX ctxOuter; +} HMAC_SHA512_CTX; + +int HMAC_SHA512_Init(HMAC_SHA512_CTX *pctx, const void *pkey, size_t len); +int HMAC_SHA512_Update(HMAC_SHA512_CTX *pctx, const void *pdata, size_t len); +int HMAC_SHA512_Final(unsigned char *pmd, HMAC_SHA512_CTX *pctx); + #endif diff --git a/src/init.cpp b/src/init.cpp index 09871c012c..7182c14146 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -424,6 +424,7 @@ bool AppInit2(boost::thread_group& threadGroup) fDebug = GetBoolArg("-debug", false); fBenchmark = GetBoolArg("-benchmark", false); + mempool.fChecks = GetBoolArg("-checkmempool", RegTest()); // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency nScriptCheckThreads = GetArg("-par", 0); @@ -777,6 +778,7 @@ bool AppInit2(boost::thread_group& threadGroup) break; } } catch(std::exception &e) { + if (fDebug) printf("%s\n", e.what()); strLoadError = _("Error opening block database"); break; } diff --git a/src/key.cpp b/src/key.cpp index 1ab4c62ebf..85dc9cda2b 100644 --- a/src/key.cpp +++ b/src/key.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 <openssl/bn.h> #include <openssl/ecdsa.h> #include <openssl/rand.h> #include <openssl/obj_mac.h> @@ -194,9 +195,26 @@ public: } bool Sign(const uint256 &hash, std::vector<unsigned char>& vchSig) { + vchSig.clear(); + ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)&hash, sizeof(hash), pkey); + if (sig == NULL) + return false; + if (BN_is_odd(sig->s)) { + // enforce even S values, by negating the value (modulo the order) if odd + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + const EC_GROUP *group = EC_KEY_get0_group(pkey); + BIGNUM *order = BN_CTX_get(ctx); + EC_GROUP_get_order(group, order, ctx); + BN_sub(sig->s, order, sig->s); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + } unsigned int nSize = ECDSA_size(pkey); vchSig.resize(nSize); // Make sure it is big enough - assert(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 return true; } @@ -254,6 +272,57 @@ public: ECDSA_SIG_free(sig); return ret; } + + static bool TweakSecret(unsigned char vchSecretOut[32], const unsigned char vchSecretIn[32], const unsigned char vchTweak[32]) + { + bool ret = true; + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + BIGNUM *bnSecret = BN_CTX_get(ctx); + BIGNUM *bnTweak = BN_CTX_get(ctx); + BIGNUM *bnOrder = BN_CTX_get(ctx); + EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp256k1); + EC_GROUP_get_order(group, bnOrder, ctx); // what a grossly inefficient way to get the (constant) group order... + BN_bin2bn(vchTweak, 32, bnTweak); + if (BN_cmp(bnTweak, bnOrder) >= 0) + ret = false; // extremely unlikely + BN_bin2bn(vchSecretIn, 32, bnSecret); + BN_add(bnSecret, bnSecret, bnTweak); + BN_nnmod(bnSecret, bnSecret, bnOrder, ctx); + if (BN_is_zero(bnSecret)) + ret = false; // ridiculously unlikely + int nBits = BN_num_bits(bnSecret); + memset(vchSecretOut, 0, 32); + BN_bn2bin(bnSecret, &vchSecretOut[32-(nBits+7)/8]); + EC_GROUP_free(group); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ret; + } + + bool TweakPublic(const unsigned char vchTweak[32]) { + bool ret = true; + BN_CTX *ctx = BN_CTX_new(); + BN_CTX_start(ctx); + BIGNUM *bnTweak = BN_CTX_get(ctx); + BIGNUM *bnOrder = BN_CTX_get(ctx); + BIGNUM *bnOne = BN_CTX_get(ctx); + const EC_GROUP *group = EC_KEY_get0_group(pkey); + EC_GROUP_get_order(group, bnOrder, ctx); // what a grossly inefficient way to get the (constant) group order... + BN_bin2bn(vchTweak, 32, bnTweak); + if (BN_cmp(bnTweak, bnOrder) >= 0) + ret = false; // extremely unlikely + EC_POINT *point = EC_POINT_dup(EC_KEY_get0_public_key(pkey), group); + BN_one(bnOne); + EC_POINT_mul(group, point, bnTweak, point, bnOne, ctx); + if (EC_POINT_is_at_infinity(group, point)) + ret = false; // ridiculously unlikely + EC_KEY_set_public_key(pkey, point); + EC_POINT_free(point); + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ret; + } }; }; // end of anonymous namespace @@ -394,3 +463,131 @@ bool CPubKey::Decompress() { key.GetPubKey(*this, false); 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; + HMAC_SHA512_CTX ctx; + HMAC_SHA512_Init(&ctx, chainCode, 32); + HMAC_SHA512_Update(&ctx, &header, 1); + HMAC_SHA512_Update(&ctx, data, 32); + HMAC_SHA512_Update(&ctx, num, 4); + HMAC_SHA512_Final(output, &ctx); +} + +bool CKey::Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const { + assert(IsValid()); + assert(IsCompressed()); + unsigned char out[64]; + LockObject(out); + if ((nChild >> 31) == 0) { + CPubKey pubkey = GetPubKey(); + assert(pubkey.begin() + 33 == pubkey.end()); + BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, out); + } else { + assert(begin() + 32 == end()); + BIP32Hash(cc, nChild, 0, begin(), out); + } + memcpy(ccChild, out+32, 32); + bool ret = CECKey::TweakSecret((unsigned char*)keyChild.begin(), begin(), out); + UnlockObject(out); + keyChild.fCompressed = true; + keyChild.fValid = ret; + 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); + CECKey key; + bool ret = key.SetPubKey(*this); + ret &= key.TweakPublic(out); + key.GetPubKey(pubkeyChild, true); + return ret; +} + +bool CExtKey::Derive(CExtKey &out, unsigned int nChild) const { + out.nDepth = nDepth + 1; + CKeyID id = key.GetPubKey().GetID(); + memcpy(&out.vchFingerprint[0], &id, 4); + out.nChild = nChild; + return key.Derive(out.key, out.vchChainCode, nChild, vchChainCode); +} + +void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) { + static const char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; + HMAC_SHA512_CTX ctx; + HMAC_SHA512_Init(&ctx, hashkey, sizeof(hashkey)); + HMAC_SHA512_Update(&ctx, seed, nSeedLen); + unsigned char out[64]; + LockObject(out); + HMAC_SHA512_Final(out, &ctx); + key.Set(&out[0], &out[32], true); + memcpy(vchChainCode, &out[32], 32); + UnlockObject(out); + nDepth = 0; + nChild = 0; + memset(vchFingerprint, 0, sizeof(vchFingerprint)); +} + +CExtPubKey CExtKey::Neuter() const { + CExtPubKey ret; + ret.nDepth = nDepth; + memcpy(&ret.vchFingerprint[0], &vchFingerprint[0], 4); + ret.nChild = nChild; + ret.pubkey = key.GetPubKey(); + memcpy(&ret.vchChainCode[0], &vchChainCode[0], 32); + return ret; +} + +void CExtKey::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); + code[41] = 0; + assert(key.size() == 32); + memcpy(code+42, key.begin(), 32); +} + +void CExtKey::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); + 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); +} @@ -161,6 +161,9 @@ public: // 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; }; @@ -201,6 +204,10 @@ public: UnlockObject(vch); } + friend bool operator==(const CKey &a, const CKey &b) { + return a.fCompressed == b.fCompressed && memcmp(&a.vch[0], &b.vch[0], 32); + } + // Initialize using begin and end iterators to byte data. template<typename T> void Set(const T pbegin, const T pend, bool fCompressedIn) { @@ -251,6 +258,45 @@ public: // 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; + + // Derive BIP32 child key. + bool Derive(CKey& keyChild, 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; +}; + +struct CExtKey { + unsigned char nDepth; + unsigned char vchFingerprint[4]; + unsigned int nChild; + unsigned char vchChainCode[32]; + CKey key; + + friend bool operator==(const CExtKey &a, const CExtKey &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.key == b.key; + } + + void Encode(unsigned char code[74]) const; + void Decode(const unsigned char code[74]); + bool Derive(CExtKey &out, unsigned int nChild) const; + CExtPubKey Neuter() const; + void SetMaster(const unsigned char *seed, unsigned int nSeedLen); }; #endif diff --git a/src/leveldb.cpp b/src/leveldb.cpp index e66f8514a7..5e3fa08f5e 100644 --- a/src/leveldb.cpp +++ b/src/leveldb.cpp @@ -15,6 +15,7 @@ void HandleError(const leveldb::Status &status) throw(leveldb_error) { if (status.ok()) return; + printf("%s\n", status.ToString().c_str()); if (status.IsCorruption()) throw leveldb_error("Database corrupted"); if (status.IsIOError()) @@ -54,8 +55,7 @@ CLevelDB::CLevelDB(const boost::filesystem::path &path, size_t nCacheSize, bool printf("Opening LevelDB in %s\n", path.string().c_str()); } leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb); - if (!status.ok()) - throw std::runtime_error(strprintf("CLevelDB(): error opening database environment %s", status.ToString().c_str())); + HandleError(status); printf("Opened LevelDB successfully\n"); } @@ -72,10 +72,6 @@ CLevelDB::~CLevelDB() { bool CLevelDB::WriteBatch(CLevelDBBatch &batch, bool fSync) throw(leveldb_error) { leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch); - if (!status.ok()) { - printf("LevelDB write failure: %s\n", status.ToString().c_str()); - HandleError(status); - return false; - } + HandleError(status); return true; } diff --git a/src/main.cpp b/src/main.cpp index e78e055147..2ccd5131d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -973,15 +973,15 @@ bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) { LOCK(cs); uint256 hash = tx.GetHash(); + if (fRecursive) { + for (unsigned int i = 0; i < tx.vout.size(); i++) { + std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i)); + if (it != mapNextTx.end()) + remove(*it->second.ptx, true); + } + } if (mapTx.count(hash)) { - if (fRecursive) { - for (unsigned int i = 0; i < tx.vout.size(); i++) { - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i)); - if (it != mapNextTx.end()) - remove(*it->second.ptx, true); - } - } BOOST_FOREACH(const CTxIn& txin, tx.vin) mapNextTx.erase(txin.prevout); mapTx.erase(hash); @@ -1014,6 +1014,45 @@ void CTxMemPool::clear() ++nTransactionsUpdated; } +bool CTxMemPool::fChecks = false; + +void CTxMemPool::check(CCoinsViewCache *pcoins) const +{ + if (!fChecks) + return; + + printf("Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); + + LOCK(cs); + for (std::map<uint256, CTransaction>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + unsigned int i = 0; + BOOST_FOREACH(const CTxIn &txin, it->second.vin) { + // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. + std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(txin.prevout.hash); + if (it2 != mapTx.end()) { + assert(it2->second.vout.size() > txin.prevout.n && !it2->second.vout[txin.prevout.n].IsNull()); + } else { + CCoins &coins = pcoins->GetCoins(txin.prevout.hash); + assert(coins.IsAvailable(txin.prevout.n)); + } + // Check whether its inputs are marked in mapNextTx. + std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout); + assert(it3 != mapNextTx.end()); + assert(it3->second.ptx == &it->second); + assert(it3->second.n == i); + i++; + } + } + for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) { + uint256 hash = it->second.ptx->GetHash(); + std::map<uint256, CTransaction>::const_iterator it2 = mapTx.find(hash); + assert(it2 != mapTx.end()); + assert(&it2->second == it->second.ptx); + assert(it2->second.vin.size() > it->second.n); + assert(it->first == it->second.ptx->vin[it->second.n].prevout); + } +} + void CTxMemPool::queryHashes(std::vector<uint256>& vtxid) { vtxid.clear(); @@ -1970,6 +2009,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) { + mempool.check(pcoinsTip); + // All modifications to the coin state will be done in this cache. // Only when all have succeeded, we push it to pcoinsTip. CCoinsViewCache view(*pcoinsTip, true); @@ -2083,7 +2124,8 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) BOOST_FOREACH(CTransaction& tx, vResurrect) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - mempool.accept(stateDummy, tx, false, NULL); + if (!mempool.accept(stateDummy, tx, false, NULL)) + mempool.remove(tx, true); } // Delete redundant memory transactions that are in the connected branch @@ -2092,6 +2134,8 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) mempool.removeConflicts(tx); } + mempool.check(pcoinsTip); + // Update best block in wallet (so we can detect restored wallets) if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) { @@ -3677,6 +3721,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CValidationState state; if (mempool.accept(state, tx, true, &fMissingInputs)) { + mempool.check(pcoinsTip); RelayTransaction(tx, inv.hash); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -3712,6 +3757,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vEraseQueue.push_back(orphanHash); printf(" removed orphan tx %s\n", orphanHash.ToString().c_str()); } + mempool.check(pcoinsTip); } } diff --git a/src/main.h b/src/main.h index ea86a2bcc0..a690a2bc9c 100644 --- a/src/main.h +++ b/src/main.h @@ -1077,6 +1077,7 @@ public: class CTxMemPool { public: + static bool fChecks; mutable CCriticalSection cs; std::map<uint256, CTransaction> mapTx; std::map<COutPoint, CInPoint> mapNextTx; @@ -1088,6 +1089,7 @@ public: void clear(); void queryHashes(std::vector<uint256>& vtxid); void pruneSpent(const uint256& hash, CCoins &coins); + void check(CCoinsViewCache *pcoins) const; unsigned long size() { diff --git a/src/script.cpp b/src/script.cpp index 5699fbfb6a..2df2e9f0d5 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -227,7 +227,10 @@ const char* GetOpName(opcodetype opcode) } } -bool IsCanonicalPubKey(const valtype &vchPubKey) { +bool IsCanonicalPubKey(const valtype &vchPubKey, unsigned int flags) { + if (!(flags & SCRIPT_VERIFY_STRICTENC)) + return true; + if (vchPubKey.size() < 33) return error("Non-canonical public key: too short"); if (vchPubKey[0] == 0x04) { @@ -242,7 +245,10 @@ bool IsCanonicalPubKey(const valtype &vchPubKey) { return true; } -bool IsCanonicalSignature(const valtype &vchSig) { +bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { + if (!(flags & SCRIPT_VERIFY_STRICTENC)) + return true; + // See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 // A canonical signature exists of: <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype> // Where R and S are not negative (their first byte has its highest bit not set), and not @@ -286,6 +292,11 @@ bool IsCanonicalSignature(const valtype &vchSig) { if (nLenS > 1 && (S[0] == 0x00) && !(S[1] & 0x80)) return error("Non-canonical signature: S value excessively padded"); + if (flags & SCRIPT_VERIFY_EVEN_S) { + if (S[nLenS-1] & 1) + return error("Non-canonical signature: S value odd"); + } + return true; } @@ -302,7 +313,6 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co if (script.size() > 10000) return false; int nOpCount = 0; - bool fStrictEncodings = flags & SCRIPT_VERIFY_STRICTENC; try { @@ -841,9 +851,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co // Drop the signature, since there's no way for a signature to sign itself scriptCode.FindAndDelete(CScript(vchSig)); - bool fSuccess = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); - if (fSuccess) - fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); + bool fSuccess = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && + CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); popstack(stack); popstack(stack); @@ -903,9 +912,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co valtype& vchPubKey = stacktop(-ikey); // Check signature - bool fOk = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey))); - if (fOk) - fOk = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); + bool fOk = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && + CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); if (fOk) { isig++; diff --git a/src/script.h b/src/script.h index 03afe8b652..fd5b12921e 100644 --- a/src/script.h +++ b/src/script.h @@ -32,9 +32,10 @@ enum enum { SCRIPT_VERIFY_NONE = 0, - SCRIPT_VERIFY_P2SH = (1U << 0), - SCRIPT_VERIFY_STRICTENC = (1U << 1), - SCRIPT_VERIFY_NOCACHE = (1U << 2), + SCRIPT_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts + SCRIPT_VERIFY_STRICTENC = (1U << 1), // enforce strict conformance to DER and SEC2 for signatures and pubkeys + SCRIPT_VERIFY_EVEN_S = (1U << 2), // enforce even S values in signatures (depends on STRICTENC) + SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it) }; enum txnouttype @@ -665,8 +666,8 @@ public: } }; -bool IsCanonicalPubKey(const std::vector<unsigned char> &vchPubKey); -bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig); +bool IsCanonicalPubKey(const std::vector<unsigned char> &vchPubKey, unsigned int flags); +bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig, unsigned int flags); bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp new file mode 100644 index 0000000000..66d2330fbf --- /dev/null +++ b/src/test/bip32_tests.cpp @@ -0,0 +1,116 @@ +#include <boost/test/unit_test.hpp> + +#include <string> +#include <vector> + +#include "key.h" +#include "base58.h" +#include "uint256.h" +#include "util.h" + +struct TestDerivation { + std::string pub; + std::string prv; + unsigned int nChild; +}; + +struct TestVector { + std::string strHexMaster; + std::vector<TestDerivation> vDerive; + + TestVector(std::string strHexMasterIn) : strHexMaster(strHexMasterIn) {} + + TestVector& operator()(std::string pub, std::string prv, unsigned int nChild) { + vDerive.push_back(TestDerivation()); + TestDerivation &der = vDerive.back(); + der.pub = pub; + der.prv = prv; + der.nChild = nChild; + return *this; + } +}; + +TestVector test1 = + TestVector("000102030405060708090a0b0c0d0e0f") + ("xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", + "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", + 0x80000000) + ("xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", + "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", + 1) + ("xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ", + "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", + 0x80000002) + ("xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", + "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", + 2) + ("xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV", + "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", + 1000000000) + ("xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy", + "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", + 0); + +TestVector test2 = + TestVector("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542") + ("xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", + 0) + ("xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", + "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", + 0xFFFFFFFF) + ("xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a", + "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", + 1) + ("xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon", + "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", + 0xFFFFFFFE) + ("xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", + "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", + 2) + ("xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", + "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", + 0); + +void RunTest(const TestVector &test) { + std::vector<unsigned char> seed = ParseHex(test.strHexMaster); + CExtKey key; + CExtPubKey pubkey; + key.SetMaster(&seed[0], seed.size()); + pubkey = key.Neuter(); + BOOST_FOREACH(const TestDerivation &derive, test.vDerive) { + unsigned char data[74]; + key.Encode(data); + pubkey.Encode(data); + // Test private key + CBitcoinExtKey b58key; b58key.SetKey(key); + BOOST_CHECK(b58key.ToString() == derive.prv); + // Test public key + CBitcoinExtPubKey b58pubkey; b58pubkey.SetKey(pubkey); + BOOST_CHECK(b58pubkey.ToString() == derive.pub); + // Derive new keys + CExtKey keyNew; + BOOST_CHECK(key.Derive(keyNew, derive.nChild)); + CExtPubKey pubkeyNew = keyNew.Neuter(); + if (!(derive.nChild & 0x80000000)) { + // Compare with public derivation + CExtPubKey pubkeyNew2; + BOOST_CHECK(pubkey.Derive(pubkeyNew2, derive.nChild)); + BOOST_CHECK(pubkeyNew == pubkeyNew2); + } + key = keyNew; + pubkey = pubkeyNew; + } +} + +BOOST_AUTO_TEST_SUITE(bip32_tests) + +BOOST_AUTO_TEST_CASE(bip32_test1) { + RunTest(test1); +} + +BOOST_AUTO_TEST_CASE(bip32_test2) { + RunTest(test2); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/canonical_tests.cpp b/src/test/canonical_tests.cpp index 42d21f8ac5..09988da259 100644 --- a/src/test/canonical_tests.cpp +++ b/src/test/canonical_tests.cpp @@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(script_canon) string test = tv.get_str(); if (IsHex(test)) { std::vector<unsigned char> sig = ParseHex(test); - BOOST_CHECK_MESSAGE(IsCanonicalSignature(sig), test); + BOOST_CHECK_MESSAGE(IsCanonicalSignature(sig, SCRIPT_VERIFY_STRICTENC), test); BOOST_CHECK_MESSAGE(IsCanonicalSignature_OpenSSL(sig), test); } } @@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(script_noncanon) string test = tv.get_str(); if (IsHex(test)) { std::vector<unsigned char> sig = ParseHex(test); - BOOST_CHECK_MESSAGE(!IsCanonicalSignature(sig), test); + BOOST_CHECK_MESSAGE(!IsCanonicalSignature(sig, SCRIPT_VERIFY_STRICTENC), test); BOOST_CHECK_MESSAGE(!IsCanonicalSignature_OpenSSL(sig), test); } } diff --git a/src/test/data/script_valid.json b/src/test/data/script_valid.json index cc56a310ae..7aefc8e6e8 100644 --- a/src/test/data/script_valid.json +++ b/src/test/data/script_valid.json @@ -316,6 +316,15 @@ ["2147483647", "1ADD 1"], ["-2147483647", "1ADD 1"], +["1", "0x02 0x0100 EQUAL NOT", "Not the same byte array..."], +["1", "0x02 0x0100 NUMEQUAL", "... but they are numerically equal"], +["11", "0x4c 0x03 0x0b0000 NUMEQUAL"], +["0", "0x01 0x80 EQUAL NOT"], +["0", "0x01 0x80 NUMEQUAL", "Zero numerically equals negative zero"], +["0", "0x02 0x0080 NUMEQUAL"], +["0x03 0x000080", "0x04 0x00000080 NUMEQUAL"], +["0x03 0x100080", "0x04 0x10000080 NUMEQUAL"], +["0x03 0x100000", "0x04 0x10000000 NUMEQUAL"], ["NOP", "NOP 1", "The following tests check the if(stack.size() < N) tests in each opcode"], ["1", "IF 1 ENDIF", "They are here to catch copy-and-paste errors"], diff --git a/src/test/hmac_tests.cpp b/src/test/hmac_tests.cpp new file mode 100644 index 0000000000..92ca5e6aff --- /dev/null +++ b/src/test/hmac_tests.cpp @@ -0,0 +1,125 @@ +#include <boost/test/unit_test.hpp> + +#include "hash.h" +#include "util.h" + +using namespace std; + +BOOST_AUTO_TEST_SUITE(hmac_tests) + +typedef struct { + const char *pszKey; + const char *pszData; + const char *pszMAC; +} testvec_t; + +// test cases 1, 2, 3, 4, 6 and 7 of RFC 4231 +static const testvec_t vtest[] = { + { + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" + "0b0b0b0b", + "4869205468657265", + "87aa7cdea5ef619d4ff0b4241a1d6cb0" + "2379f4e2ce4ec2787ad0b30545e17cde" + "daa833b7d6b8a702038b274eaea3f4e4" + "be9d914eeb61f1702e696c203a126854" + }, + { + "4a656665", + "7768617420646f2079612077616e7420" + "666f72206e6f7468696e673f", + "164b7a7bfcf819e2e395fbe73b56e0a3" + "87bd64222e831fd610270cd7ea250554" + "9758bf75c05a994a6d034f65f8f0e6fd" + "caeab1a34d4a6b4b636e070a38bce737" + }, + { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaa", + "dddddddddddddddddddddddddddddddd" + "dddddddddddddddddddddddddddddddd" + "dddddddddddddddddddddddddddddddd" + "dddd", + "fa73b0089d56a284efb0f0756c890be9" + "b1b5dbdd8ee81a3655f83e33b2279d39" + "bf3e848279a722c806b485a47e67c807" + "b946a337bee8942674278859e13292fb" + }, + { + "0102030405060708090a0b0c0d0e0f10" + "111213141516171819", + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" + "cdcd", + "b0ba465637458c6990e5a8c5f61d4af7" + "e576d97ff94b872de76f8050361ee3db" + "a91ca5c11aa25eb4d679275cc5788063" + "a5f19741120c4f2de2adebeb10a298dd" + }, + { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaa", + "54657374205573696e67204c61726765" + "72205468616e20426c6f636b2d53697a" + "65204b6579202d2048617368204b6579" + "204669727374", + "80b24263c7c1a3ebb71493c1dd7be8b4" + "9b46d1f41b4aeec1121b013783f8f352" + "6b56d037e05f2598bd0fd2215d6a1e52" + "95e64f73f63f0aec8b915a985d786598" + }, + { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaa", + "54686973206973206120746573742075" + "73696e672061206c6172676572207468" + "616e20626c6f636b2d73697a65206b65" + "7920616e642061206c61726765722074" + "68616e20626c6f636b2d73697a652064" + "6174612e20546865206b6579206e6565" + "647320746f2062652068617368656420" + "6265666f7265206265696e6720757365" + "642062792074686520484d414320616c" + "676f726974686d2e", + "e37b6a775dc87dbaa4dfa9f96e5e3ffd" + "debd71f8867289865df5a32d20cdc944" + "b6022cac3c4982b10d5eeb55c3e4de15" + "134676fb6de0446065c97440fa8c6a58" + } +}; + +BOOST_AUTO_TEST_CASE(hmacsha512_testvectors) +{ + for (unsigned int n=0; n<sizeof(vtest)/sizeof(vtest[0]); n++) + { + vector<unsigned char> vchKey = ParseHex(vtest[n].pszKey); + vector<unsigned char> vchData = ParseHex(vtest[n].pszData); + vector<unsigned char> vchMAC = ParseHex(vtest[n].pszMAC); + unsigned char vchTemp[64]; + + HMAC_SHA512_CTX ctx; + HMAC_SHA512_Init(&ctx, &vchKey[0], vchKey.size()); + HMAC_SHA512_Update(&ctx, &vchData[0], vchData.size()); + HMAC_SHA512_Final(&vchTemp[0], &ctx); + + BOOST_CHECK(memcmp(&vchTemp[0], &vchMAC[0], 64) == 0); + + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index fd936517fd..abfd882655 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -291,4 +291,15 @@ BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) } } +BOOST_AUTO_TEST_CASE(util_TimingResistantEqual) +{ + BOOST_CHECK(TimingResistantEqual(std::string(""), std::string(""))); + BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string(""))); + BOOST_CHECK(!TimingResistantEqual(std::string(""), std::string("abc"))); + BOOST_CHECK(!TimingResistantEqual(std::string("a"), std::string("aa"))); + BOOST_CHECK(!TimingResistantEqual(std::string("aa"), std::string("a"))); + BOOST_CHECK(TimingResistantEqual(std::string("abc"), std::string("abc"))); + BOOST_CHECK(!TimingResistantEqual(std::string("abc"), std::string("aba"))); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/util.h b/src/util.h index 9aea564406..c9614d3055 100644 --- a/src/util.h +++ b/src/util.h @@ -437,6 +437,21 @@ static inline uint32_t insecure_rand(void) */ void seed_insecure_rand(bool fDeterministic=false); +/** + * Timing-attack-resistant comparison. + * Takes time proportional to length + * of first argument. + */ +template <typename T> +bool TimingResistantEqual(const T& a, const T& b) +{ + if (b.size() == 0) return a.size() == 0; + size_t accumulator = a.size() ^ b.size(); + for (size_t i = 0; i < a.size(); i++) + accumulator |= a[i] ^ b[i%b.size()]; + return accumulator == 0; +} + /** Median filter over a stream of values. * Returns the median of the last N numbers */ |