bitcoin: reject 65 bytes signature with sighash 0x00
What changed, and why it matters
This commit fixes a bug where the Rust Bitcoin library incorrectly accepted 65-byte Taproot signatures whose final byte was 0x00. Under Bitcoin's BIP-341 rules, such signatures are invalid. Accepting them could let invalid transactions or signatures slip through, and it could break the library's own round-trip serialization (reading a signature in and writing it back out).
Review any code that validates or serializes Taproot signatures with this library to ensure it now rejects 65-byte signatures with sighash 0x00, and verify that no consensus-critical paths relied on the previous lenient behavior.
Security signals we found
BIP-341 non-compliance in Taproot signature parsing
Invalid signature accepted as valid
Potential round-trip serialization inconsistency
Evidence from the diff
In bitcoin/src/crypto/taproot.rs, Signature::from_slice() parses 64-byte signatures as having the default sighash type, and 65-byte signatures as having an explicit sighash type in the 65th byte. Previously, when the 65th byte was 0x00, it was accepted as TapSighashType::Default. BIP-341 explicitly forbids this: a 65-byte signature with sighash byte 0x00 must be rejected. The patch adds a check that returns an error in that case.
Changed components
bitcoin/src/crypto/taproot.rsSignature::from_slice()Taproot key-path signature validationInspect captured patch +5 / −0
diff --git a/bitcoin/src/crypto/taproot.rs b/bitcoin/src/crypto/taproot.rs
index 7a7d5e1a..19623e26 100644
--- a/bitcoin/src/crypto/taproot.rs
+++ b/bitcoin/src/crypto/taproot.rs
@@ -41,6 +41,11 @@ impl Signature {
} else if let Ok(signature) = <[u8; 65]>::try_from(sl) {
let (sighash_type, signature) = signature.split_last();
let sighash_type = TapSighashType::from_consensus_u8(*sighash_type)?;
+ // per BIP-341: if the sig is 65 bytes long, return Fail if sig[64] = 0x00
+ // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#taproot-key-path-spending-signature-validation
+ if sighash_type == TapSighashType::Default {
+ return Err(SigFromSliceError::SighashType(InvalidSighashTypeError(0)));
+ }
let signature = secp256k1::schnorr::Signature::from_byte_array(*signature);
Ok(Self { signature, sighash_type })
} else {
Why this scored 60/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.