diff options
Diffstat (limited to 'src/script/interpreter.cpp')
| -rw-r--r-- | src/script/interpreter.cpp | 446 |
1 files changed, 300 insertions, 146 deletions
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index f9b783588..23d5b72a5 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1,21 +1,18 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2009-2020 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "interpreter.h" +#include <script/interpreter.h> -#include "primitives/transaction.h" -#include "crypto/ripemd160.h" -#include "crypto/sha1.h" -#include "crypto/sha256.h" -#include "pubkey.h" -#include "script/script.h" -#include "uint256.h" +#include <crypto/ripemd160.h> +#include <crypto/sha1.h> +#include <crypto/sha256.h> +#include <pubkey.h> +#include <script/script.h> +#include <uint256.h> -using namespace std; - -typedef vector<unsigned char> valtype; +typedef std::vector<unsigned char> valtype; namespace { @@ -33,7 +30,7 @@ inline bool set_error(ScriptError* ret, const ScriptError serror) return false; } -} // anon namespace +} // namespace bool CastToBool(const valtype& vch) { @@ -56,25 +53,25 @@ bool CastToBool(const valtype& vch) */ #define stacktop(i) (stack.at(stack.size()+(i))) #define altstacktop(i) (altstack.at(altstack.size()+(i))) -static inline void popstack(vector<valtype>& stack) +static inline void popstack(std::vector<valtype>& stack) { if (stack.empty()) - throw runtime_error("popstack(): stack empty"); + throw std::runtime_error("popstack(): stack empty"); stack.pop_back(); } bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { - if (vchPubKey.size() < 33) { + if (vchPubKey.size() < CPubKey::COMPRESSED_SIZE) { // Non-canonical public key: too short return false; } if (vchPubKey[0] == 0x04) { - if (vchPubKey.size() != 65) { + if (vchPubKey.size() != CPubKey::SIZE) { // Non-canonical public key: invalid length for uncompressed key return false; } } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { - if (vchPubKey.size() != 33) { + if (vchPubKey.size() != CPubKey::COMPRESSED_SIZE) { // Non-canonical public key: invalid length for compressed key return false; } @@ -86,7 +83,7 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { } bool static IsCompressedPubKey(const valtype &vchPubKey) { - if (vchPubKey.size() != 33) { + if (vchPubKey.size() != CPubKey::COMPRESSED_SIZE) { // Non-canonical public key: invalid length for compressed key return false; } @@ -102,7 +99,7 @@ bool static IsCompressedPubKey(const valtype &vchPubKey) { * Where R and S are not negative (their first byte has its highest bit not set), and not * excessively padded (do not start with a 0 byte, unless an otherwise negative number follows, * in which case a single 0 byte is necessary and even required). - * + * * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623 * * This function is consensus-critical since BIP66. @@ -113,7 +110,7 @@ bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) { // excluding the sighash byte. // * R-length: 1-byte length descriptor of the R value that follows. // * R: arbitrary-length big-endian encoded R value. It must use the shortest - // possible encoding for a positive integers (which means no null bytes at + // possible encoding for a positive integer (which means no null bytes at // the start, except a single one when the next byte has its highest bit set). // * S-length: 1-byte length descriptor of the S value that follows. // * S: arbitrary-length big-endian encoded S value. The same rules apply. @@ -142,7 +139,7 @@ bool static IsValidSignatureEncoding(const std::vector<unsigned char> &sig) { // Verify that the length of the signature matches the sum of the length // of the elements. if ((size_t)(lenR + lenS + 7) != sig.size()) return false; - + // Check whether the R element is an integer. if (sig[2] != 0x02) return false; @@ -176,7 +173,13 @@ bool static IsLowDERSignature(const valtype &vchSig, ScriptError* serror) { if (!IsValidSignatureEncoding(vchSig)) { return set_error(serror, SCRIPT_ERR_SIG_DER); } + // https://bitcoin.stackexchange.com/a/12556: + // Also note that inside transaction signatures, an extra hashtype byte + // follows the actual signature data. std::vector<unsigned char> vchSigCopy(vchSig.begin(), vchSig.begin() + vchSig.size() - 1); + // 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 (!CPubKey::CheckLowS(vchSigCopy)) { return set_error(serror, SCRIPT_ERR_SIG_HIGH_S); } @@ -194,7 +197,7 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) { return true; } -bool CheckSignatureEncoding(const vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror) { +bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror) { // Empty signature. Not strictly DER encoded, but allowed to provide a // compact way to provide an invalid signature for use with CHECK(MULTI)SIG if (vchSig.size() == 0) { @@ -216,43 +219,166 @@ bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, co return set_error(serror, SCRIPT_ERR_PUBKEYTYPE); } // Only compressed keys are accepted in segwit - if ((flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0 && sigversion == SIGVERSION_WITNESS_V0 && !IsCompressedPubKey(vchPubKey)) { + if ((flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0 && sigversion == SigVersion::WITNESS_V0 && !IsCompressedPubKey(vchPubKey)) { return set_error(serror, SCRIPT_ERR_WITNESS_PUBKEYTYPE); } return true; } bool static CheckMinimalPush(const valtype& data, opcodetype opcode) { + // Excludes OP_1NEGATE, OP_1-16 since they are by definition minimal + assert(0 <= opcode && opcode <= OP_PUSHDATA4); if (data.size() == 0) { - // Could have used OP_0. + // Should have used OP_0. return opcode == OP_0; } else if (data.size() == 1 && data[0] >= 1 && data[0] <= 16) { - // Could have used OP_1 .. OP_16. - return opcode == OP_1 + (data[0] - 1); + // Should have used OP_1 .. OP_16. + return false; } else if (data.size() == 1 && data[0] == 0x81) { - // Could have used OP_1NEGATE. - return opcode == OP_1NEGATE; + // Should have used OP_1NEGATE. + return false; } else if (data.size() <= 75) { - // Could have used a direct push (opcode indicating number of bytes pushed + those bytes). + // Must have used a direct push (opcode indicating number of bytes pushed + those bytes). return opcode == data.size(); } else if (data.size() <= 255) { - // Could have used OP_PUSHDATA. + // Must have used OP_PUSHDATA. return opcode == OP_PUSHDATA1; } else if (data.size() <= 65535) { - // Could have used OP_PUSHDATA2. + // Must have used OP_PUSHDATA2. return opcode == OP_PUSHDATA2; } return true; } -bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror) +int FindAndDelete(CScript& script, const CScript& b) +{ + int nFound = 0; + if (b.empty()) + return nFound; + CScript result; + CScript::const_iterator pc = script.begin(), pc2 = script.begin(), end = script.end(); + opcodetype opcode; + do + { + result.insert(result.end(), pc2, pc); + while (static_cast<size_t>(end - pc) >= b.size() && std::equal(b.begin(), b.end(), pc)) + { + pc = pc + b.size(); + ++nFound; + } + pc2 = pc; + } + while (script.GetOp(pc, opcode)); + + if (nFound > 0) { + result.insert(result.end(), pc2, end); + script = std::move(result); + } + + return nFound; +} + +namespace { +/** A data type to abstract out the condition stack during script execution. + * + * Conceptually it acts like a vector of booleans, one for each level of nested + * IF/THEN/ELSE, indicating whether we're in the active or inactive branch of + * each. + * + * The elements on the stack cannot be observed individually; we only need to + * expose whether the stack is empty and whether or not any false values are + * present at all. To implement OP_ELSE, a toggle_top modifier is added, which + * flips the last value without returning it. + * + * This uses an optimized implementation that does not materialize the + * actual stack. Instead, it just stores the size of the would-be stack, + * and the position of the first false value in it. + */ +class ConditionStack { +private: + //! A constant for m_first_false_pos to indicate there are no falses. + static constexpr uint32_t NO_FALSE = std::numeric_limits<uint32_t>::max(); + + //! The size of the implied stack. + uint32_t m_stack_size = 0; + //! The position of the first false value on the implied stack, or NO_FALSE if all true. + uint32_t m_first_false_pos = NO_FALSE; + +public: + bool empty() { return m_stack_size == 0; } + bool all_true() { return m_first_false_pos == NO_FALSE; } + void push_back(bool f) + { + if (m_first_false_pos == NO_FALSE && !f) { + // The stack consists of all true values, and a false is added. + // The first false value will appear at the current size. + m_first_false_pos = m_stack_size; + } + ++m_stack_size; + } + void pop_back() + { + assert(m_stack_size > 0); + --m_stack_size; + if (m_first_false_pos == m_stack_size) { + // When popping off the first false value, everything becomes true. + m_first_false_pos = NO_FALSE; + } + } + void toggle_top() + { + assert(m_stack_size > 0); + if (m_first_false_pos == NO_FALSE) { + // The current stack is all true values; the first false will be the top. + m_first_false_pos = m_stack_size - 1; + } else if (m_first_false_pos == m_stack_size - 1) { + // The top is the first false value; toggling it will make everything true. + m_first_false_pos = NO_FALSE; + } else { + // There is a false value, but not on top. No action is needed as toggling + // anything but the first false value is unobservable. + } + } +}; +} + +/** Helper for OP_CHECKSIG and OP_CHECKSIGVERIFY + * + * A return value of false means the script fails entirely. When true is returned, the + * fSuccess variable indicates whether the signature check itself succeeded. + */ +static bool EvalChecksig(const valtype& vchSig, const valtype& vchPubKey, CScript::const_iterator pbegincodehash, CScript::const_iterator pend, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror, bool& fSuccess) +{ + // Subset of script starting at the most recent codeseparator + CScript scriptCode(pbegincodehash, pend); + + // Drop the signature in pre-segwit scripts but not segwit scripts + if (sigversion == SigVersion::BASE) { + int found = FindAndDelete(scriptCode, CScript() << vchSig); + if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) + return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE); + } + + if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { + //serror is set + return false; + } + fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); + + if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size()) + return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL); + + return true; +} + +bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror) { static const CScriptNum bnZero(0); static const CScriptNum bnOne(1); - static const CScriptNum bnFalse(0); - static const CScriptNum bnTrue(1); + // static const CScriptNum bnFalse(0); + // static const CScriptNum bnTrue(1); static const valtype vchFalse(0); - static const valtype vchZero(0); + // static const valtype vchZero(0); static const valtype vchTrue(1, 1); CScript::const_iterator pc = script.begin(); @@ -260,8 +386,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un CScript::const_iterator pbegincodehash = script.begin(); opcodetype opcode; valtype vchPushValue; - vector<bool> vfExec; - vector<valtype> altstack; + ConditionStack vfExec; + std::vector<valtype> altstack; set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); if (script.size() > MAX_SCRIPT_SIZE) return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE); @@ -272,7 +398,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { while (pc < pend) { - bool fExec = !count(vfExec.begin(), vfExec.end(), false); + bool fExec = vfExec.all_true(); // // Read instruction @@ -301,7 +427,11 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un opcode == OP_MOD || opcode == OP_LSHIFT || opcode == OP_RSHIFT) - return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes. + return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes (CVE-2010-5137). + + // With SCRIPT_VERIFY_CONST_SCRIPTCODE, OP_CODESEPARATOR in non-segwit script is rejected even in an unexecuted branch + if (opcode == OP_CODESEPARATOR && sigversion == SigVersion::BASE && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) + return set_error(serror, SCRIPT_ERR_OP_CODESEPARATOR); if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) { if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { @@ -351,9 +481,6 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) { // not enabled; treat as a NOP2 - if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { - return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); - } break; } @@ -393,9 +520,6 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) { // not enabled; treat as a NOP3 - if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { - return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); - } break; } @@ -444,7 +568,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un if (stack.size() < 1) return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL); valtype& vch = stacktop(-1); - if (sigversion == SIGVERSION_WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) { + if (sigversion == SigVersion::WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) { if (vch.size() > 1) return set_error(serror, SCRIPT_ERR_MINIMALIF); if (vch.size() == 1 && vch[0] != 1) @@ -463,7 +587,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un { if (vfExec.empty()) return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL); - vfExec.back() = !vfExec.back(); + vfExec.toggle_top(); } break; @@ -868,10 +992,13 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un popstack(stack); stack.push_back(vchHash); } - break; + break; case OP_CODESEPARATOR: { + // If SCRIPT_VERIFY_CONST_SCRIPTCODE flag is set, use of OP_CODESEPARATOR is rejected in pre-segwit + // script, even in an unexecuted branch (this is checked above the opcode case statement). + // Hash starts after the code separator pbegincodehash = pc; } @@ -887,23 +1014,8 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un valtype& vchSig = stacktop(-2); valtype& vchPubKey = stacktop(-1); - // Subset of script starting at the most recent codeseparator - CScript scriptCode(pbegincodehash, pend); - - // Drop the signature in pre-segwit scripts but not segwit scripts - if (sigversion == SIGVERSION_BASE) { - scriptCode.FindAndDelete(CScript(vchSig)); - } - - if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { - //serror is set - return false; - } - bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); - - if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size()) - return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL); - + bool fSuccess = true; + if (!EvalChecksig(vchSig, vchPubKey, pbegincodehash, pend, flags, checker, sigversion, serror, fSuccess)) return false; popstack(stack); popstack(stack); stack.push_back(fSuccess ? vchTrue : vchFalse); @@ -955,8 +1067,10 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un for (int k = 0; k < nSigsCount; k++) { valtype& vchSig = stacktop(-isig-k); - if (sigversion == SIGVERSION_BASE) { - scriptCode.FindAndDelete(CScript(vchSig)); + if (sigversion == SigVersion::BASE) { + int found = FindAndDelete(scriptCode, CScript() << vchSig); + if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) + return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE); } } @@ -1030,7 +1144,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un } // Size limits - if (stack.size() + altstack.size() > 1000) + if (stack.size() + altstack.size() > MAX_STACK_SIZE) return set_error(serror, SCRIPT_ERR_STACK_SIZE); } } @@ -1051,9 +1165,11 @@ namespace { * Wrapper that serializes like CTransaction, but with the modifications * required for the signature hash done in-place */ -class CTransactionSignatureSerializer { +template <class T> +class CTransactionSignatureSerializer +{ private: - const CTransaction& txTo; //!< reference to the spending transaction (the one being serialized) + const T& txTo; //!< reference to the spending transaction (the one being serialized) const CScript& scriptCode; //!< output script being consumed const unsigned int nIn; //!< input index of txTo being signed const bool fAnyoneCanPay; //!< whether the hashtype has the SIGHASH_ANYONECANPAY flag set @@ -1061,7 +1177,7 @@ private: const bool fHashNone; //!< whether the hashtype is SIGHASH_NONE public: - CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : + CTransactionSignatureSerializer(const T& txToIn, const CScript& scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn), fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)), fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE), @@ -1101,7 +1217,7 @@ public: // Serialize the script if (nInput != nIn) // Blank out other inputs' signatures - ::Serialize(s, CScriptBase()); + ::Serialize(s, CScript()); else SerializeScriptCode(s); // Serialize the nSequence @@ -1142,57 +1258,87 @@ public: } }; -uint256 GetPrevoutHash(const CTransaction& txTo) { +template <class T> +uint256 GetPrevoutHash(const T& txTo) +{ CHashWriter ss(SER_GETHASH, 0); - for (unsigned int n = 0; n < txTo.vin.size(); n++) { - ss << txTo.vin[n].prevout; + for (const auto& txin : txTo.vin) { + ss << txin.prevout; } return ss.GetHash(); } -uint256 GetSequenceHash(const CTransaction& txTo) { +template <class T> +uint256 GetSequenceHash(const T& txTo) +{ CHashWriter ss(SER_GETHASH, 0); - for (unsigned int n = 0; n < txTo.vin.size(); n++) { - ss << txTo.vin[n].nSequence; + for (const auto& txin : txTo.vin) { + ss << txin.nSequence; } return ss.GetHash(); } -uint256 GetOutputsHash(const CTransaction& txTo) { +template <class T> +uint256 GetOutputsHash(const T& txTo) +{ CHashWriter ss(SER_GETHASH, 0); - for (unsigned int n = 0; n < txTo.vout.size(); n++) { - ss << txTo.vout[n]; + for (const auto& txout : txTo.vout) { + ss << txout; } return ss.GetHash(); } -} // anon namespace +} // namespace -PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) +template <class T> +void PrecomputedTransactionData::Init(const T& txTo) { - hashPrevouts = GetPrevoutHash(txTo); - hashSequence = GetSequenceHash(txTo); - hashOutputs = GetOutputsHash(txTo); + assert(!m_ready); + + // Cache is calculated only for transactions with witness + if (txTo.HasWitness()) { + hashPrevouts = GetPrevoutHash(txTo); + hashSequence = GetSequenceHash(txTo); + hashOutputs = GetOutputsHash(txTo); + } + + m_ready = true; } -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) +template <class T> +PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo) { - if (sigversion == SIGVERSION_WITNESS_V0) { + Init(txTo); +} + +// explicit instantiation +template void PrecomputedTransactionData::Init(const CTransaction& txTo); +template void PrecomputedTransactionData::Init(const CMutableTransaction& txTo); +template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo); +template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo); + +template <class T> +uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) +{ + assert(nIn < txTo.vin.size()); + + if (sigversion == SigVersion::WITNESS_V0) { uint256 hashPrevouts; uint256 hashSequence; uint256 hashOutputs; + const bool cacheready = cache && cache->m_ready; if (!(nHashType & SIGHASH_ANYONECANPAY)) { - hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo); + hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo); } if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo); + hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo); } if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo); + hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo); } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { CHashWriter ss(SER_GETHASH, 0); ss << txTo.vout[nIn]; @@ -1209,7 +1355,7 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig // The prevout may already be contained in hashPrevout, and the nSequence // may already be contain in hashSequence. ss << txTo.vin[nIn].prevout; - ss << static_cast<const CScriptBase&>(scriptCode); + ss << scriptCode; ss << amount; ss << txTo.vin[nIn].nSequence; // Outputs (none/one/all, depending on flags) @@ -1222,22 +1368,16 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig return ss.GetHash(); } - static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); - if (nIn >= txTo.vin.size()) { - // nIn out of range - return one; - } - // Check for invalid use of SIGHASH_SINGLE if ((nHashType & 0x1f) == SIGHASH_SINGLE) { if (nIn >= txTo.vout.size()) { // nOut out of range - return one; + return UINT256_ONE(); } } // Wrapper to serialize only the necessary parts of the transaction being signed - CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType); + CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType); // Serialize and hash CHashWriter ss(SER_GETHASH, 0); @@ -1245,19 +1385,21 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig return ss.GetHash(); } -bool TransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const +template <class T> +bool GenericTransactionSignatureChecker<T>::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const { return pubkey.Verify(sighash, vchSig); } -bool TransactionSignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn, const vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const +template <class T> +bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const { CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) return false; // Hash type is one byte tacked on to the end of the signature - vector<unsigned char> vchSig(vchSigIn); + std::vector<unsigned char> vchSig(vchSigIn); if (vchSig.empty()) return false; int nHashType = vchSig.back(); @@ -1271,7 +1413,8 @@ bool TransactionSignatureChecker::CheckSig(const vector<unsigned char>& vchSigIn return true; } -bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const +template <class T> +bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLockTime) const { // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether @@ -1307,7 +1450,8 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con return true; } -bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) const +template <class T> +bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSequence) const { // Relative lock times are supported by comparing the passed // in operand to the sequence number of the input. @@ -1353,63 +1497,71 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con return true; } +// explicit instantiation +template class GenericTransactionSignatureChecker<CTransaction>; +template class GenericTransactionSignatureChecker<CMutableTransaction>; + +static bool ExecuteWitnessScript(const Span<const valtype>& stack_span, const CScript& scriptPubKey, unsigned int flags, SigVersion sigversion, const BaseSignatureChecker& checker, ScriptError* serror) +{ + std::vector<valtype> stack{stack_span.begin(), stack_span.end()}; + + // Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack + for (const valtype& elem : stack) { + if (elem.size() > MAX_SCRIPT_ELEMENT_SIZE) return set_error(serror, SCRIPT_ERR_PUSH_SIZE); + } + + // Run the script interpreter. + if (!EvalScript(stack, scriptPubKey, flags, checker, sigversion, serror)) return false; + + // Scripts inside witness implicitly require cleanstack behaviour + if (stack.size() != 1) return set_error(serror, SCRIPT_ERR_CLEANSTACK); + if (!CastToBool(stack.back())) return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + return true; +} + static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { - vector<vector<unsigned char> > stack; CScript scriptPubKey; + Span<const valtype> stack = MakeSpan(witness.stack); if (witversion == 0) { - if (program.size() == 32) { + if (program.size() == WITNESS_V0_SCRIPTHASH_SIZE) { // Version 0 segregated witness program: SHA256(CScript) inside the program, CScript + inputs in witness - if (witness.stack.size() == 0) { + if (stack.size() == 0) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY); } - scriptPubKey = CScript(witness.stack.back().begin(), witness.stack.back().end()); - stack = std::vector<std::vector<unsigned char> >(witness.stack.begin(), witness.stack.end() - 1); + const valtype& script_bytes = SpanPopBack(stack); + scriptPubKey = CScript(script_bytes.begin(), script_bytes.end()); uint256 hashScriptPubKey; CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin()); - if (memcmp(hashScriptPubKey.begin(), &program[0], 32)) { + if (memcmp(hashScriptPubKey.begin(), program.data(), 32)) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); } - } else if (program.size() == 20) { + return ExecuteWitnessScript(stack, scriptPubKey, flags, SigVersion::WITNESS_V0, checker, serror); + } else if (program.size() == WITNESS_V0_KEYHASH_SIZE) { // Special case for pay-to-pubkeyhash; signature + pubkey in witness - if (witness.stack.size() != 2) { + if (stack.size() != 2) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness } scriptPubKey << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG; - stack = witness.stack; + return ExecuteWitnessScript(stack, scriptPubKey, flags, SigVersion::WITNESS_V0, checker, serror); } else { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH); } - } else if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) { - return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM); } else { + if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM); + } // Higher version witness scripts return true for future softfork compatibility - return set_success(serror); - } - - // Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack - for (unsigned int i = 0; i < stack.size(); i++) { - if (stack.at(i).size() > MAX_SCRIPT_ELEMENT_SIZE) - return set_error(serror, SCRIPT_ERR_PUSH_SIZE); - } - - if (!EvalScript(stack, scriptPubKey, flags, checker, SIGVERSION_WITNESS_V0, serror)) { - return false; + return true; } - - // Scripts inside witness implicitly require cleanstack behaviour - if (stack.size() != 1) - return set_error(serror, SCRIPT_ERR_EVAL_FALSE); - if (!CastToBool(stack.back())) - return set_error(serror, SCRIPT_ERR_EVAL_FALSE); - return true; + // There is intentionally no return statement here, to be able to use "control reaches end of non-void function" warnings to detect gaps in the logic above. } bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { static const CScriptWitness emptyWitness; - if (witness == NULL) { + if (witness == nullptr) { witness = &emptyWitness; } bool hadWitness = false; @@ -1420,13 +1572,15 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY); } - vector<vector<unsigned char> > stack, stackCopy; - if (!EvalScript(stack, scriptSig, flags, checker, SIGVERSION_BASE, serror)) + // scriptSig and scriptPubKey must be evaluated sequentially on the same stack + // rather than being simply concatenated (see CVE-2010-5141) + std::vector<std::vector<unsigned char> > stack, stackCopy; + if (!EvalScript(stack, scriptSig, flags, checker, SigVersion::BASE, serror)) // serror is set return false; if (flags & SCRIPT_VERIFY_P2SH) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, flags, checker, SIGVERSION_BASE, serror)) + if (!EvalScript(stack, scriptPubKey, flags, checker, SigVersion::BASE, serror)) // serror is set return false; if (stack.empty()) @@ -1472,7 +1626,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stack); - if (!EvalScript(stack, pubKey2, flags, checker, SIGVERSION_BASE, serror)) + if (!EvalScript(stack, pubKey2, flags, checker, SigVersion::BASE, serror)) // serror is set return false; if (stack.empty()) @@ -1525,13 +1679,13 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return set_success(serror); } -size_t static WitnessSigOps(int witversion, const std::vector<unsigned char>& witprogram, const CScriptWitness& witness, int flags) +size_t static WitnessSigOps(int witversion, const std::vector<unsigned char>& witprogram, const CScriptWitness& witness) { if (witversion == 0) { - if (witprogram.size() == 20) + if (witprogram.size() == WITNESS_V0_KEYHASH_SIZE) return 1; - if (witprogram.size() == 32 && witness.stack.size() > 0) { + if (witprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE && witness.stack.size() > 0) { CScript subscript(witness.stack.back().begin(), witness.stack.back().end()); return subscript.GetSigOpCount(true); } @@ -1553,19 +1707,19 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, int witnessversion; std::vector<unsigned char> witnessprogram; if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { - return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags); + return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty); } if (scriptPubKey.IsPayToScriptHash() && scriptSig.IsPushOnly()) { CScript::const_iterator pc = scriptSig.begin(); - vector<unsigned char> data; + std::vector<unsigned char> data; while (pc < scriptSig.end()) { opcodetype opcode; scriptSig.GetOp(pc, opcode, data); } CScript subscript(data.begin(), data.end()); if (subscript.IsWitnessProgram(witnessversion, witnessprogram)) { - return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags); + return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty); } } |