Add policy: null signature for failed CHECK(MULTI)SIG

This commit is contained in:
Johnson Lau 2016-09-22 15:06:54 +08:00 committed by Johnson Lau
parent 5a4f6d72e6
commit e41bd449ab
8 changed files with 55 additions and 2 deletions

View file

@ -43,6 +43,7 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
SCRIPT_VERIFY_CLEANSTACK | SCRIPT_VERIFY_CLEANSTACK |
SCRIPT_VERIFY_MINIMALIF | SCRIPT_VERIFY_MINIMALIF |
SCRIPT_VERIFY_NULLFAIL |
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
SCRIPT_VERIFY_CHECKSEQUENCEVERIFY | SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_LOW_S |

View file

@ -885,6 +885,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
} }
bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size())
return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
popstack(stack); popstack(stack);
popstack(stack); popstack(stack);
stack.push_back(fSuccess ? vchTrue : vchFalse); stack.push_back(fSuccess ? vchTrue : vchFalse);
@ -914,6 +917,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
if (nOpCount > MAX_OPS_PER_SCRIPT) if (nOpCount > MAX_OPS_PER_SCRIPT)
return set_error(serror, SCRIPT_ERR_OP_COUNT); return set_error(serror, SCRIPT_ERR_OP_COUNT);
int ikey = ++i; int ikey = ++i;
// ikey2 is the position of last non-signature item in the stack. Top stack item = 1.
// With SCRIPT_VERIFY_NULLFAIL, this is used for cleanup if operation fails.
int ikey2 = nKeysCount + 2;
i += nKeysCount; i += nKeysCount;
if ((int)stack.size() < i) if ((int)stack.size() < i)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
@ -970,8 +976,14 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
} }
// Clean up stack of actual arguments // Clean up stack of actual arguments
while (i-- > 1) while (i-- > 1) {
// If the operation failed, we require that all signatures must be empty vector
if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && !ikey2 && stacktop(-1).size())
return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
if (ikey2 > 0)
ikey2--;
popstack(stack); popstack(stack);
}
// A bug causes CHECKMULTISIG to consume one extra argument // A bug causes CHECKMULTISIG to consume one extra argument
// whose contents were not checked in any way. // whose contents were not checked in any way.

View file

@ -98,6 +98,10 @@ enum
// Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector // Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector
// //
SCRIPT_VERIFY_MINIMALIF = (1U << 13), SCRIPT_VERIFY_MINIMALIF = (1U << 13),
// Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed
//
SCRIPT_VERIFY_NULLFAIL = (1U << 14),
}; };
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror); bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);

View file

@ -65,6 +65,8 @@ const char* ScriptErrorString(const ScriptError serror)
return "Dummy CHECKMULTISIG argument must be zero"; return "Dummy CHECKMULTISIG argument must be zero";
case SCRIPT_ERR_MINIMALIF: case SCRIPT_ERR_MINIMALIF:
return "OP_IF/NOTIF argument must be minimal"; return "OP_IF/NOTIF argument must be minimal";
case SCRIPT_ERR_SIG_NULLFAIL:
return "Signature must be zero for failed CHECK(MULTI)SIG operation";
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS: case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS:
return "NOPx reserved for soft-fork upgrades"; return "NOPx reserved for soft-fork upgrades";
case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM: case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM:

View file

@ -49,6 +49,7 @@ typedef enum ScriptError_t
SCRIPT_ERR_PUBKEYTYPE, SCRIPT_ERR_PUBKEYTYPE,
SCRIPT_ERR_CLEANSTACK, SCRIPT_ERR_CLEANSTACK,
SCRIPT_ERR_MINIMALIF, SCRIPT_ERR_MINIMALIF,
SCRIPT_ERR_SIG_NULLFAIL,
/* softfork safeness */ /* softfork safeness */
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,

View file

