From e6740a0ada378efcedb64fd114c8115baa959a61 Mon Sep 17 00:00:00 2001 From: "rearden-grok[bot]" <317016512+rearden-grok[bot]@users.noreply.github.com> Date: Sat, 17 Oct 2026 21:21:21 -0700 Subject: [PATCH] consensus: pin executed OP_VERIF and raw ECDSA hashtypes OP_VERIF and OP_VERNOTIF already fail in an untaken branch. These rows also fail them in the taken branch. DERSIG still accepts hashtype 0x00, 0x80, and 0x85, STRICTENC rejects them, and rewriting the type byte makes the signature miss. --- changelog.d/verif-sighash-corpus.md | 3 + crates/rbitcoin-consensus/src/script/mod.rs | 16 + .../tests/fixtures/script_corpus/INDEX.md | 26 +- .../fixtures/script_corpus/script_tests.json | 279 ++++++++++++++++++ 4 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 changelog.d/verif-sighash-corpus.md diff --git a/changelog.d/verif-sighash-corpus.md b/changelog.d/verif-sighash-corpus.md new file mode 100644 index 0000000000..0e6304e123 --- /dev/null +++ b/changelog.d/verif-sighash-corpus.md @@ -0,0 +1,3 @@ +Added + +- **The script corpus pins executed `OP_VERIF` / `OP_VERNOTIF` and ECDSA hashtypes `0x00`, `0x80`, and `0x85`.** DERSIG still accepts those hashtypes. STRICTENC rejects them. Rewriting the type byte rejects the signature. diff --git a/crates/rbitcoin-consensus/src/script/mod.rs b/crates/rbitcoin-consensus/src/script/mod.rs index b62b0d7b84..b4f03c936c 100644 --- a/crates/rbitcoin-consensus/src/script/mod.rs +++ b/crates/rbitcoin-consensus/src/script/mod.rs @@ -879,6 +879,22 @@ pub(crate) mod crypto { assert!(!is_valid_signature_encoding(&sm)); assert!(is_valid_signature_encoding(&valid_der_sig())); + // BIP66 does not range-check the hashtype byte. STRICTENC does, + // via `is_defined_hashtype`. `0x00`, `0x04`, `0x80`, and `0x85` + // stay valid DER. + for ht in [0x00u8, 0x04, 0x80, 0x85] { + let mut sig = valid_der_sig(); + let last = sig.len() - 1; + sig[last] = ht; + assert!( + is_valid_signature_encoding(&sig), + "BIP66 keeps hashtype {ht:#x}" + ); + assert!( + !is_defined_hashtype(&sig), + "STRICTENC rejects hashtype {ht:#x}" + ); + } // A one-byte zero integer is minimal. `len > 1` must not become `>=`. assert!(is_valid_signature_encoding(&[ 0x30, 0x06, 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, 0x01 diff --git a/crates/rbitcoin-consensus/tests/fixtures/script_corpus/INDEX.md b/crates/rbitcoin-consensus/tests/fixtures/script_corpus/INDEX.md index afad2d5623..e6460b6eac 100644 --- a/crates/rbitcoin-consensus/tests/fixtures/script_corpus/INDEX.md +++ b/crates/rbitcoin-consensus/tests/fixtures/script_corpus/INDEX.md @@ -81,6 +81,12 @@ through `VerifyScript`. Legacy rows are bare scripts with no flags. | `vernotif-unexec-legacy` | OP_VERNOTIF rejects even inside an unexecuted IF, in every signature version. | | `vernotif-unexec-v0` | OP_VERNOTIF rejects even inside an unexecuted IF, in every signature version. | | `vernotif-unexec-tapscript` | OP_VERNOTIF rejects even inside an unexecuted IF, in every signature version. | +| `verif-exec-legacy` | OP_VERIF in the taken IF branch rejects, in every signature version. | +| `verif-exec-v0` | OP_VERIF in the taken IF branch rejects, in every signature version. | +| `verif-exec-tapscript` | OP_VERIF in the taken IF branch rejects, in every signature version. | +| `vernotif-exec-legacy` | OP_VERNOTIF in the taken IF branch rejects, in every signature version. | +| `vernotif-exec-v0` | OP_VERNOTIF in the taken IF branch rejects, in every signature version. | +| `vernotif-exec-tapscript` | OP_VERNOTIF in the taken IF branch rejects, in every signature version. | | `stack-1000-legacy` | Legacy main stack of 1,000 accepts. Cleanstack is off. | | `stack-1001-legacy` | Legacy main stack of 1,001 rejects. | | `stack-alt-1000-legacy` | Legacy main stack plus altstack of 1,000 accepts: 201 items moved aside, then 799 more. | @@ -297,7 +303,7 @@ Core `script_tests.json` already has both an accept and a reject for NOP, VERIFY An optional `spend` object sets `version`, `locktime`, `sequence`, and `n_outputs` on the spending transaction only. A missing object is version 1, locktime 0, sequence `0xffffffff`, and one output. `version` is the consensus i32 bit pattern. -Unknown hashtype bytes are a STRICTENC check. DERSIG checks the DER encoding and still accepts hashtype `0x04`. LOW_S stays off. +Unknown hashtype bytes are a STRICTENC check. DERSIG checks the DER encoding and still accepts hashtype `0x00`, `0x04`, `0x80`, and `0x85`. The signature is over that raw byte, so rewriting it to a standard type rejects. LOW_S stays off. FindAndDelete is observed with an empty signature. Its needle is the single byte `OP_0`. A verifying signature is not placed in `scriptPubKey`, because this credit template would make that signature commit to a txid that already contains it. A `0x00` buried in a push is the off-boundary case. A signature longer than `scriptCode` is the no-op already covered by `sighash-all-legacy`. @@ -338,6 +344,24 @@ Tapscript validation weight: one non-empty CHECKSIGADD against a 1-byte pubkey a | `sighash-type04-strictenc-legacy` | STRICTENC rejects hashtype 0x04. LOW_S stays off. | | `sighash-type04-dersig-v0` | Witness v0 DERSIG alone accepts hashtype 0x04. | | `sighash-type04-strictenc-v0` | Witness v0 STRICTENC rejects hashtype 0x04. | +| `sighash-type00-dersig-legacy` | DERSIG accepts hashtype 0x00. The digest uses that raw byte, not SIGHASH_ALL. | +| `sighash-type00-strictenc-legacy` | STRICTENC rejects hashtype 0x00. | +| `sighash-type00-as-01-legacy` | The 0x00 signature with its type byte rewritten to 0x01 rejects. | +| `sighash-type00-dersig-v0` | Witness v0 DERSIG accepts hashtype 0x00. | +| `sighash-type00-strictenc-v0` | Witness v0 STRICTENC rejects hashtype 0x00. | +| `sighash-type00-as-01-v0` | Witness v0 rejects the 0x00 signature rewritten to 0x01. | +| `sighash-type80-dersig-legacy` | DERSIG accepts hashtype 0x80 (ANYONECANPAY with base 0). | +| `sighash-type80-strictenc-legacy` | STRICTENC rejects hashtype 0x80. | +| `sighash-type80-as-81-legacy` | The 0x80 signature rewritten to 0x81 rejects. | +| `sighash-type80-dersig-v0` | Witness v0 DERSIG accepts hashtype 0x80. | +| `sighash-type80-strictenc-v0` | Witness v0 STRICTENC rejects hashtype 0x80. | +| `sighash-type80-as-81-v0` | Witness v0 rejects the 0x80 signature rewritten to 0x81. | +| `sighash-type85-dersig-legacy` | DERSIG accepts hashtype 0x85 (ANYONECANPAY with base 5). | +| `sighash-type85-strictenc-legacy` | STRICTENC rejects hashtype 0x85. | +| `sighash-type85-as-81-legacy` | The 0x85 signature rewritten to 0x81 rejects. | +| `sighash-type85-dersig-v0` | Witness v0 DERSIG accepts hashtype 0x85. | +| `sighash-type85-strictenc-v0` | Witness v0 STRICTENC rejects hashtype 0x85. | +| `sighash-type85-as-81-v0` | Witness v0 rejects the 0x85 signature rewritten to 0x81. | | `dersig-off-nondet-legacy` | With DERSIG and LOW_S off, a non-minimal S that the lax parser accepts still verifies. | | `dersig-on-nondet-legacy` | DERSIG rejects that non-minimal S. LOW_S stays off. | | `dersig-off-nondet-v0` | Witness v0 with DERSIG off accepts the same non-minimal S. | diff --git a/crates/rbitcoin-consensus/tests/fixtures/script_corpus/script_tests.json b/crates/rbitcoin-consensus/tests/fixtures/script_corpus/script_tests.json index f14959b213..a490ca6f9c 100644 --- a/crates/rbitcoin-consensus/tests/fixtures/script_corpus/script_tests.json +++ b/crates/rbitcoin-consensus/tests/fixtures/script_corpus/script_tests.json @@ -692,6 +692,78 @@ "ERR" ] }, + { + "id": "verif-exec-legacy", + "row": [ + "", + "0x516365675168", + "NONE", + "ERR" + ] + }, + { + "id": "verif-exec-v0", + "row": [ + [ + "516365675168", + 0.0 + ], + "", + "0x0020e06680f1661ad5b946977b6213d642e746144e4443619c0348079c83fd1bccbb", + "P2SH,WITNESS", + "ERR" + ] + }, + { + "id": "verif-exec-tapscript", + "row": [ + [ + "#SCRIPT# 0x516365675168", + "#CONTROLBLOCK#", + 0.0 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT", + "ERR" + ] + }, + { + "id": "vernotif-exec-legacy", + "row": [ + "", + "0x516366675168", + "NONE", + "ERR" + ] + }, + { + "id": "vernotif-exec-v0", + "row": [ + [ + "516366675168", + 0.0 + ], + "", + "0x00201ed8d3cebff162c775e607217c2c3679321e4b2df8bf70aeb71a2e5fd0a7dfe7", + "P2SH,WITNESS", + "ERR" + ] + }, + { + "id": "vernotif-exec-tapscript", + "row": [ + [ + "#SCRIPT# 0x516366675168", + "#CONTROLBLOCK#", + 0.0 + ], + "", + "0x51 0x20 #TAPROOTOUTPUT#", + "P2SH,WITNESS,TAPROOT", + "ERR" + ] + }, { "id": "stack-1000-legacy", "row": [ @@ -9506,6 +9578,213 @@ "ERR" ] }, + { + "id": "sighash-type00-dersig-legacy", + "row": [ + "0x483045022100a4c88cbf157bcb0c2a083675614690ce35d6a9577b7e28bc0f5831e182e1a9f00220109b1f54ade4786d10aa7d787609d9ba92a56f4b55c85c640dbce00cfedf2cd300", + "0x21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + "DERSIG", + "OK" + ] + }, + { + "id": "sighash-type00-strictenc-legacy", + "row": [ + "0x483045022100a4c88cbf157bcb0c2a083675614690ce35d6a9577b7e28bc0f5831e182e1a9f00220109b1f54ade4786d10aa7d787609d9ba92a56f4b55c85c640dbce00cfedf2cd300", + "0x21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + "STRICTENC", + "ERR" + ] + }, + { + "id": "sighash-type00-as-01-legacy", + "row": [ + "0x483045022100a4c88cbf157bcb0c2a083675614690ce35d6a9577b7e28bc0f5831e182e1a9f00220109b1f54ade4786d10aa7d787609d9ba92a56f4b55c85c640dbce00cfedf2cd301", + "0x21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + "DERSIG", + "ERR" + ] + }, + { + "id": "sighash-type00-dersig-v0", + "row": [ + [ + "3045022100970bf0ef00ef0c9c70871a2722fc659c726f8eef60ab2bd91f3d4146f8e0ed4f022017706ddd9d97351e5b9c02b76dd2587764fd692df8a57e2e510bde5856351fd600", + "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + 0.0 + ], + "", + "0x00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245", + "P2SH,WITNESS,DERSIG", + "OK" + ] + }, + { + "id": "sighash-type00-strictenc-v0", + "row": [ + [ + "3045022100970bf0ef00ef0c9c70871a2722fc659c726f8eef60ab2bd91f3d4146f8e0ed4f022017706ddd9d97351e5b9c02b76dd2587764fd692df8a57e2e510bde5856351fd600", + "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + 0.0 + ], + "", + "0x00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245", + "P2SH,WITNESS,STRICTENC", + "ERR" + ] + }, + { + "id": "sighash-type00-as-01-v0", + "row": [ + [ + "3045022100970bf0ef00ef0c9c70871a2722fc659c726f8eef60ab2bd91f3d4146f8e0ed4f022017706ddd9d97351e5b9c02b76dd2587764fd692df8a57e2e510bde5856351fd601", + "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + 0.0 + ], + "", + "0x00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245", + "P2SH,WITNESS,DERSIG", + "ERR" + ] + }, + { + "id": "sighash-type80-dersig-legacy", + "row": [ + "0x483045022100fdcc007649c5545067c1e1caaeb229deb358ccd1a4c912b3c90d63919ffa2f6902205741a9a1b8dd4ebd9d9f656285f773fb0276567c475f62a1c4a9a37f4f37319a80", + "0x21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + "DERSIG", + "OK" + ] + }, + { + "id": "sighash-type80-strictenc-legacy", + "row": [ + "0x483045022100fdcc007649c5545067c1e1caaeb229deb358ccd1a4c912b3c90d63919ffa2f6902205741a9a1b8dd4ebd9d9f656285f773fb0276567c475f62a1c4a9a37f4f37319a80", + "0x21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + "STRICTENC", + "ERR" + ] + }, + { + "id": "sighash-type80-as-81-legacy", + "row": [ + "0x483045022100fdcc007649c5545067c1e1caaeb229deb358ccd1a4c912b3c90d63919ffa2f6902205741a9a1b8dd4ebd9d9f656285f773fb0276567c475f62a1c4a9a37f4f37319a81", + "0x21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + "DERSIG", + "ERR" + ] + }, + { + "id": "sighash-type80-dersig-v0", + "row": [ + [ + "3045022100a29b93bdbe099c4bcd88071d890be29b1c2f1a5c43ee2adeda66e6960e08cc2602200c4ece9d140cf6fe3d61e9ba03c4f7d2a9a31cc6e1982ce07e14ecac8b0cf37980", + "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + 0.0 + ], + "", + "0x00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245", + "P2SH,WITNESS,DERSIG", + "OK" + ] + }, + { + "id": "sighash-type80-strictenc-v0", + "row": [ + [ + "3045022100a29b93bdbe099c4bcd88071d890be29b1c2f1a5c43ee2adeda66e6960e08cc2602200c4ece9d140cf6fe3d61e9ba03c4f7d2a9a31cc6e1982ce07e14ecac8b0cf37980", + "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + 0.0 + ], + "", + "0x00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245", + "P2SH,WITNESS,STRICTENC", + "ERR" + ] + }, + { + "id": "sighash-type80-as-81-v0", + "row": [ + [ + "3045022100a29b93bdbe099c4bcd88071d890be29b1c2f1a5c43ee2adeda66e6960e08cc2602200c4ece9d140cf6fe3d61e9ba03c4f7d2a9a31cc6e1982ce07e14ecac8b0cf37981", + "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + 0.0 + ], + "", + "0x00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245", + "P2SH,WITNESS,DERSIG", + "ERR" + ] + }, + { + "id": "sighash-type85-dersig-legacy", + "row": [ + "0x4730440220480d07c26876b82c87089d7450fb055416c603fb747c41f5c0ea598521cd11fd02201127b240fb977b1c15c5b98e7786aaea96bc4e44e166d4e4cc80eb7496dafd9085", + "0x21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + "DERSIG", + "OK" + ] + }, + { + "id": "sighash-type85-strictenc-legacy", + "row": [ + "0x4730440220480d07c26876b82c87089d7450fb055416c603fb747c41f5c0ea598521cd11fd02201127b240fb977b1c15c5b98e7786aaea96bc4e44e166d4e4cc80eb7496dafd9085", + "0x21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + "STRICTENC", + "ERR" + ] + }, + { + "id": "sighash-type85-as-81-legacy", + "row": [ + "0x4730440220480d07c26876b82c87089d7450fb055416c603fb747c41f5c0ea598521cd11fd02201127b240fb977b1c15c5b98e7786aaea96bc4e44e166d4e4cc80eb7496dafd9081", + "0x21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + "DERSIG", + "ERR" + ] + }, + { + "id": "sighash-type85-dersig-v0", + "row": [ + [ + "304502210090ac2a7e4b7ee1d1c6c89916029d1a98e7f5633e2fe426a0e8a45396482c95e1022044ee303bf6b4e069776d34c3da602364acbae6ce9f38ed3bc4693ef60ffe5e9c85", + "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + 0.0 + ], + "", + "0x00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245", + "P2SH,WITNESS,DERSIG", + "OK" + ] + }, + { + "id": "sighash-type85-strictenc-v0", + "row": [ + [ + "304502210090ac2a7e4b7ee1d1c6c89916029d1a98e7f5633e2fe426a0e8a45396482c95e1022044ee303bf6b4e069776d34c3da602364acbae6ce9f38ed3bc4693ef60ffe5e9c85", + "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + 0.0 + ], + "", + "0x00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245", + "P2SH,WITNESS,STRICTENC", + "ERR" + ] + }, + { + "id": "sighash-type85-as-81-v0", + "row": [ + [ + "304502210090ac2a7e4b7ee1d1c6c89916029d1a98e7f5633e2fe426a0e8a45396482c95e1022044ee303bf6b4e069776d34c3da602364acbae6ce9f38ed3bc4693ef60ffe5e9c81", + "21034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aaac", + 0.0 + ], + "", + "0x00208f30ec1925eaa1916f4d67a9742d938c247d220c796bef6550111eaa90327245", + "P2SH,WITNESS,DERSIG", + "ERR" + ] + }, { "id": "dersig-off-nondet-legacy", "row": [