@@ -45,6 +45,14 @@ use VarInt;
45
45
#[ cfg( doc) ]
46
46
use util:: sighash:: SchnorrSigHashType ;
47
47
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
+
48
56
/// A reference to a transaction output.
49
57
#[ derive( Copy , Clone , Debug , Eq , Hash , PartialEq , PartialOrd , Ord ) ]
50
58
pub struct OutPoint {
@@ -332,20 +340,11 @@ impl Transaction {
332
340
script_pubkey : & Script ,
333
341
sighash_type : U ,
334
342
) -> Result < ( ) , encode:: Error > {
335
- let sighash_type : u32 = sighash_type. into ( ) ;
343
+ let sighash_type: u32 = sighash_type. into ( ) ;
336
344
assert ! ( input_index < self . input. len( ) ) ; // Panic on OOB
337
345
338
346
let ( sighash, anyone_can_pay) = EcdsaSigHashType :: from_u32_consensus ( sighash_type) . split_anyonecanpay_flag ( ) ;
339
347
340
- // Special-case sighash_single bug because this is easy enough.
341
- if sighash == EcdsaSigHashType :: Single && input_index >= self . output . len ( ) {
342
- writer. write_all ( & [ 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
343
- 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
344
- 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
345
- 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ) ?;
346
- return Ok ( ( ) ) ;
347
- }
348
-
349
348
// Build tx to sign
350
349
let mut tx = Transaction {
351
350
version : self . version ,
@@ -401,12 +400,24 @@ impl Transaction {
401
400
script_pubkey : & Script ,
402
401
sighash_u32 : u32
403
402
) -> SigHash {
403
+ if self . is_invalid_use_of_sighash_single ( sighash_u32, input_index) {
404
+ return SigHash :: from_slice ( & UINT256_ONE ) . expect ( "const-size array" ) ;
405
+ }
406
+
404
407
let mut engine = SigHash :: engine ( ) ;
405
408
self . encode_signing_data_to ( & mut engine, input_index, script_pubkey, sighash_u32)
406
409
. expect ( "engines don't error" ) ;
407
410
SigHash :: from_engine ( engine)
408
411
}
409
412
413
+ /// The SIGHASH_SINGLE bug becomes exploitable when one tries to sign a transaction with
414
+ /// SIGHASH_SINGLE and there is not a corresponding output transaction with the same index as
415
+ /// the input transaction.
416
+ fn is_invalid_use_of_sighash_single ( & self , sighash : u32 , input_index : usize ) -> bool {
417
+ let ty = EcdsaSigHashType :: from_u32_consensus ( sighash) ;
418
+ ty == EcdsaSigHashType :: Single && input_index >= self . output . len ( )
419
+ }
420
+
410
421
/// Gets the "weight" of this transaction, as defined by BIP141. For transactions with an empty
411
422
/// witness, this is simply the consensus-serialized size times 4. For transactions with a
412
423
/// witness, this is the non-witness consensus-serialized size multiplied by 3 plus the
@@ -815,7 +826,7 @@ impl ::std::error::Error for SigHashTypeParseError {}
815
826
816
827
#[ cfg( test) ]
817
828
mod tests {
818
- use super :: { OutPoint , ParseOutPointError , Transaction , TxIn , NonStandardSigHashType } ;
829
+ use super :: * ;
819
830
820
831
use core:: str:: FromStr ;
821
832
use blockdata:: constants:: WITNESS_SCALE_FACTOR ;
@@ -1073,25 +1084,6 @@ mod tests {
1073
1084
serde_round_trip ! ( tx) ;
1074
1085
}
1075
1086
1076
- fn run_test_sighash ( tx : & str , script : & str , input_index : usize , hash_type : i32 , expected_result : & str ) {
1077
- let tx: Transaction = deserialize ( & Vec :: from_hex ( tx) . unwrap ( ) [ ..] ) . unwrap ( ) ;
1078
- let script = Script :: from ( Vec :: from_hex ( script) . unwrap ( ) ) ;
1079
- let mut raw_expected = Vec :: from_hex ( expected_result) . unwrap ( ) ;
1080
- raw_expected. reverse ( ) ;
1081
- let expected_result = SigHash :: from_slice ( & raw_expected[ ..] ) . unwrap ( ) ;
1082
-
1083
- let actual_result = if raw_expected[ 0 ] % 2 == 0 {
1084
- // tx.signature_hash and cache.legacy_signature_hash are the same, this if helps to test
1085
- // both the codepaths without repeating the test code
1086
- tx. signature_hash ( input_index, & script, hash_type as u32 )
1087
- } else {
1088
- let cache = SigHashCache :: new ( & tx) ;
1089
- cache. legacy_signature_hash ( input_index, & script, hash_type as u32 ) . unwrap ( )
1090
- } ;
1091
-
1092
- assert_eq ! ( actual_result, expected_result) ;
1093
- }
1094
-
1095
1087
// Test decoding transaction `4be105f158ea44aec57bf12c5817d073a712ab131df6f37786872cfc70734188`
1096
1088
// from testnet, which is the first BIP144-encoded transaction I encountered.
1097
1089
#[ test]
@@ -1148,6 +1140,48 @@ mod tests {
1148
1140
assert_eq ! ( EcdsaSigHashType :: from_u32_standard( nonstandard_hashtype) , Err ( NonStandardSigHashType ( 0x04 ) ) ) ;
1149
1141
}
1150
1142
1143
+ #[ test]
1144
+ fn sighash_single_bug ( ) {
1145
+ const SIGHASH_SINGLE : u32 = 3 ;
1146
+ // We need a tx with more inputs than outputs.
1147
+ let mut input = Vec :: new ( ) ;
1148
+ input. push ( TxIn :: default ( ) ) ;
1149
+ input. push ( TxIn :: default ( ) ) ;
1150
+ let mut output = Vec :: new ( ) ;
1151
+ output. push ( TxOut :: default ( ) ) ;
1152
+
1153
+ let tx = Transaction {
1154
+ version : 1 ,
1155
+ lock_time : 0 ,
1156
+ input : input,
1157
+ output : output, // TODO: Use Vec::from([TxOut]) once we bump MSRV.
1158
+ } ;
1159
+ let script = Script :: new ( ) ;
1160
+ let got = tx. signature_hash ( 1 , & script, SIGHASH_SINGLE ) ;
1161
+ let want = SigHash :: from_slice ( & UINT256_ONE ) . unwrap ( ) ;
1162
+
1163
+ assert_eq ! ( got, want)
1164
+ }
1165
+
1166
+ fn run_test_sighash ( tx : & str , script : & str , input_index : usize , hash_type : i32 , expected_result : & str ) {
1167
+ let tx: Transaction = deserialize ( & Vec :: from_hex ( tx) . unwrap ( ) [ ..] ) . unwrap ( ) ;
1168
+ let script = Script :: from ( Vec :: from_hex ( script) . unwrap ( ) ) ;
1169
+ let mut raw_expected = Vec :: from_hex ( expected_result) . unwrap ( ) ;
1170
+ raw_expected. reverse ( ) ;
1171
+ let expected_result = SigHash :: from_slice ( & raw_expected[ ..] ) . unwrap ( ) ;
1172
+
1173
+ let actual_result = if raw_expected[ 0 ] % 2 == 0 {
1174
+ // tx.signature_hash and cache.legacy_signature_hash are the same, this if helps to test
1175
+ // both the codepaths without repeating the test code
1176
+ tx. signature_hash ( input_index, & script, hash_type as u32 )
1177
+ } else {
1178
+ let cache = SigHashCache :: new ( & tx) ;
1179
+ cache. legacy_signature_hash ( input_index, & script, hash_type as u32 ) . unwrap ( )
1180
+ } ;
1181
+
1182
+ assert_eq ! ( actual_result, expected_result) ;
1183
+ }
1184
+
1151
1185
// These test vectors were stolen from libbtc, which is Copyright 2014 Jonas Schnelli MIT
1152
1186
// They were transformed by replacing {...} with run_test_sighash(...), then the ones containing
1153
1187
// OP_CODESEPARATOR in their pubkeys were removed
0 commit comments