@ -1491,6 +1491,27 @@
"OK", "OK",
"BIP66 example 4, with DERSIG" "BIP66 example 4, with DERSIG"
], ],
[
"0x09 0x300602010102010101",
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT",
"DERSIG",
"OK",
"BIP66 example 4, with DERSIG, non-null DER-compliant signature"
],
[
"0",
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT",
"DERSIG,NULLFAIL",
"OK",
"BIP66 example 4, with DERSIG and NULLFAIL"
],
[
"0x09 0x300602010102010101",
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG NOT",
"DERSIG,NULLFAIL",
"NULLFAIL",
"BIP66 example 4, with DERSIG and NULLFAIL, non-null DER-compliant signature"
],
[ [
"1", "1",
"0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG", "0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508 CHECKSIG",
@ -2208,5 +2229,15 @@
[["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "UNBALANCED_CONDITIONAL"], [["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS", "UNBALANCED_CONDITIONAL"],
[["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"], [["645168", 0.00000001], "0x22 0x0020f913eacf2e38a5d6fc3a8311d72ae704cb83866350a984dd3e5eb76d2a8c28e8", "HASH160 0x14 0xdbb7d1c0a56b7a9c423300c8cca6e6e065baf1dc EQUAL", "P2SH,WITNESS,MINIMALIF", "UNBALANCED_CONDITIONAL"],
["The End"] ["NULLFAIL should cover all signatures and signatures only"],
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66 and NULLFAIL-compliant"],
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "OK", "BIP66 and NULLFAIL-compliant"],
["1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "OK", "BIP66 and NULLFAIL-compliant, not NULLDUMMY-compliant"],
["1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL,NULLDUMMY", "SIG_NULLDUMMY", "BIP66 and NULLFAIL-compliant, not NULLDUMMY-compliant"],
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x09 0x300602010102010101", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66-compliant but not NULLFAIL-compliant"],
["0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0x09 0x300602010102010101", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"],
["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG", "OK", "BIP66-compliant but not NULLFAIL-compliant"],
["0 0x09 0x300602010102010101 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", "0x01 0x14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0x01 0x14 CHECKMULTISIG NOT", "DERSIG,NULLFAIL", "NULLFAIL", "BIP66-compliant but not NULLFAIL-compliant"],
["The End"]
] ]

View file

@ -90,6 +90,7 @@ static ScriptErrorDesc script_errors[]={
{SCRIPT_ERR_PUBKEYTYPE, "PUBKEYTYPE"}, {SCRIPT_ERR_PUBKEYTYPE, "PUBKEYTYPE"},
{SCRIPT_ERR_CLEANSTACK, "CLEANSTACK"}, {SCRIPT_ERR_CLEANSTACK, "CLEANSTACK"},
{SCRIPT_ERR_MINIMALIF, "MINIMALIF"}, {SCRIPT_ERR_MINIMALIF, "MINIMALIF"},
{SCRIPT_ERR_SIG_NULLFAIL, "NULLFAIL"},
{SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, "DISCOURAGE_UPGRADABLE_NOPS"}, {SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, "DISCOURAGE_UPGRADABLE_NOPS"},
{SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM, "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"}, {SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM, "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"},
{SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH, "WITNESS_PROGRAM_WRONG_LENGTH"}, {SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH, "WITNESS_PROGRAM_WRONG_LENGTH"},

View file

@ -51,6 +51,7 @@ static std::map<string, unsigned int> mapFlagNames = boost::assign::map_list_of
(string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) (string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
(string("CLEANSTACK"), (unsigned int)SCRIPT_VERIFY_CLEANSTACK) (string("CLEANSTACK"), (unsigned int)SCRIPT_VERIFY_CLEANSTACK)
(string("MINIMALIF"), (unsigned int)SCRIPT_VERIFY_MINIMALIF) (string("MINIMALIF"), (unsigned int)SCRIPT_VERIFY_MINIMALIF)
(string("NULLFAIL"), (unsigned int)SCRIPT_VERIFY_NULLFAIL)
(string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY) (string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)
(string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY) (string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)
(string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS) (string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS)