diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -164,6 +164,7 @@ script/interpreter.cpp script/script.cpp script/script_error.cpp + script/sigencoding.cpp uint256.cpp utilstrencodings.cpp ) diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -317,6 +317,9 @@ script/script.h \ script/script_error.cpp \ script/script_error.h \ + script/script_flags.h \ + script/sigencoding.cpp \ + script/sigencoding.h \ serialize.h \ tinyformat.h \ uint256.cpp \ diff --git a/src/core_write.cpp b/src/core_write.cpp --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -7,6 +7,7 @@ #include "dstencode.h" #include "primitives/transaction.h" #include "script/script.h" +#include "script/sigencoding.h" #include "script/standard.h" #include "serialize.h" #include "streams.h" diff --git a/src/policy/policy.h b/src/policy/policy.h --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -7,12 +7,12 @@ #define BITCOIN_POLICY_POLICY_H #include "consensus/consensus.h" -#include "script/interpreter.h" #include "script/standard.h" #include class CCoinsViewCache; +class CTransaction; /** Default for -blockmaxsize, which controls the maximum size of block the * mining code will create **/ diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -8,6 +8,7 @@ #include "policy/policy.h" +#include "script/interpreter.h" #include "tinyformat.h" #include "util.h" #include "utilstrencodings.h" diff --git a/src/script/interpreter.h b/src/script/interpreter.h --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2017-2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -7,6 +8,7 @@ #define BITCOIN_SCRIPT_INTERPRETER_H #include "primitives/transaction.h" +#include "script/script_flags.h" #include "script_error.h" #include "sighashtype.h" @@ -19,98 +21,6 @@ class CTransaction; class uint256; -/** Script verification flags */ -enum { - SCRIPT_VERIFY_NONE = 0, - - // Evaluate P2SH subscripts (softfork safe, BIP16). - SCRIPT_VERIFY_P2SH = (1U << 0), - - // Passing a non-strict-DER signature or one with undefined hashtype to a - // checksig operation causes script failure. Evaluating a pubkey that is not - // (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) by checksig causes script - // failure. - SCRIPT_VERIFY_STRICTENC = (1U << 1), - - // Passing a non-strict-DER signature to a checksig operation causes script - // failure (softfork safe, BIP62 rule 1) - SCRIPT_VERIFY_DERSIG = (1U << 2), - - // Passing a non-strict-DER signature or one with S > order/2 to a checksig - // operation causes script failure - // (softfork safe, BIP62 rule 5). - SCRIPT_VERIFY_LOW_S = (1U << 3), - - // verify dummy stack item consumed by CHECKMULTISIG is of zero-length - // (softfork safe, BIP62 rule 7). - SCRIPT_VERIFY_NULLDUMMY = (1U << 4), - - // Using a non-push operator in the scriptSig causes script failure - // (softfork safe, BIP62 rule 2). - SCRIPT_VERIFY_SIGPUSHONLY = (1U << 5), - - // Require minimal encodings for all push operations (OP_0... OP_16, - // OP_1NEGATE where possible, direct pushes up to 75 bytes, OP_PUSHDATA up - // to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating any other - // push causes the script to fail (BIP62 rule 3). In addition, whenever a - // stack element is interpreted as a number, it must be of minimal length - // (BIP62 rule 4). - // (softfork safe) - SCRIPT_VERIFY_MINIMALDATA = (1U << 6), - - // Discourage use of NOPs reserved for upgrades (NOP1-10) - // - // Provided so that nodes can avoid accepting or mining transactions - // containing executed NOP's whose meaning may change after a soft-fork, - // thus rendering the script invalid; with this flag set executing - // discouraged NOPs fails the script. This verification flag will never be a - // mandatory flag applied to scripts in a block. NOPs that are not executed, - // e.g. within an unexecuted IF ENDIF block, are *not* rejected. - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1U << 7), - - // Require that only a single stack element remains after evaluation. This - // changes the success criterion from "At least one stack element must - // remain, and when interpreted as a boolean, it must be true" to "Exactly - // one stack element must remain, and when interpreted as a boolean, it must - // be true". - // (softfork safe, BIP62 rule 6) - // Note: CLEANSTACK should never be used without P2SH or WITNESS. - SCRIPT_VERIFY_CLEANSTACK = (1U << 8), - - // Verify CHECKLOCKTIMEVERIFY - // - // See BIP65 for details. - SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), - - // support CHECKSEQUENCEVERIFY opcode - // - // See BIP112 for details - SCRIPT_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), - - // Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector - // - SCRIPT_VERIFY_MINIMALIF = (1U << 13), - - // Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed - // - SCRIPT_VERIFY_NULLFAIL = (1U << 14), - - // Public keys in scripts must be compressed - // - SCRIPT_VERIFY_COMPRESSED_PUBKEYTYPE = (1U << 15), - - // Do we accept signature using SIGHASH_FORKID - // - SCRIPT_ENABLE_SIGHASH_FORKID = (1U << 16), - - // Do we accept activate replay protection using a different fork id. - // - SCRIPT_ENABLE_REPLAY_PROTECTION = (1U << 17), -}; - -bool CheckSignatureEncoding(const std::vector &vchSig, uint32_t flags, - ScriptError *serror); - uint256 SignatureHash(const CScript &scriptCode, const CTransaction &txTo, unsigned int nIn, SigHashType sigHashType, const Amount amount, diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2017-2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -11,28 +12,10 @@ #include "primitives/transaction.h" #include "pubkey.h" #include "script/script.h" +#include "script/script_flags.h" +#include "script/sigencoding.h" #include "uint256.h" -typedef std::vector valtype; - -namespace { - -inline bool set_success(ScriptError *ret) { - if (ret) { - *ret = SCRIPT_ERR_OK; - } - return true; -} - -inline bool set_error(ScriptError *ret, const ScriptError serror) { - if (ret) { - *ret = serror; - } - return false; -} - -} // namespace - bool CastToBool(const valtype &vch) { for (size_t i = 0; i < vch.size(); i++) { if (vch[i] != 0) { @@ -59,140 +42,6 @@ stack.pop_back(); } -static bool IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { - if (vchPubKey.size() < 33) { - // Non-canonical public key: too short - return false; - } - if (vchPubKey[0] == 0x04) { - if (vchPubKey.size() != 65) { - // Non-canonical public key: invalid length for uncompressed key - return false; - } - } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { - if (vchPubKey.size() != 33) { - // Non-canonical public key: invalid length for compressed key - return false; - } - } else { - // Non-canonical public key: neither compressed nor uncompressed - return false; - } - return true; -} - -static bool IsCompressedPubKey(const valtype &vchPubKey) { - if (vchPubKey.size() != 33) { - // Non-canonical public key: invalid length for compressed key - return false; - } - if (vchPubKey[0] != 0x02 && vchPubKey[0] != 0x03) { - // Non-canonical public key: invalid prefix for compressed key - return false; - } - return true; -} - -/** - * A canonical signature exists of: <30> <02> <02> , 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. - */ -static bool IsValidSignatureEncoding(const std::vector &sig) { - // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] - // [sighash] - // * total-length: 1-byte length descriptor of everything that follows, - // 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 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. - // * sighash: 1-byte value indicating what data is hashed (not part of the - // DER signature) - - // Minimum and maximum size constraints. - if (sig.size() < 9) return false; - if (sig.size() > 73) return false; - - // A signature is of type 0x30 (compound). - if (sig[0] != 0x30) return false; - - // Make sure the length covers the entire signature. - if (sig[1] != sig.size() - 3) return false; - - // Extract the length of the R element. - unsigned int lenR = sig[3]; - - // Make sure the length of the S element is still inside the signature. - if (5 + lenR >= sig.size()) return false; - - // Extract the length of the S element. - unsigned int lenS = sig[5 + lenR]; - - // 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; - - // Zero-length integers are not allowed for R. - if (lenR == 0) return false; - - // Negative numbers are not allowed for R. - if (sig[4] & 0x80) return false; - - // Null bytes at the start of R are not allowed, unless R would otherwise be - // interpreted as a negative number. - if (lenR > 1 && (sig[4] == 0x00) && !(sig[5] & 0x80)) return false; - - // Check whether the S element is an integer. - if (sig[lenR + 4] != 0x02) return false; - - // Zero-length integers are not allowed for S. - if (lenS == 0) return false; - - // Negative numbers are not allowed for S. - if (sig[lenR + 6] & 0x80) return false; - - // Null bytes at the start of S are not allowed, unless S would otherwise be - // interpreted as a negative number. - if (lenS > 1 && (sig[lenR + 6] == 0x00) && !(sig[lenR + 7] & 0x80)) { - return false; - } - - return true; -} - -static bool IsLowDERSignature(const valtype &vchSig, ScriptError *serror) { - if (!IsValidSignatureEncoding(vchSig)) { - return set_error(serror, SCRIPT_ERR_SIG_DER); - } - std::vector vchSigCopy(vchSig.begin(), - vchSig.begin() + vchSig.size() - 1); - if (!CPubKey::CheckLowS(vchSigCopy)) { - return set_error(serror, SCRIPT_ERR_SIG_HIGH_S); - } - return true; -} - -static SigHashType GetHashType(const valtype &vchSig) { - if (vchSig.size() == 0) { - return SigHashType(0); - } - - return SigHashType(vchSig[vchSig.size() - 1]); -} - static void CleanupScriptCode(CScript &scriptCode, const std::vector &vchSig, uint32_t flags) { @@ -203,54 +52,6 @@ } } -bool CheckSignatureEncoding(const std::vector &vchSig, uint32_t 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) { - return true; - } - if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | - SCRIPT_VERIFY_STRICTENC)) != 0 && - !IsValidSignatureEncoding(vchSig)) { - return set_error(serror, SCRIPT_ERR_SIG_DER); - } - if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && - !IsLowDERSignature(vchSig, serror)) { - // serror is set - return false; - } - if ((flags & SCRIPT_VERIFY_STRICTENC) != 0) { - if (!GetHashType(vchSig).isDefined()) { - return set_error(serror, SCRIPT_ERR_SIG_HASHTYPE); - } - bool usesForkId = GetHashType(vchSig).hasForkId(); - bool forkIdEnabled = flags & SCRIPT_ENABLE_SIGHASH_FORKID; - if (!forkIdEnabled && usesForkId) { - return set_error(serror, SCRIPT_ERR_ILLEGAL_FORKID); - } - if (forkIdEnabled && !usesForkId) { - return set_error(serror, SCRIPT_ERR_MUST_USE_FORKID); - } - } - return true; -} - -static bool CheckPubKeyEncoding(const valtype &vchPubKey, uint32_t flags, - ScriptError *serror) { - if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && - !IsCompressedOrUncompressedPubKey(vchPubKey)) { - return set_error(serror, SCRIPT_ERR_PUBKEYTYPE); - } - // Only compressed keys are accepted when - // SCRIPT_VERIFY_COMPRESSED_PUBKEYTYPE is enabled. - if (flags & SCRIPT_VERIFY_COMPRESSED_PUBKEYTYPE && - !IsCompressedPubKey(vchPubKey)) { - return set_error(serror, SCRIPT_ERR_NONCOMPRESSED_PUBKEY); - } - return true; -} - static bool CheckMinimalPush(const valtype &data, opcodetype opcode) { if (data.size() == 0) { // Could have used OP_0. diff --git a/src/script/script_error.h b/src/script/script_error.h --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -77,4 +77,22 @@ const char *ScriptErrorString(const ScriptError error); +namespace { + +inline bool set_success(ScriptError *ret) { + if (ret) { + *ret = SCRIPT_ERR_OK; + } + return true; +} + +inline bool set_error(ScriptError *ret, const ScriptError serror) { + if (ret) { + *ret = serror; + } + return false; +} + +} // namespace + #endif // BITCOIN_SCRIPT_SCRIPT_ERROR_H diff --git a/src/script/interpreter.h b/src/script/script_flags.h copy from src/script/interpreter.h copy to src/script/script_flags.h --- a/src/script/interpreter.h +++ b/src/script/script_flags.h @@ -1,23 +1,11 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2017-2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_SCRIPT_INTERPRETER_H -#define BITCOIN_SCRIPT_INTERPRETER_H - -#include "primitives/transaction.h" -#include "script_error.h" -#include "sighashtype.h" - -#include -#include -#include - -class CPubKey; -class CScript; -class CTransaction; -class uint256; +#ifndef BITCOIN_SCRIPT_SCRIPTFLAGS_H +#define BITCOIN_SCRIPT_SCRIPTFLAGS_H /** Script verification flags */ enum { @@ -108,76 +96,4 @@ SCRIPT_ENABLE_REPLAY_PROTECTION = (1U << 17), }; -bool CheckSignatureEncoding(const std::vector &vchSig, uint32_t flags, - ScriptError *serror); - -uint256 SignatureHash(const CScript &scriptCode, const CTransaction &txTo, - unsigned int nIn, SigHashType sigHashType, - const Amount amount, - const PrecomputedTransactionData *cache = nullptr, - uint32_t flags = SCRIPT_ENABLE_SIGHASH_FORKID); - -class BaseSignatureChecker { -public: - virtual bool CheckSig(const std::vector &scriptSig, - const std::vector &vchPubKey, - const CScript &scriptCode, uint32_t flags) const { - return false; - } - - virtual bool CheckLockTime(const CScriptNum &nLockTime) const { - return false; - } - - virtual bool CheckSequence(const CScriptNum &nSequence) const { - return false; - } - - virtual ~BaseSignatureChecker() {} -}; - -class TransactionSignatureChecker : public BaseSignatureChecker { -private: - const CTransaction *txTo; - unsigned int nIn; - const Amount amount; - const PrecomputedTransactionData *txdata; - -protected: - virtual bool VerifySignature(const std::vector &vchSig, - const CPubKey &vchPubKey, - const uint256 &sighash) const; - -public: - TransactionSignatureChecker(const CTransaction *txToIn, unsigned int nInIn, - const Amount amountIn) - : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {} - TransactionSignatureChecker(const CTransaction *txToIn, unsigned int nInIn, - const Amount amountIn, - const PrecomputedTransactionData &txdataIn) - : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} - bool CheckSig(const std::vector &scriptSig, - const std::vector &vchPubKey, - const CScript &scriptCode, uint32_t flags) const override; - bool CheckLockTime(const CScriptNum &nLockTime) const override; - bool CheckSequence(const CScriptNum &nSequence) const override; -}; - -class MutableTransactionSignatureChecker : public TransactionSignatureChecker { -private: - const CTransaction txTo; - -public: - MutableTransactionSignatureChecker(const CMutableTransaction *txToIn, - unsigned int nInIn, const Amount amount) - : TransactionSignatureChecker(&txTo, nInIn, amount), txTo(*txToIn) {} -}; - -bool EvalScript(std::vector> &stack, const CScript &script, - uint32_t flags, const BaseSignatureChecker &checker, - ScriptError *error = nullptr); -bool VerifyScript(const CScript &scriptSig, const CScript &scriptPubKey, - uint32_t flags, const BaseSignatureChecker &checker, - ScriptError *serror = nullptr); - -#endif // BITCOIN_SCRIPT_INTERPRETER_H +#endif // BITCOIN_SCRIPT_SCRIPTFLAGS_H diff --git a/src/script/sigencoding.h b/src/script/sigencoding.h new file mode 100644 --- /dev/null +++ b/src/script/sigencoding.h @@ -0,0 +1,35 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2017-2018 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_SCRIPT_SIGENCODING_H +#define BITCOIN_SCRIPT_SIGENCODING_H + +#include "script_error.h" +#include "sighashtype.h" + +#include +#include + +typedef std::vector valtype; + +namespace { + +inline SigHashType GetHashType(const valtype &vchSig) { + if (vchSig.size() == 0) { + return SigHashType(0); + } + + return SigHashType(vchSig[vchSig.size() - 1]); +} + +} // namespace + +bool CheckSignatureEncoding(const valtype &vchSig, uint32_t flags, + ScriptError *serror); +bool CheckPubKeyEncoding(const valtype &vchPubKey, uint32_t flags, + ScriptError *serror); + +#endif // BITCOIN_SCRIPT_SIGENCODING_H diff --git a/src/script/sigencoding.cpp b/src/script/sigencoding.cpp new file mode 100644 --- /dev/null +++ b/src/script/sigencoding.cpp @@ -0,0 +1,184 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2017-2018 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "sigencoding.h" + +#include "pubkey.h" +#include "script_flags.h" + +/** + * A canonical signature exists of: <30> <02> <02> , 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. + */ +static bool IsValidSignatureEncoding(const valtype &sig) { + // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] + // [sighash] + // * total-length: 1-byte length descriptor of everything that follows, + // 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 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. + // * sighash: 1-byte value indicating what data is hashed (not part of the + // DER signature) + + // Minimum and maximum size constraints. + if (sig.size() < 9) return false; + if (sig.size() > 73) return false; + + // A signature is of type 0x30 (compound). + if (sig[0] != 0x30) return false; + + // Make sure the length covers the entire signature. + if (sig[1] != sig.size() - 3) return false; + + // Extract the length of the R element. + unsigned int lenR = sig[3]; + + // Make sure the length of the S element is still inside the signature. + if (5 + lenR >= sig.size()) return false; + + // Extract the length of the S element. + unsigned int lenS = sig[5 + lenR]; + + // 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; + + // Zero-length integers are not allowed for R. + if (lenR == 0) return false; + + // Negative numbers are not allowed for R. + if (sig[4] & 0x80) return false; + + // Null bytes at the start of R are not allowed, unless R would otherwise be + // interpreted as a negative number. + if (lenR > 1 && (sig[4] == 0x00) && !(sig[5] & 0x80)) return false; + + // Check whether the S element is an integer. + if (sig[lenR + 4] != 0x02) return false; + + // Zero-length integers are not allowed for S. + if (lenS == 0) return false; + + // Negative numbers are not allowed for S. + if (sig[lenR + 6] & 0x80) return false; + + // Null bytes at the start of S are not allowed, unless S would otherwise be + // interpreted as a negative number. + if (lenS > 1 && (sig[lenR + 6] == 0x00) && !(sig[lenR + 7] & 0x80)) { + return false; + } + + return true; +} + +static bool IsLowDERSignature(const valtype &vchSig, ScriptError *serror) { + if (!IsValidSignatureEncoding(vchSig)) { + return set_error(serror, SCRIPT_ERR_SIG_DER); + } + std::vector vchSigCopy(vchSig.begin(), + vchSig.begin() + vchSig.size() - 1); + if (!CPubKey::CheckLowS(vchSigCopy)) { + return set_error(serror, SCRIPT_ERR_SIG_HIGH_S); + } + return true; +} + +bool CheckSignatureEncoding(const valtype &vchSig, uint32_t 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) { + return true; + } + if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | + SCRIPT_VERIFY_STRICTENC)) != 0 && + !IsValidSignatureEncoding(vchSig)) { + return set_error(serror, SCRIPT_ERR_SIG_DER); + } + if ((flags & SCRIPT_VERIFY_LOW_S) != 0 && + !IsLowDERSignature(vchSig, serror)) { + // serror is set + return false; + } + if ((flags & SCRIPT_VERIFY_STRICTENC) != 0) { + if (!GetHashType(vchSig).isDefined()) { + return set_error(serror, SCRIPT_ERR_SIG_HASHTYPE); + } + bool usesForkId = GetHashType(vchSig).hasForkId(); + bool forkIdEnabled = flags & SCRIPT_ENABLE_SIGHASH_FORKID; + if (!forkIdEnabled && usesForkId) { + return set_error(serror, SCRIPT_ERR_ILLEGAL_FORKID); + } + if (forkIdEnabled && !usesForkId) { + return set_error(serror, SCRIPT_ERR_MUST_USE_FORKID); + } + } + return true; +} + +static bool IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { + if (vchPubKey.size() < 33) { + // Non-canonical public key: too short + return false; + } + if (vchPubKey[0] == 0x04) { + if (vchPubKey.size() != 65) { + // Non-canonical public key: invalid length for uncompressed key + return false; + } + } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { + if (vchPubKey.size() != 33) { + // Non-canonical public key: invalid length for compressed key + return false; + } + } else { + // Non-canonical public key: neither compressed nor uncompressed + return false; + } + return true; +} + +static bool IsCompressedPubKey(const valtype &vchPubKey) { + if (vchPubKey.size() != 33) { + // Non-canonical public key: invalid length for compressed key + return false; + } + if (vchPubKey[0] != 0x02 && vchPubKey[0] != 0x03) { + // Non-canonical public key: invalid prefix for compressed key + return false; + } + return true; +} + +bool CheckPubKeyEncoding(const valtype &vchPubKey, uint32_t flags, + ScriptError *serror) { + if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && + !IsCompressedOrUncompressedPubKey(vchPubKey)) { + return set_error(serror, SCRIPT_ERR_PUBKEYTYPE); + } + // Only compressed keys are accepted when + // SCRIPT_VERIFY_COMPRESSED_PUBKEYTYPE is enabled. + if ((flags & SCRIPT_VERIFY_COMPRESSED_PUBKEYTYPE) && + !IsCompressedPubKey(vchPubKey)) { + return set_error(serror, SCRIPT_ERR_NONCOMPRESSED_PUBKEY); + } + return true; +} diff --git a/src/script/standard.h b/src/script/standard.h --- a/src/script/standard.h +++ b/src/script/standard.h @@ -6,7 +6,9 @@ #ifndef BITCOIN_SCRIPT_STANDARD_H #define BITCOIN_SCRIPT_STANDARD_H -#include "script/interpreter.h" +#include "amount.h" +#include "pubkey.h" +#include "script/script_flags.h" #include "uint256.h" #include diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -6,6 +6,7 @@ #include "consensus/validation.h" #include "key.h" #include "pubkey.h" +#include "script/interpreter.h" #include "script/script.h" #include "script/standard.h" #include "test/test_bitcoin.h"