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 @@ -61,6 +61,9 @@ SCRIPT_ERR_MINIMALIF, SCRIPT_ERR_SIG_NULLFAIL, + /* Schnorr */ + SCRIPT_ERR_SIG_BADLENGTH, + /* softfork safeness */ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -82,6 +82,8 @@ case SCRIPT_ERR_SIG_NULLFAIL: return "Signature must be zero for failed CHECK(MULTI)SIG " "operation"; + case SCRIPT_ERR_SIG_BADLENGTH: + return "Signature cannot be 65 bytes in CHECKMULTISIG"; case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS: return "NOPx reserved for soft-fork upgrades"; case SCRIPT_ERR_PUBKEYTYPE: diff --git a/src/script/script_flags.h b/src/script/script_flags.h --- a/src/script/script_flags.h +++ b/src/script/script_flags.h @@ -99,7 +99,8 @@ // SCRIPT_ENABLE_CHECKDATASIG = (1U << 18), - // Are Schnorr signatures enabled for OP_CHECK(DATA)SIG(VERIFY)? + // Are Schnorr signatures enabled for OP_CHECK(DATA)SIG(VERIFY) and + // 65-byte signatures banned for OP_CHECKMULTISIG(VERIFY)? // SCRIPT_ENABLE_SCHNORR = (1U << 19), }; diff --git a/src/script/sigencoding.cpp b/src/script/sigencoding.cpp --- a/src/script/sigencoding.cpp +++ b/src/script/sigencoding.cpp @@ -156,6 +156,11 @@ static bool CheckRawECDSASignatureEncoding(const slicedvaltype &sig, uint32_t flags, ScriptError *serror) { + if ((flags & SCRIPT_ENABLE_SCHNORR) && (sig.size() == 64)) { + // In an ECDSA-only context, 64-byte signatures are banned when + // Schnorr flag set. + return set_error(serror, SCRIPT_ERR_SIG_BADLENGTH); + } if ((flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC)) && !IsValidDERSignatureEncoding(sig)) { @@ -171,6 +176,11 @@ static bool CheckRawSignatureEncoding(const slicedvaltype &sig, uint32_t flags, ScriptError *serror) { + if ((flags & SCRIPT_ENABLE_SCHNORR) && (sig.size() == 64)) { + // In a generic-signature context, 64-byte signatures are interpreted + // as Schnorr signatures (always correctly encoded) when flag set. + return true; + } return CheckRawECDSASignatureEncoding(sig, flags, serror); } diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -1572,6 +1572,30 @@ ["0 0 0x02 0x0000", "CHECKMULTISIGVERIFY 1", "MINIMALDATA", "UNKNOWN_ERROR"], ["0 0x02 0x0000 0", "CHECKMULTISIGVERIFY 1", "MINIMALDATA", "UNKNOWN_ERROR"], +["64/65-byte sig length tests"], +["0x41 0x303e021d4444444444444444444444444444444444444444444444444444444444021d444444444444444444444444444444444444444444444444444444444401", "0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 CHECKSIG NOT", "", "OK"], +["0x40 0x303e021d4444444444444444444444444444444444444444444444444444444444021d4444444444444444444444444444444444444444444444444444444444", "0 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 CHECKDATASIG NOT", "CHECKDATASIG", "OK"], +["0x41 0x303e021d4444444444444444444444444444444444444444444444444444444444021d444444444444444444444444444444444444444444444444444444444401", " 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 CHECKSIG NOT", "SCHNORR", "OK"], +["0x40 0x303e021d4444444444444444444444444444444444444444444444444444444444021d4444444444444444444444444444444444444444444444444444444444", "0 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 CHECKDATASIG NOT", "CHECKDATASIG,SCHNORR", "OK"], +["0 0x41 0x303e021d4444444444444444444444444444444444444444444444444444444444021d444444444444444444444444444444444444444444444444444444444401", "1 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 1 CHECKMULTISIG NOT", "", "OK"], +["0 0x41 0x303e021d4444444444444444444444444444444444444444444444444444444444021d444444444444444444444444444444444444444444444444444444444401", "1 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 1 CHECKMULTISIG NOT", "STRICTENC", "OK"], +["0 0x41 0x303e021d4444444444444444444444444444444444444444444444444444444444021d444444444444444444444444444444444444444444444444444444444401", "1 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 1 CHECKMULTISIG NOT", "SCHNORR", "SIG_BADLENGTH"], +["0 0x41 0x303e021d4444444444444444444444444444444444444444444444444444444444021d444444444444444444444444444444444444444444444444444444444401", "1 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 1 CHECKMULTISIG NOT", "SCHNORR,STRICTENC,NULLFAIL", "SIG_BADLENGTH"], +["0 0x40 0x303d021d4444444444444444444444444444444444444444444444444444444444021c4444444444444444444444444444444444444444444444444444444401", "1 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 1 CHECKMULTISIG NOT", "SCHNORR", "OK"], +["0 0x42 0x303f021d4444444444444444444444444444444444444444444444444444444444021e44444444444444444444444444444444444444444444444444444444444401", "1 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 1 CHECKMULTISIG NOT", "SCHNORR", "OK"], + +["0x41 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 CHECKSIG NOT", "", "OK"], +["0x41 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 CHECKSIG NOT", "STRICTENC", "SIG_DER"], +["0x41 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 CHECKSIG NOT", "STRICTENC,SCHNORR", "OK"], +["0x40 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 CHECKDATASIG NOT", "CHECKDATASIG", "OK"], +["0x40 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 CHECKDATASIG NOT", "CHECKDATASIG,STRICTENC", "SIG_DER"], +["0x40 0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 CHECKDATASIG NOT", "CHECKDATASIG,STRICTENC,SCHNORR", "OK"], +["0 0x41 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "1 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 1 CHECKMULTISIG NOT", "", "OK"], +["0 0x41 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "1 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 1 CHECKMULTISIG NOT", "STRICTENC", "SIG_DER"], +["0 0x41 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "1 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 1 CHECKMULTISIG NOT", "STRICTENC,SCHNORR", "SIG_BADLENGTH"], +["0 0x41 0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "1 0x21 0x0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 1 CHECKMULTISIG NOT", "STRICTENC,NULLFAIL,SCHNORR", "SIG_BADLENGTH"], + + ["Order of CHECKMULTISIG evaluation tests, inverted by swapping the order of"], ["pubkeys/signatures so they fail due to the STRICTENC rules on validly encoded"], diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -81,6 +81,7 @@ {SCRIPT_ERR_CLEANSTACK, "CLEANSTACK"}, {SCRIPT_ERR_MINIMALIF, "MINIMALIF"}, {SCRIPT_ERR_SIG_NULLFAIL, "NULLFAIL"}, + {SCRIPT_ERR_SIG_BADLENGTH, "SIG_BADLENGTH"}, {SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, "DISCOURAGE_UPGRADABLE_NOPS"}, {SCRIPT_ERR_NONCOMPRESSED_PUBKEY, "NONCOMPRESSED_PUBKEY"}, {SCRIPT_ERR_ILLEGAL_FORKID, "ILLEGAL_FORKID"}, diff --git a/src/test/sigencoding_tests.cpp b/src/test/sigencoding_tests.cpp --- a/src/test/sigencoding_tests.cpp +++ b/src/test/sigencoding_tests.cpp @@ -177,7 +177,7 @@ BOOST_CHECK(CheckTransactionSignatureEncoding({}, flags, &err)); BOOST_CHECK(CheckTransactionECDSASignatureEncoding({}, flags, &err)); - // Signature are valid as long as the forkid flag is correct. + // Signatures are valid as long as the forkid flag is correct. CheckSignatureEncodingWithSigHashType(minimalSig, flags); if (flags & SCRIPT_VERIFY_LOW_S) { @@ -370,4 +370,78 @@ } } +BOOST_AUTO_TEST_CASE(checkschnorr_test) { + // tests using 64 byte sigs (+hashtype byte where relevant) + valtype Zero64{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + valtype DER64{0x30, 0x3e, 0x02, 0x1d, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x02, 0x1d, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44}; + + BOOST_REQUIRE_EQUAL(Zero64.size(), 64); + BOOST_REQUIRE_EQUAL(DER64.size(), 64); + + MMIXLinearCongruentialGenerator lcg; + for (int i = 0; i < 4096; i++) { + uint32_t flags = lcg.next(); + + const bool hasForkId = (flags & SCRIPT_ENABLE_SIGHASH_FORKID) != 0; + const bool hasSchnorr = (flags & SCRIPT_ENABLE_SCHNORR) != 0; + const bool hasStricts = + (flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | + SCRIPT_VERIFY_STRICTENC)) != 0; + + ScriptError err = SCRIPT_ERR_OK; + valtype DER65_hb = + SignatureWithHashType(DER64, SigHashType().withForkId(hasForkId)); + valtype Zero65_hb = + SignatureWithHashType(Zero64, SigHashType().withForkId(hasForkId)); + + BOOST_CHECK(CheckDataSignatureEncoding(DER64, flags, &err)); + BOOST_CHECK(CheckTransactionSignatureEncoding(DER65_hb, flags, &err)); + BOOST_CHECK_EQUAL( + !CheckTransactionECDSASignatureEncoding(DER65_hb, flags, &err), + hasSchnorr); + if (hasSchnorr) { + BOOST_CHECK_EQUAL(err, SCRIPT_ERR_SIG_BADLENGTH); + } + + if (hasSchnorr) { + BOOST_CHECK(CheckDataSignatureEncoding(Zero64, flags, &err)); + BOOST_CHECK( + CheckTransactionSignatureEncoding(Zero65_hb, flags, &err)); + BOOST_CHECK(!CheckTransactionECDSASignatureEncoding(Zero65_hb, + flags, &err)); + BOOST_CHECK_EQUAL(err, SCRIPT_ERR_SIG_BADLENGTH); + } else { + BOOST_CHECK_EQUAL(!CheckDataSignatureEncoding(Zero64, flags, &err), + hasStricts); + if (hasStricts) { + BOOST_CHECK_EQUAL(err, SCRIPT_ERR_SIG_DER); + } + BOOST_CHECK_EQUAL( + !CheckTransactionSignatureEncoding(Zero65_hb, flags, &err), + hasStricts); + if (hasStricts) { + BOOST_CHECK_EQUAL(err, SCRIPT_ERR_SIG_DER); + } + BOOST_CHECK_EQUAL( + !CheckTransactionECDSASignatureEncoding(Zero65_hb, flags, &err), + hasStricts); + if (hasStricts) { + BOOST_CHECK_EQUAL(err, SCRIPT_ERR_SIG_DER); + } + } + } +} + BOOST_AUTO_TEST_SUITE_END()