@@ -30,6 +30,7 @@ pub struct Schnorr<CH, NG = (), GT = BasePoint> {
30
30
///
31
31
/// [`NonceGen`]: crate::nonce::NonceGen
32
32
nonce_challenge_bundle : NonceChallengeBundle < CH , NG > ,
33
+ application_tag : Option < [ u8 ; 64 ] > ,
33
34
}
34
35
35
36
/// Describes the kind of messages that will be signed with a [`Schnorr`] instance.
@@ -48,7 +49,12 @@ pub enum MessageKind {
48
49
Plain {
49
50
/// You must provide a tag to separate signatures from your application
50
51
/// from other applications. If two [`Schnorr`] instances are created
51
- /// with a different `tag` then a signature valid for one will never be valid for the other.
52
+ /// with a different `tag` then a signature valid for one will never be
53
+ /// valid for the other. It will also never be valid for `Prehashed`
54
+ /// instances. The specific method of domain separation used is
55
+ /// described [here].
56
+ ///
57
+ /// [here]: https://github.com/sipa/bips/issues/207#issuecomment-673681901
52
58
tag : & ' static str ,
53
59
} ,
54
60
}
@@ -105,15 +111,26 @@ where
105
111
challenge_hash : CH :: default ( ) ,
106
112
nonce_gen,
107
113
}
108
- . add_protocol_tag ( "BIP340 " ) ;
114
+ . add_protocol_tag ( "BIP0340 " ) ;
109
115
110
- if let MessageKind :: Plain { tag } = msgkind {
111
- nonce_challenge_bundle = nonce_challenge_bundle. add_application_tag ( tag) ;
112
- }
116
+ let application_tag = match msgkind {
117
+ MessageKind :: Prehashed => None ,
118
+ MessageKind :: Plain { tag } => {
119
+ assert ! ( tag. len( ) <= 64 ) ;
120
+ // Only add the application tag to nonce gen since we will be
121
+ // separately adding the tag to the challenge hash in self.challenge()
122
+ nonce_challenge_bundle. nonce_gen =
123
+ nonce_challenge_bundle. nonce_gen . add_application_tag ( tag) ;
124
+ let mut application_tag = [ 0u8 ; 64 ] ;
125
+ application_tag[ ..tag. len ( ) ] . copy_from_slice ( tag. as_bytes ( ) ) ;
126
+ Some ( application_tag)
127
+ }
128
+ } ;
113
129
114
130
Self {
115
131
G : fun:: G . clone ( ) ,
116
132
nonce_challenge_bundle,
133
+ application_tag,
117
134
}
118
135
}
119
136
}
@@ -149,7 +166,7 @@ where
149
166
public => [ X , message]
150
167
) ;
151
168
152
- let R = XOnly :: < SquareY > :: from_scalar_mul ( & self . G , & mut r) ;
169
+ let R = XOnly :: from_scalar_mul ( & self . G , & mut r) ;
153
170
let c = self . challenge ( & R , X , message) ;
154
171
let s = s ! ( r + c * x) . mark :: < Public > ( ) ;
155
172
@@ -205,20 +222,22 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
205
222
/// let message = b"we rolled our own sign!".as_ref().mark::<Public>();
206
223
/// let keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
207
224
/// let mut r = Scalar::random(&mut rand::thread_rng());
208
- /// let R = XOnly::<SquareY>:: from_scalar_mul(schnorr.G(), &mut r);
225
+ /// let R = XOnly::from_scalar_mul(schnorr.G(), &mut r);
209
226
/// let challenge = schnorr.challenge(&R, keypair.public_key(), message);
210
227
/// let s = s!(r + challenge * { keypair.secret_key() });
211
228
/// let signature = Signature { R, s };
212
229
/// assert!(schnorr.verify(&keypair.verification_key(), message, &signature));
213
230
/// ```
214
- pub fn challenge < S : Secrecy > (
215
- & self ,
216
- R : & XOnly < SquareY > ,
217
- X : & XOnly < EvenY > ,
218
- m : Slice < ' _ , S > ,
219
- ) -> Scalar < S , Zero > {
231
+ pub fn challenge < S : Secrecy > ( & self , R : & XOnly , X : & XOnly , m : Slice < ' _ , S > ) -> Scalar < S , Zero > {
220
232
let hash = self . nonce_challenge_bundle . challenge_hash . clone ( ) ;
221
- let challenge = Scalar :: from_hash ( hash. add ( R ) . add ( X ) . add ( & m) ) ;
233
+ let mut hash = hash. add ( R ) . add ( X ) ;
234
+
235
+ if let Some ( tag) = self . application_tag {
236
+ hash. update ( & tag[ ..] ) ;
237
+ }
238
+
239
+ let challenge = Scalar :: from_hash ( hash. add ( & m) ) ;
240
+
222
241
challenge
223
242
// Since the challenge pre-image is adversarially controlled we
224
243
// conservatively allow for it to be zero
@@ -228,8 +247,8 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
228
247
}
229
248
230
249
/// Verifies a signature on a message under a given public key. Note that a full
231
- /// `Point<EvenY,..>` is passed in rather than a `XOnly<EvenY,..> ` because it's more efficient
232
- /// for repeated verification (where as `XOnly<EvenY,..> ` is more efficient for repeated
250
+ /// `Point<EvenY,..>` is passed in rather than a `XOnly` because it's more efficient
251
+ /// for repeated verification (where as `XOnly` is more efficient for repeated
233
252
/// signing).
234
253
///
235
254
/// For an example see the [Synopsis](crate#synopsis)
@@ -243,7 +262,8 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
243
262
let X = public_key;
244
263
let ( R , s) = signature. as_tuple ( ) ;
245
264
let c = self . challenge ( R , & X . to_xonly ( ) , message) ;
246
- g ! ( s * self . G - c * X ) == * R
265
+ let R_tmp = g ! ( s * self . G - c * X ) . mark :: < Normal > ( ) ;
266
+ R_tmp == * R
247
267
}
248
268
249
269
/// _Anticipates_ a Schnorr signature given the nonce `R` that will be used ahead of time.
@@ -252,7 +272,7 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
252
272
pub fn anticipate_signature (
253
273
& self ,
254
274
X : & Point < EvenY , impl Secrecy > ,
255
- R : & Point < SquareY , impl Secrecy > ,
275
+ R : & Point < EvenY , impl Secrecy > ,
256
276
m : Slice < ' _ , impl Secrecy > ,
257
277
) -> Point < Jacobian , Public , Zero > {
258
278
let c = self . challenge ( & R . to_xonly ( ) , & X . to_xonly ( ) , m) ;
@@ -262,6 +282,8 @@ impl<NG, CH: Digest<OutputSize = U32> + Clone, GT> Schnorr<CH, NG, GT> {
262
282
263
283
#[ cfg( test) ]
264
284
pub mod test {
285
+ use fun:: nonce:: Deterministic ;
286
+
265
287
use super :: * ;
266
288
use crate :: fun:: TEST_SOUNDNESS ;
267
289
crate :: fun:: test_plus_wasm! {
@@ -308,5 +330,22 @@ pub mod test {
308
330
assert_ne!( signature_1. R , signature_4. R ) ;
309
331
}
310
332
}
333
+
334
+ fn deterministic_nonces_for_different_message_kinds( ) {
335
+ use sha2:: Sha256 ;
336
+ let schnorr_1 = Schnorr :: <Sha256 , _>:: new( Deterministic :: <Sha256 >:: default ( ) , MessageKind :: Prehashed ) ;
337
+ let schnorr_2 = Schnorr :: <Sha256 , _>:: new( Deterministic :: <Sha256 >:: default ( ) , MessageKind :: Plain { tag: "two" } ) ;
338
+ let schnorr_3 = Schnorr :: <Sha256 , _>:: new( Deterministic :: <Sha256 >:: default ( ) , MessageKind :: Plain { tag: "three" } ) ;
339
+ let x = Scalar :: from_str( "18451f9e08af9530814243e202a4a977130e672079f5c14dcf15bd4dee723072" ) . unwrap( ) ;
340
+ let keypair = schnorr_1. new_keypair( x) ;
341
+ let message = b"foo" . as_ref( ) . mark:: <Public >( ) ;
342
+ assert_ne!( schnorr_1. sign( & keypair, message) . R , schnorr_2. sign( & keypair, message) . R ) ;
343
+ assert_ne!( schnorr_1. sign( & keypair, message) . R , schnorr_3. sign( & keypair, message) . R ) ;
344
+
345
+ // make sure deterministic signatures don't change
346
+ use core:: str :: FromStr ;
347
+ assert_eq!( schnorr_1. sign( & keypair, message) , Signature :: <Public >:: from_str( "fe9e5d0319d5d221988d6fd7fe1c4bedd2fb4465f592f1002f461503332a266977bb4a0b00c00d07072c796212cbea0957ebaaa5139143761c45d997ebe36cbe" ) . unwrap( ) ) ;
348
+ assert_eq!( schnorr_2. sign( & keypair, message) , Signature :: <Public >:: from_str( "6cad3863f4d01494ce4c40f3422e4916c616356d730bc4ffe33e386f038b328ba1dc9621e626992c2612f33cdb35f4be4badc464c1f4bf3de15517e7aedcf615" ) . unwrap( ) ) ;
349
+ }
311
350
}
312
351
}
0 commit comments