1
1
// BIP322 Generic Signature Algorithm
2
- // Written in 2019 by
2
+ // Written in 2021 by
3
3
// Rajarshi Maitra <[email protected] >]
4
4
//
5
5
// To the extent possible under law, the author(s) have dedicated all
19
19
//! `https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki`
20
20
//!
21
21
22
- use crate :: { Descriptor , DescriptorTrait , MiniscriptKey , ToPublicKey } ;
22
+ use super :: { Descriptor , DescriptorTrait , MiniscriptKey , ToPublicKey } ;
23
23
use bitcoin:: blockdata:: { opcodes, script:: Builder } ;
24
24
use bitcoin:: hashes:: {
25
25
borrow_slice_impl, hex_fmt_impl, index_impl, serde_impl, sha256t_hash_newtype, Hash ,
26
26
} ;
27
27
use bitcoin:: secp256k1:: { Secp256k1 , Signature } ;
28
28
use bitcoin:: { OutPoint , PublicKey , SigHashType , Transaction , TxIn , TxOut } ;
29
29
30
- use crate :: interpreter:: { Error as InterpreterError , Interpreter } ;
30
+ use super :: interpreter:: { Error as InterpreterError , Interpreter } ;
31
31
use std:: convert:: From ;
32
32
33
- // BIP322 message tag = sha256("BIP0322-signed-message")
34
- static MIDSTATE : [ u8 ; 32 ] = [
35
- 116 , 101 , 132 , 161 , 135 , 47 , 161 , 0 , 65 , 85 , 78 , 255 , 160 , 56 , 214 , 18 , 73 , 66 , 221 , 121 , 180 ,
36
- 229 , 138 , 76 , 218 , 24 , 78 , 19 , 219 , 230 , 44 , 73 ,
33
+ // BIP322 message tagged hash midstate
34
+ const MIDSTATE : [ u8 ; 32 ] = [
35
+ 137 , 110 , 101 , 166 , 158 , 24 , 33 , 51 , 154 , 160 , 217 , 89 , 167 , 185 , 222 , 252 , 115 , 60 , 186 , 140 ,
36
+ 151 , 47 , 2 , 20 , 94 , 72 , 184 , 111 , 248 , 59 , 249 , 156 ,
37
37
] ;
38
38
39
39
// BIP322 Tagged Hash
@@ -42,7 +42,7 @@ sha256t_hash_newtype!(
42
42
MessageTag ,
43
43
MIDSTATE ,
44
44
64 ,
45
- doc = "test hash" ,
45
+ doc = "BIP322 message tagged hash" ,
46
46
true
47
47
) ;
48
48
@@ -81,44 +81,50 @@ pub enum Bip322Signature {
81
81
/// either for proving fund availability, or committing to a message as the intended
82
82
/// recipient of funds sent to the invoice address.
83
83
#[ derive( Debug , Clone , Eq , PartialEq ) ]
84
- pub struct Bip322 < T : MiniscriptKey + ToPublicKey > {
84
+ pub struct Bip322Validator < Pk : MiniscriptKey + ToPublicKey > {
85
85
/// Message to be signed
86
- message : Vec < u8 > ,
86
+ message : String ,
87
87
88
88
/// Signature to verify the message
89
- /// Optional value is used here because a validator structure can be
90
- /// created without a BIP322Signature. Such structure can only produce
91
- /// to_spend (or empty to_sign) transaction, but cannot validate them.
92
- signature : Option < Bip322Signature > ,
89
+ signature : Bip322Signature ,
93
90
94
91
/// script_pubkey to define the challenge script inside to_spend transaction
95
92
/// here we take in descriptors to derive the resulting script_pubkey
96
- message_challenge : Descriptor < T > ,
93
+ message_challenge : Descriptor < Pk > ,
94
+
95
+ /// Age
96
+ age : u32 ,
97
+
98
+ /// Height
99
+ height : u32 ,
97
100
}
98
101
99
- impl < T : MiniscriptKey + ToPublicKey > Bip322 < T > {
102
+ impl < Pk : MiniscriptKey + ToPublicKey > Bip322Validator < Pk > {
100
103
/// Create a new BIP322 validator
101
- pub fn new ( msg : & [ u8 ] , sig : Option < Bip322Signature > , addr : Descriptor < T > ) -> Self {
102
- Bip322 {
103
- message : msg. to_vec ( ) ,
104
+ pub fn new (
105
+ msg : String ,
106
+ sig : Bip322Signature ,
107
+ addr : Descriptor < Pk > ,
108
+ age : u32 ,
109
+ height : u32 ,
110
+ ) -> Self {
111
+ Bip322Validator {
112
+ message : msg,
104
113
signature : sig,
105
114
message_challenge : addr,
115
+ age : age,
116
+ height : height,
106
117
}
107
118
}
108
119
109
- /// Insert Signature inside BIP322 structure
110
- pub fn insert_sig ( & mut self , sig : Bip322Signature ) {
111
- self . signature = Some ( sig)
112
- }
113
-
114
120
/// create the to_spend transaction
115
121
pub fn to_spend ( & self ) -> Transaction {
116
122
// create default input and output
117
123
let mut vin = TxIn :: default ( ) ;
118
124
let mut vout = TxOut :: default ( ) ;
119
125
120
126
// calculate the message tagged hash
121
- let msg_hash = MessageHash :: hash ( & self . message [ .. ] ) . into_inner ( ) ;
127
+ let msg_hash = MessageHash :: hash ( & self . message . as_bytes ( ) ) . into_inner ( ) ;
122
128
123
129
// mutate the input with appropriate script_sig and sequence
124
130
vin. script_sig = Builder :: new ( )
@@ -143,12 +149,12 @@ impl<T: MiniscriptKey + ToPublicKey> Bip322<T> {
143
149
/// Create to_sign transaction
144
150
/// This will create a transaction structure with empty signature and witness field
145
151
/// its up to the user of the library to fill the Tx with appropriate signature and witness
146
- pub fn to_sign ( & self ) -> Transaction {
152
+ pub fn empty_to_sign ( & self ) -> Transaction {
147
153
// create the appropriate input
148
154
let outpoint = OutPoint :: new ( self . to_spend ( ) . txid ( ) , 0 ) ;
149
155
let mut input = TxIn :: default ( ) ;
150
156
input. previous_output = outpoint;
151
- input. sequence = 0 ;
157
+ input. sequence = self . height ;
152
158
153
159
// create the output
154
160
let output = TxOut {
@@ -160,8 +166,8 @@ impl<T: MiniscriptKey + ToPublicKey> Bip322<T> {
160
166
161
167
// return resulting transaction
162
168
Transaction {
163
- version : 0 ,
164
- lock_time : 0 ,
169
+ version : 2 ,
170
+ lock_time : self . age ,
165
171
input : vec ! [ input] ,
166
172
output : vec ! [ output] ,
167
173
}
@@ -171,46 +177,46 @@ impl<T: MiniscriptKey + ToPublicKey> Bip322<T> {
171
177
/// This will require a BIP322Signature inside the structure
172
178
pub fn validate ( & self ) -> Result < bool , BIP322Error > {
173
179
match & self . signature {
174
- None => Err ( BIP322Error :: InternalError (
175
- "Signature required for validation" . to_string ( ) ,
176
- ) ) ,
177
- Some ( sig ) => {
178
- match sig {
179
- // A Full signature can be validated directly against the `to_sign` transaction
180
- Bip322Signature :: Full ( to_sign ) => self . tx_validation ( to_sign ) ,
181
-
182
- // If Simple Signature is provided, the resulting ` to_sign` Tx will be computed
183
- Bip322Signature :: Simple ( witness ) => {
184
- // create empty to_sign transaction
185
- let mut to_sign = self . to_sign ( ) ;
186
-
187
- to_sign . input [ 0 ] . witness = witness . to_owned ( ) ;
188
-
189
- self . tx_validation ( & to_sign )
190
- }
191
-
192
- // Legacy Signature can only be used to validate against P2PKH message_challenge
193
- Bip322Signature :: Legacy ( sig , pubkey ) => {
194
- if ! self . message_challenge . script_pubkey ( ) . is_p2pkh ( ) {
195
- return Err ( BIP322Error :: InternalError ( "Legacy style signature is only applicable for P2PKH message_challenge" . to_string ( ) ) ) ;
196
- } else {
197
- let mut sig_ser = sig . serialize_der ( ) [ .. ] . to_vec ( ) ;
198
-
199
- // By default SigHashType is ALL
200
- sig_ser . push ( SigHashType :: All as u8 ) ;
201
-
202
- let script_sig = Builder :: new ( )
203
- . push_slice ( & sig_ser [ .. ] )
204
- . push_key ( & pubkey )
205
- . into_script ( ) ;
206
-
207
- let mut to_sign = self . to_sign ( ) ;
208
-
209
- to_sign. input [ 0 ] . script_sig = script_sig ;
210
-
211
- self . tx_validation ( & to_sign )
212
- }
213
- }
180
+ // A Full signature can be validated directly against the `to_sign` transaction
181
+ Bip322Signature :: Full ( to_sign ) => self . tx_validation ( to_sign ) ,
182
+
183
+ // If Simple Signature is provided, the resulting `to_sign` Tx will be computed
184
+ Bip322Signature :: Simple ( witness ) => {
185
+ if ! self . message_challenge . script_pubkey ( ) . is_witness_program ( ) {
186
+ return Err ( BIP322Error :: InternalError ( "Simple style signature is only applicable for Segwit type message_challenge" . to_string ( ) ) ) ;
187
+ } else {
188
+ // create empty to_sign transaction
189
+ let mut to_sign = self . empty_to_sign ( ) ;
190
+
191
+ to_sign. input [ 0 ] . witness = witness . to_owned ( ) ;
192
+
193
+ self . tx_validation ( & to_sign )
194
+ }
195
+ }
196
+
197
+ // Legacy Signature can only be used to validate against P2PKH message_challenge
198
+ Bip322Signature :: Legacy ( sig , pubkey ) => {
199
+ if ! self . message_challenge . script_pubkey ( ) . is_p2pkh ( ) {
200
+ return Err ( BIP322Error :: InternalError (
201
+ "Legacy style signature is only applicable for P2PKH message_challenge"
202
+ . to_string ( ) ,
203
+ ) ) ;
204
+ } else {
205
+ let mut sig_ser = sig . serialize_der ( ) [ .. ] . to_vec ( ) ;
206
+
207
+ // By default SigHashType is ALL
208
+ sig_ser . push ( SigHashType :: All as u8 ) ;
209
+
210
+ let script_sig = Builder :: new ( )
211
+ . push_slice ( & sig_ser [ .. ] )
212
+ . push_key ( & pubkey )
213
+ . into_script ( ) ;
214
+
215
+ let mut to_sign = self . empty_to_sign ( ) ;
216
+
217
+ to_sign . input [ 0 ] . script_sig = script_sig ;
218
+
219
+ self . tx_validation ( & to_sign )
214
220
}
215
221
}
216
222
}
@@ -248,6 +254,8 @@ impl<T: MiniscriptKey + ToPublicKey> Bip322<T> {
248
254
#[ cfg( test) ]
249
255
mod test {
250
256
use super :: * ;
257
+ use bitcoin:: hashes:: sha256t:: Tag ;
258
+ use bitcoin:: hashes:: { sha256, HashEngine } ;
251
259
use bitcoin:: secp256k1:: { Message , Secp256k1 } ;
252
260
use bitcoin:: util:: bip143;
253
261
use bitcoin:: PrivateKey ;
@@ -268,23 +276,69 @@ mod test {
268
276
// Corresponding p2pkh script. used for sighash calculation
269
277
let p2pkh_script = bitcoin:: Script :: new_p2pkh ( & pk. pubkey_hash ( ) ) ;
270
278
271
- // Create BIP322 structures with empty signature
272
- let mut bip322_1 = Bip322 {
273
- message : b"Hello World" . to_vec ( ) ,
274
- message_challenge : desc. clone ( ) ,
275
- signature : None ,
279
+ let message = "Hello World" . to_string ( ) ;
280
+ let age = 0 ;
281
+ let height = 0 ;
282
+
283
+ // Create to_spend transaction
284
+ let to_spend = {
285
+ // create default input and output
286
+ let mut vin = TxIn :: default ( ) ;
287
+ let mut vout = TxOut :: default ( ) ;
288
+
289
+ // calculate the message tagged hash
290
+ let msg_hash = MessageHash :: hash ( & message. as_bytes ( ) ) . into_inner ( ) ;
291
+
292
+ // mutate the input with appropriate script_sig and sequence
293
+ vin. script_sig = Builder :: new ( )
294
+ . push_int ( 0 )
295
+ . push_slice ( & msg_hash[ ..] )
296
+ . into_script ( ) ;
297
+ vin. sequence = 0 ;
298
+
299
+ // mutate the value and script_pubkey as appropriate
300
+ vout. value = 0 ;
301
+ vout. script_pubkey = desc. script_pubkey ( ) ;
302
+
303
+ // create and return final transaction
304
+ Transaction {
305
+ version : 0 ,
306
+ lock_time : 0 ,
307
+ input : vec ! [ vin] ,
308
+ output : vec ! [ vout] ,
309
+ }
310
+ } ;
311
+
312
+ // create an empty to_sign transaction
313
+ let mut empty_to_sign = {
314
+ // create the appropriate input
315
+ let outpoint = OutPoint :: new ( to_spend. txid ( ) , 0 ) ;
316
+ let mut input = TxIn :: default ( ) ;
317
+ input. previous_output = outpoint;
318
+ input. sequence = height;
319
+
320
+ // create the output
321
+ let output = TxOut {
322
+ value : 0 ,
323
+ script_pubkey : Builder :: new ( )
324
+ . push_opcode ( opcodes:: all:: OP_RETURN )
325
+ . into_script ( ) ,
326
+ } ;
327
+
328
+ // return resulting transaction
329
+ Transaction {
330
+ version : 2 ,
331
+ lock_time : age,
332
+ input : vec ! [ input] ,
333
+ output : vec ! [ output] ,
334
+ }
276
335
} ;
277
- let mut bip322_2 = bip322_1. clone ( ) ;
278
- let mut bip322_3 = bip322_1. clone ( ) ;
279
336
280
337
// --------------------------------------------------------------
281
338
// Check BIP322Signature::FUll
282
339
283
- // Generate to_sign transaction
284
- let mut to_sign = bip322_1. to_sign ( ) ;
285
-
286
340
// Generate witness for above wpkh pubkey
287
- let mut sighash_cache = bip143:: SigHashCache :: new ( & to_sign ) ;
341
+ let mut sighash_cache = bip143:: SigHashCache :: new ( & empty_to_sign ) ;
288
342
let message = sighash_cache. signature_hash ( 0 , & p2pkh_script, 0 , SigHashType :: All . into ( ) ) ;
289
343
let message = Message :: from_slice ( & message[ ..] ) . unwrap ( ) ;
290
344
@@ -294,11 +348,20 @@ mod test {
294
348
sig_with_hash. push ( SigHashType :: All as u8 ) ;
295
349
296
350
let witness: Vec < Vec < u8 > > = vec ! [ sig_with_hash, pk. to_bytes( ) ] ;
297
- to_sign . input [ 0 ] . witness = witness. clone ( ) ;
351
+ empty_to_sign . input [ 0 ] . witness = witness. clone ( ) ;
298
352
299
- // Insert signature inside BIP322 structure
300
- let bip322_signature = Bip322Signature :: Full ( to_sign) ;
301
- bip322_1. insert_sig ( bip322_signature) ;
353
+ let bip322_signature = Bip322Signature :: Full ( empty_to_sign) ;
354
+
355
+ // Create BIP322 Validator
356
+ let bip322_1 = Bip322Validator {
357
+ message : "Hello World" . to_string ( ) ,
358
+ message_challenge : desc. clone ( ) ,
359
+ signature : bip322_signature,
360
+ age : 0 ,
361
+ height : 0 ,
362
+ } ;
363
+ let mut bip322_2 = bip322_1. clone ( ) ;
364
+ let mut bip322_3 = bip322_1. clone ( ) ;
302
365
303
366
// Check validation
304
367
assert_eq ! ( bip322_1. validate( ) . unwrap( ) , true ) ;
@@ -307,7 +370,7 @@ mod test {
307
370
// Check Bip322Signature::Simple
308
371
309
372
// Same structure can be validated with Simple type signature
310
- bip322_2. insert_sig ( Bip322Signature :: Simple ( witness) ) ;
373
+ bip322_2. signature = Bip322Signature :: Simple ( witness) ;
311
374
312
375
assert_eq ! ( bip322_2. validate( ) . unwrap( ) , true ) ;
313
376
@@ -320,7 +383,7 @@ mod test {
320
383
bip322_3. message_challenge = desc. clone ( ) ;
321
384
322
385
// Create empty to_sign
323
- let to_sign = bip322_3. to_sign ( ) ;
386
+ let to_sign = bip322_3. empty_to_sign ( ) ;
324
387
325
388
// Compute SigHash and Signature
326
389
let message = to_sign. signature_hash ( 0 , & desc. script_pubkey ( ) , SigHashType :: All as u32 ) ;
@@ -329,9 +392,20 @@ mod test {
329
392
330
393
// Create Bip322Signature::Legacy
331
394
let bip322_sig = Bip322Signature :: Legacy ( signature, pk) ;
332
- bip322_3. insert_sig ( bip322_sig) ;
395
+ bip322_3. signature = bip322_sig;
333
396
334
397
// Check validation
335
398
assert_eq ! ( bip322_3. validate( ) . unwrap( ) , true ) ;
336
399
}
400
+
401
+ #[ test]
402
+ fn test_tagged_hash ( ) {
403
+ let mut engine = sha256:: Hash :: engine ( ) ;
404
+ let tag_hash = sha256:: Hash :: hash ( "BIP0322-signed-message" . as_bytes ( ) ) ;
405
+ engine. input ( & tag_hash[ ..] ) ;
406
+ engine. input ( & tag_hash[ ..] ) ;
407
+
408
+ assert_eq ! ( engine. midstate( ) . into_inner( ) , MIDSTATE ) ;
409
+ assert_eq ! ( engine. midstate( ) , MessageTag :: engine( ) . midstate( ) ) ;
410
+ }
337
411
}
0 commit comments