Skip to content

Commit 44c0790

Browse files
committed
Use 1 signature hash for invalid SIGHASH_SINGLE
When signing a transaction will result in the sighash single bug being exploitable we should return the 1 array (equivalent to 1 as a uint256) as the signature hash. Currently we are using the correct array value but are re-hashing it, instead we should directly return it.
1 parent f211d7b commit 44c0790

File tree

1 file changed

+20
-9
lines changed

1 file changed

+20
-9
lines changed

src/blockdata/transaction.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ use VarInt;
4545
#[cfg(doc)]
4646
use util::sighash::SchnorrSigHashType;
4747

48+
/// Used for signature hash for invalid use of SIGHASH_SINGLE.
49+
const UINT256_ONE: [u8; 32] = [
50+
1, 0, 0, 0, 0, 0, 0, 0,
51+
0, 0, 0, 0, 0, 0, 0, 0,
52+
0, 0, 0, 0, 0, 0, 0, 0,
53+
0, 0, 0, 0, 0, 0, 0, 0
54+
];
55+
4856
/// A reference to a transaction output.
4957
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
5058
pub struct OutPoint {
@@ -340,15 +348,6 @@ impl Transaction {
340348

341349
let (sighash, anyone_can_pay) = EcdsaSigHashType::from_u32_consensus(sighash_type).split_anyonecanpay_flag();
342350

343-
// Special-case sighash_single bug because this is easy enough.
344-
if sighash == EcdsaSigHashType::Single && input_index >= self.output.len() {
345-
writer.write_all(&[1, 0, 0, 0, 0, 0, 0, 0,
346-
0, 0, 0, 0, 0, 0, 0, 0,
347-
0, 0, 0, 0, 0, 0, 0, 0,
348-
0, 0, 0, 0, 0, 0, 0, 0])?;
349-
return Ok(());
350-
}
351-
352351
// Build tx to sign
353352
let mut tx = Transaction {
354353
version: self.version,
@@ -404,12 +403,24 @@ impl Transaction {
404403
script_pubkey: &Script,
405404
sighash_u32: u32
406405
) -> SigHash {
406+
if self.is_invalid_use_of_sighash_single(sighash_u32, input_index) {
407+
return SigHash::from_slice(&UINT256_ONE).expect("const-size array");
408+
}
409+
407410
let mut engine = SigHash::engine();
408411
self.encode_signing_data_to(&mut engine, input_index, script_pubkey, sighash_u32)
409412
.expect("engines don't error");
410413
SigHash::from_engine(engine)
411414
}
412415

416+
/// The SIGHASH_SINGLE bug becomes exploitable when one tries to sign a transaction with
417+
/// SIGHASH_SINGLE and there is not a corresponding output transaction with the same index as
418+
/// the input transaction.
419+
fn is_invalid_use_of_sighash_single(&self, sighash: u32, input_index: usize) -> bool {
420+
let ty = EcdsaSigHashType::from_u32_consensus(sighash);
421+
ty == EcdsaSigHashType::Single && input_index >= self.output.len()
422+
}
423+
413424
/// Gets the "weight" of this transaction, as defined by BIP141. For transactions with an empty
414425
/// witness, this is simply the consensus-serialized size times 4. For transactions with a
415426
/// witness, this is the non-witness consensus-serialized size multiplied by 3 plus the

0 commit comments

Comments
 (0)