diff --git a/src/script/sigencoding.cpp b/src/script/sigencoding.cpp index ea76ded50..c2695d838 100644 --- a/src/script/sigencoding.cpp +++ b/src/script/sigencoding.cpp @@ -1,246 +1,258 @@ // 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" #include typedef boost::sliced_range slicedvaltype; /** * 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 slicedvaltype &sig) { // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] // * 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. // Minimum and maximum size constraints. if (sig.size() < 8 || sig.size() > 72) { return false; } // // Check that the signature is a coumpound structure of proper size. // // A signature is of type 0x30 (compound). if (sig[0] != 0x30) { return false; } // Make sure the length covers the entire signature. // Remove: // * 1 byte for the coupound type. // * 1 byte for the length of the signature. if (sig[1] != sig.size() - 2) { return false; } // // Check that R is an positive integer of sensible size. // // Check whether the R element is an integer. if (sig[2] != 0x02) { return false; } // Extract the length of the R element. const uint32_t lenR = sig[3]; // 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; } // Make sure the length of the R element is consistent with the signature // size. // Remove: // * 1 byte for the coumpound type. // * 1 byte for the length of the signature. // * 2 bytes for the integer type of R and S. // * 2 bytes for the size of R and S. // * 1 byte for S itself. if (lenR > (sig.size() - 7)) { return false; } // Null bytes at the start of R are not allowed, unless R would otherwise be // interpreted as a negative number. // // /!\ This check can only be performed after we checked that lenR is // consistent with the size of the signature or we risk to access out of // bound elements. if (lenR > 1 && (sig[4] == 0x00) && !(sig[5] & 0x80)) { return false; } // // Check that S is an positive integer of sensible size. // // S's definition starts after R's definition: // * 1 byte for the coumpound type. // * 1 byte for the length of the signature. // * 1 byte for the size of R. // * lenR bytes for R itself. // * 1 byte to get to S. const uint32_t startS = lenR + 4; // Check whether the S element is an integer. if (sig[startS] != 0x02) { return false; } // Extract the length of the S element. const uint32_t lenS = sig[startS + 1]; // Zero-length integers are not allowed for S. if (lenS == 0) { return false; } // Negative numbers are not allowed for S. if (sig[startS + 2] & 0x80) { return false; } // Verify that the length of S is consistent with the size of the signature // including metadatas: // * 1 byte for the integer type of S. // * 1 byte for the size of S. if (size_t(startS + lenS + 2) != sig.size()) { return false; } // Null bytes at the start of S are not allowed, unless S would otherwise be // interpreted as a negative number. // // /!\ This check can only be performed after we checked that lenR and lenS // are consistent with the size of the signature or we risk to access // out of bound elements. if (lenS > 1 && (sig[startS + 2] == 0x00) && !(sig[startS + 3] & 0x80)) { return false; } return true; } static bool CheckRawSignatureEncoding(const slicedvaltype &sig, uint32_t flags, ScriptError *serror) { if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) && !IsValidSignatureEncoding(sig)) { return set_error(serror, SCRIPT_ERR_SIG_DER); } if ((flags & SCRIPT_VERIFY_LOW_S) && !CPubKey::CheckLowS(sig)) { return set_error(serror, SCRIPT_ERR_SIG_HIGH_S); } return true; } +bool CheckDataSignatureEncoding(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; + } + + return CheckRawSignatureEncoding( + vchSig | boost::adaptors::sliced(0, vchSig.size()), flags, serror); +} + bool CheckTransactionSignatureEncoding(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 (!CheckRawSignatureEncoding( vchSig | boost::adaptors::sliced(0, vchSig.size() - 1), flags, serror)) { // serror is set return false; } if (flags & SCRIPT_VERIFY_STRICTENC) { 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) { switch (vchPubKey.size()) { case 33: // Compressed public key: must start with 0x02 or 0x03. return vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03; case 65: // Non-compressed public key: must start with 0x04. return vchPubKey[0] == 0x04; default: // Non-canonical public key: invalid size. return false; } } 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) && !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/sigencoding.h b/src/script/sigencoding.h index e48a3b351..d8d32f4d6 100644 --- a/src/script/sigencoding.h +++ b/src/script/sigencoding.h @@ -1,44 +1,52 @@ // 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 +/** + * Check that the signature provided on some data is properly encoded. + * Signatures passed to OP_CHECKDATASIG and its verify variant must be checked + * using this function. + */ +bool CheckDataSignatureEncoding(const valtype &vchSig, uint32_t flags, + ScriptError *serror); + /** * Check that the signature provided to authentify a transaction is properly * encoded. Signatures passed to OP_CHECKSIG, OP_CHECKMULTISIG and their verify * variants must be checked using this function. */ bool CheckTransactionSignatureEncoding(const valtype &vchSig, uint32_t flags, ScriptError *serror); /** * Check that a public key is encoded properly. */ bool CheckPubKeyEncoding(const valtype &vchPubKey, uint32_t flags, ScriptError *serror); #endif // BITCOIN_SCRIPT_SIGENCODING_H diff --git a/src/test/sigencoding_tests.cpp b/src/test/sigencoding_tests.cpp index e903161f1..9118213c6 100644 --- a/src/test/sigencoding_tests.cpp +++ b/src/test/sigencoding_tests.cpp @@ -1,369 +1,374 @@ // Copyright (c) 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 "test/test_bitcoin.h" #include "script/script_flags.h" #include "script/sigencoding.h" #include BOOST_FIXTURE_TEST_SUITE(sigencoding_tests, BasicTestingSetup) static valtype SignatureWithHashType(valtype vchSig, SigHashType sigHash) { vchSig.push_back(static_cast(sigHash.getRawSigHashType())); return vchSig; } static void CheckSignatureErrorForAllSigHashType(const valtype &vchSig, uint32_t flags, const ScriptError expected_error) { + ScriptError err = SCRIPT_ERR_OK; + BOOST_CHECK(!CheckDataSignatureEncoding(vchSig, flags, &err)); + BOOST_CHECK_EQUAL(err, expected_error); + for (int i = 0; i <= 0xff; i++) { - ScriptError err = SCRIPT_ERR_OK; valtype sig = SignatureWithHashType(vchSig, SigHashType(i)); BOOST_CHECK(!CheckTransactionSignatureEncoding(sig, flags, &err)); BOOST_CHECK_EQUAL(err, expected_error); } } static void CheckSignatureEncodingWithSigHashType(const valtype &vchSig, uint32_t flags) { + ScriptError err = SCRIPT_ERR_OK; + BOOST_CHECK(CheckDataSignatureEncoding(vchSig, flags, &err)); + const bool hasForkId = (flags & SCRIPT_ENABLE_SIGHASH_FORKID) != 0; const bool hasStrictEnc = (flags & SCRIPT_VERIFY_STRICTENC) != 0; std::vector allBaseTypes{ BaseSigHashType::ALL, BaseSigHashType::NONE, BaseSigHashType::SINGLE}; std::vector baseSigHashes; for (const BaseSigHashType baseType : allBaseTypes) { const SigHashType baseSigHash = SigHashType().withBaseType(baseType); baseSigHashes.push_back(baseSigHash); baseSigHashes.push_back(baseSigHash.withAnyoneCanPay(true)); } for (const SigHashType baseSigHash : baseSigHashes) { - ScriptError err = SCRIPT_ERR_OK; - // Check the signature with the proper forkid flag. SigHashType sigHash = baseSigHash.withForkId(hasForkId); valtype validSig = SignatureWithHashType(vchSig, sigHash); BOOST_CHECK(CheckTransactionSignatureEncoding(validSig, flags, &err)); // If we have strict encoding, we prevent the use of undefined flags. std::array undefSigHashes{ SigHashType(sigHash.getRawSigHashType() | 0x20), sigHash.withBaseType(BaseSigHashType::UNSUPPORTED), }; for (SigHashType undefSigHash : undefSigHashes) { valtype undefSighash = SignatureWithHashType(vchSig, undefSigHash); BOOST_CHECK_EQUAL( CheckTransactionSignatureEncoding(undefSighash, flags, &err), !hasStrictEnc); if (hasStrictEnc) { BOOST_CHECK_EQUAL(err, SCRIPT_ERR_SIG_HASHTYPE); } } // If we check strict encoding, then invalid forkid is an error. SigHashType invalidSigHash = baseSigHash.withForkId(!hasForkId); valtype invalidSig = SignatureWithHashType(vchSig, invalidSigHash); BOOST_CHECK_EQUAL( CheckTransactionSignatureEncoding(invalidSig, flags, &err), !hasStrictEnc); if (hasStrictEnc) { BOOST_CHECK_EQUAL(err, hasForkId ? SCRIPT_ERR_MUST_USE_FORKID : SCRIPT_ERR_ILLEGAL_FORKID); } } } BOOST_AUTO_TEST_CASE(checksignatureencoding_test) { valtype minimalSig{0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}; valtype highSSig{ 0x30, 0x45, 0x02, 0x20, 0x3e, 0x45, 0x16, 0xda, 0x72, 0x53, 0xcf, 0x06, 0x8e, 0xff, 0xec, 0x6b, 0x95, 0xc4, 0x12, 0x21, 0xc0, 0xcf, 0x3a, 0x8e, 0x6c, 0xcb, 0x8c, 0xbf, 0x17, 0x25, 0xb5, 0x62, 0xe9, 0xaf, 0xde, 0x2c, 0x02, 0x21, 0x00, 0xab, 0x1e, 0x3d, 0xa7, 0x3d, 0x67, 0xe3, 0x20, 0x45, 0xa2, 0x0e, 0x0b, 0x99, 0x9e, 0x04, 0x99, 0x78, 0xea, 0x8d, 0x6e, 0xe5, 0x48, 0x0d, 0x48, 0x5f, 0xcf, 0x2c, 0xe0, 0xd0, 0x3b, 0x2e, 0xf0}; std::vector nonDERSigs{ // Non canonical total length. {0x30, 0x80, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}, // Zero length R. {0x30, 0x2f, 0x02, 0x00, 0x02, 0x21, 0x00, 0xab, 0x1e, 0x3d, 0xa7, 0x3d, 0x67, 0xe3, 0x20, 0x45, 0xa2, 0x0e, 0x0b, 0x99, 0x9e, 0x04, 0x99, 0x78, 0xea, 0x8d, 0x6e, 0xe5, 0x48, 0x0d, 0x48, 0x5f, 0xcf, 0x2c, 0xe0, 0xd0, 0x3b, 0x2e, 0xf0}, // Non canonical length for R. {0x30, 0x31, 0x02, 0x80, 0x01, 0x6c, 0x02, 0x21, 0x00, 0xab, 0x1e, 0x3d, 0xa7, 0x3d, 0x67, 0xe3, 0x20, 0x45, 0xa2, 0x0e, 0x0b, 0x99, 0x9e, 0x04, 0x99, 0x78, 0xea, 0x8d, 0x6e, 0xe5, 0x48, 0x0d, 0x48, 0x5f, 0xcf, 0x2c, 0xe0, 0xd0, 0x3b, 0x2e, 0xf0}, // Negative R. {0x30, 0x30, 0x02, 0x01, 0x80, 0x02, 0x21, 0x00, 0xab, 0x1e, 0x3d, 0xa7, 0x3d, 0x67, 0xe3, 0x20, 0x45, 0xa2, 0x0e, 0x0b, 0x99, 0x9e, 0x04, 0x99, 0x78, 0xea, 0x8d, 0x6e, 0xe5, 0x48, 0x0d, 0x48, 0x5f, 0xcf, 0x2c, 0xe0, 0xd0, 0x3b, 0x2e, 0xf0}, // Null prefixed R. {0x30, 0x31, 0x02, 0x02, 0x00, 0x01, 0x02, 0x21, 0x00, 0xab, 0x1e, 0x3d, 0xa7, 0x3d, 0x67, 0xe3, 0x20, 0x45, 0xa2, 0x0e, 0x0b, 0x99, 0x9e, 0x04, 0x99, 0x78, 0xea, 0x8d, 0x6e, 0xe5, 0x48, 0x0d, 0x48, 0x5f, 0xcf, 0x2c, 0xe0, 0xd0, 0x3b, 0x2e, 0xf0}, // Zero length S. {0x30, 0x2f, 0x02, 0x21, 0x00, 0xab, 0x1e, 0x3d, 0xa7, 0x3d, 0x67, 0xe3, 0x20, 0x45, 0xa2, 0x0e, 0x0b, 0x99, 0x9e, 0x04, 0x99, 0x78, 0xea, 0x8d, 0x6e, 0xe5, 0x48, 0x0d, 0x48, 0x5f, 0xcf, 0x2c, 0xe0, 0xd0, 0x3b, 0x2e, 0xf0, 0x02, 0x00}, // Non canonical length for S. {0x30, 0x31, 0x02, 0x21, 0x00, 0xab, 0x1e, 0x3d, 0xa7, 0x3d, 0x67, 0xe3, 0x20, 0x45, 0xa2, 0x0e, 0x0b, 0x99, 0x9e, 0x04, 0x99, 0x78, 0xea, 0x8d, 0x6e, 0xe5, 0x48, 0x0d, 0x48, 0x5f, 0xcf, 0x2c, 0xe0, 0xd0, 0x3b, 0x2e, 0xf0, 0x02, 0x80, 0x01, 0x6c}, // Negative S. {0x30, 0x30, 0x02, 0x21, 0x00, 0xab, 0x1e, 0x3d, 0xa7, 0x3d, 0x67, 0xe3, 0x20, 0x45, 0xa2, 0x0e, 0x0b, 0x99, 0x9e, 0x04, 0x99, 0x78, 0xea, 0x8d, 0x6e, 0xe5, 0x48, 0x0d, 0x48, 0x5f, 0xcf, 0x2c, 0xe0, 0xd0, 0x3b, 0x2e, 0xf0, 0x02, 0x01, 0x80}, // Null prefixed S. {0x30, 0x31, 0x02, 0x21, 0x00, 0xab, 0x1e, 0x3d, 0xa7, 0x3d, 0x67, 0xe3, 0x20, 0x45, 0xa2, 0x0e, 0x0b, 0x99, 0x9e, 0x04, 0x99, 0x78, 0xea, 0x8d, 0x6e, 0xe5, 0x48, 0x0d, 0x48, 0x5f, 0xcf, 0x2c, 0xe0, 0xd0, 0x3b, 0x2e, 0xf0, 0x02, 0x02, 0x00, 0x01}, }; std::vector nonParsableSigs{ // Too short. {0x30}, {0x30, 0x06}, {0x30, 0x06, 0x02}, {0x30, 0x06, 0x02, 0x01}, {0x30, 0x06, 0x02, 0x01, 0x01}, {0x30, 0x06, 0x02, 0x01, 0x01, 0x02}, {0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01}, // Invalid type (must be 0x30, coumpound). {0x42, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}, // Invalid sizes. {0x30, 0x05, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}, {0x30, 0x07, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}, // Invalid R and S sizes. {0x30, 0x06, 0x02, 0x00, 0x01, 0x02, 0x01, 0x01}, {0x30, 0x06, 0x02, 0x02, 0x01, 0x02, 0x01, 0x01}, {0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x00, 0x01}, {0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01}, // Invalid R and S types. {0x30, 0x06, 0x42, 0x01, 0x01, 0x02, 0x01, 0x01}, {0x30, 0x06, 0x02, 0x01, 0x01, 0x42, 0x01, 0x01}, // Too long. {0x30, 0x47, 0x02, 0x21, 0x00, 0x8e, 0x45, 0x16, 0xda, 0x72, 0x53, 0xcf, 0x06, 0x8e, 0xff, 0xec, 0x6b, 0x95, 0xc4, 0x12, 0x21, 0xc0, 0xcf, 0x3a, 0x8e, 0x6c, 0xcb, 0x8c, 0xbf, 0x17, 0x25, 0xb5, 0x62, 0xe9, 0xaf, 0xde, 0x2c, 0x02, 0x22, 0x00, 0xab, 0x1e, 0x3d, 0x00, 0xa7, 0x3d, 0x67, 0xe3, 0x20, 0x45, 0xa2, 0x0e, 0x0b, 0x99, 0x9e, 0x04, 0x99, 0x78, 0xea, 0x8d, 0x6e, 0xe5, 0x48, 0x0d, 0x48, 0x5f, 0xcf, 0x2c, 0xe0, 0xd0, 0x3b, 0x2e, 0xf0}, }; // If we add many more flags, this loop can get too expensive, but we can // rewrite in the future to randomly pick a set of flags to evaluate. for (uint32_t flags = 0; flags < (1U << 17); flags++) { ScriptError err = SCRIPT_ERR_OK; // Empty sig is always valid. + BOOST_CHECK(CheckDataSignatureEncoding({}, flags, &err)); BOOST_CHECK(CheckTransactionSignatureEncoding({}, flags, &err)); // Signature are valid as long as the forkid flag is correct. CheckSignatureEncodingWithSigHashType(minimalSig, flags); if (flags & SCRIPT_VERIFY_LOW_S) { // If we do enforce low S, then high S sigs are rejected. CheckSignatureErrorForAllSigHashType(highSSig, flags, SCRIPT_ERR_SIG_HIGH_S); } else { // If we do not enforce low S, then high S sigs are accepted. CheckSignatureEncodingWithSigHashType(highSSig, flags); } for (const valtype &nonDERSig : nonDERSigs) { if (flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) { // If we get any of the dersig flags, the non canonical dersig // signature fails. CheckSignatureErrorForAllSigHashType(nonDERSig, flags, SCRIPT_ERR_SIG_DER); } else { // If we do not check, then it is accepted. CheckSignatureEncodingWithSigHashType(nonDERSig, flags); } } for (const valtype &nonDERSig : nonParsableSigs) { if (flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) { // If we get any of the dersig flags, the invalid signature // fails. CheckSignatureErrorForAllSigHashType(nonDERSig, flags, SCRIPT_ERR_SIG_DER); } else { // If we do not check, then it is accepted even though it cannot // even be parsed. CheckSignatureEncodingWithSigHashType(nonDERSig, flags); } } } } BOOST_AUTO_TEST_CASE(checkpubkeyencoding_test) { valtype compressedKey0{0x02, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}; valtype compressedKey1{0x03, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}; valtype fullKey{0x04, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}; std::vector invalidKeys{ // Degenerate keys. {}, {0x00}, {0x01}, {0x02}, {0x03}, {0x04}, {0x05}, {0x42}, {0xff}, // Invalid first byte 0x00. {0x00, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, {0x00, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}, // Invalid first byte 0x01. {0x01, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, {0x00, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}, // Invalid first byte 0x05. {0x05, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, {0x05, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}, // Invalid first byte 0xff. {0xff, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, {0xff, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}, // Compressed key too short. {0x02, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, {0x03, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}, // Compressed key too long. {0x02, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0xab, 0xba, 0x9a, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, {0x03, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0xab, 0xba, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}, // Compressed key, full key size. {0x02, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}, {0x03, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}, // Full key, too short. {0x04, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}, // Full key, too long. {0x04, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x56, 0x78, 0xab, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xff}, // Full key, compressed key size. {0x04, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0xab, 0xba, 0x9a, 0xde, 0xf0, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}, }; // If we add many more flags, this loop can get too expensive, but we can // rewrite in the future to randomly pick a set of flags to evaluate. for (uint32_t flags = 0; flags < (1U << 17); flags++) { ScriptError err = SCRIPT_ERR_OK; // Compressed pubkeys are always valid. BOOST_CHECK(CheckPubKeyEncoding(compressedKey0, flags, &err)); BOOST_CHECK(CheckPubKeyEncoding(compressedKey1, flags, &err)); // If SCRIPT_VERIFY_COMPRESSED_PUBKEYTYPE is specified, full key are // disabled. const bool allowFullKey = (flags & SCRIPT_VERIFY_COMPRESSED_PUBKEYTYPE) == 0; BOOST_CHECK_EQUAL(CheckPubKeyEncoding(fullKey, flags, &err), allowFullKey); if (!allowFullKey) { BOOST_CHECK_EQUAL(err, SCRIPT_ERR_NONCOMPRESSED_PUBKEY); } // If SCRIPT_VERIFY_STRICTENC or SCRIPT_VERIFY_COMPRESSED_PUBKEYTYPE is // specified, we rule out invalid keys. const bool hasStrictEnc = (flags & SCRIPT_VERIFY_STRICTENC) != 0; const bool allowInvalidKeys = allowFullKey && !hasStrictEnc; for (const valtype &key : invalidKeys) { BOOST_CHECK_EQUAL(CheckPubKeyEncoding(key, flags, &err), allowInvalidKeys); if (!allowInvalidKeys) { BOOST_CHECK_EQUAL(err, hasStrictEnc ? SCRIPT_ERR_PUBKEYTYPE : SCRIPT_ERR_NONCOMPRESSED_PUBKEY); } } } } BOOST_AUTO_TEST_SUITE_END()