1 // -*- mode: rust; -*-
2 //
3 // This file is part of schnorrkel.
4 // Copyright (c) 2019 Web 3 Foundation
5 // See LICENSE for licensing information.
6 //
7 // Authors:
8 // - Jeffrey Burdges <jeff@web3.foundation>
9 
10 //! ### Schnorr signature contexts and configuration, adaptable to most Schnorr signature schemes.
11 
12 use core::cell::RefCell;
13 
14 use rand_core::{RngCore,CryptoRng};
15 
16 use merlin::Transcript;
17 
18 use curve25519_dalek::digest::{Update,FixedOutput,ExtendableOutput,XofReader};
19 use curve25519_dalek::digest::generic_array::typenum::{U32,U64};
20 
21 use curve25519_dalek::ristretto::CompressedRistretto; // RistrettoPoint
22 use curve25519_dalek::scalar::Scalar;
23 
24 
25 // === Signing context as transcript === //
26 
27 /// Schnorr signing transcript
28 ///
29 /// We envision signatures being on messages, but if a signature occurs
30 /// inside a larger protocol then the signature scheme's internal
31 /// transcript may exist before or persist after signing.
32 ///
33 /// In this trait, we provide an interface for Schnorr signature-like
34 /// constructions that is compatable with `merlin::Transcript`, but
35 /// abstract enough to support conventional hash functions as well.
36 ///
37 /// We warn however that conventional hash functions do not provide
38 /// strong enough domain seperation for usage via `&mut` references.
39 ///
40 /// We fold randomness into witness generation here too, which
41 /// gives every function that takes a `SigningTranscript` a default
42 /// argument `rng: impl Rng = thread_rng()` too.
43 ///
44 /// We also abstract over owned and borrowed `merlin::Transcript`s,
45 /// so that simple use cases do not suffer from our support for.
46 pub trait SigningTranscript {
47     /// Extend transcript with some bytes, shadowed by `merlin::Transcript`.
commit_bytes(&mut self, label: &'static [u8], bytes: &[u8])48     fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]);
49 
50     /// Extend transcript with a protocol name
proto_name(&mut self, label: &'static [u8])51     fn proto_name(&mut self, label: &'static [u8]) {
52         self.commit_bytes(b"proto-name", label);
53     }
54 
55     /// Extend the transcript with a compressed Ristretto point
commit_point(&mut self, label: &'static [u8], compressed: &CompressedRistretto)56     fn commit_point(&mut self, label: &'static [u8], compressed: &CompressedRistretto) {
57         self.commit_bytes(label, compressed.as_bytes());
58     }
59 
60     /*
61     fn commit_sorted_points<P,S>(&mut self, label: &'static [u8], set: &mut [P])
62     where P: Borrow<CompressedRistretto>,
63           // S: BorrowMut<[P]>,
64     {
65         // let set = set.borrow_mut();
66         set.sort_unstable_by(
67             |a,b| a.borrow().as_bytes()
68              .cmp(b.borrow().as_bytes())
69         );
70         for p in set.iter() {
71             self.commit_point(label,p.borrow());
72         }
73     }
74     */
75 
76     /// Produce some challenge bytes, shadowed by `merlin::Transcript`.
challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8])77     fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]);
78 
79     /// Produce the public challenge scalar `e`.
challenge_scalar(&mut self, label: &'static [u8]) -> Scalar80     fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar {
81         let mut buf = [0; 64];
82         self.challenge_bytes(label, &mut buf);
83         Scalar::from_bytes_mod_order_wide(&buf)
84     }
85 
86     /// Produce a secret witness scalar `k`, aka nonce, from the protocol
87     /// transcript and any "nonce seeds" kept with the secret keys.
witness_scalar(&self, label: &'static [u8], nonce_seeds: &[&[u8]]) -> Scalar88     fn witness_scalar(&self, label: &'static [u8], nonce_seeds: &[&[u8]]) -> Scalar {
89         let mut scalar_bytes = [0u8; 64];
90         self.witness_bytes(label, &mut scalar_bytes, nonce_seeds);
91         Scalar::from_bytes_mod_order_wide(&scalar_bytes)
92     }
93 
94     /// Produce secret witness bytes from the protocol transcript
95     /// and any "nonce seeds" kept with the secret keys.
witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]])96     fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]]) {
97         self.witness_bytes_rng(label, dest, nonce_seeds, super::rand_hack())
98     }
99 
100     /// Produce secret witness bytes from the protocol transcript
101     /// and any "nonce seeds" kept with the secret keys.
witness_bytes_rng<R>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: R) where R: RngCore+CryptoRng102     fn witness_bytes_rng<R>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: R)
103     where R: RngCore+CryptoRng;
104 }
105 
106 
107 /// We delegates any mutable reference to its base type, like `&mut Rng`
108 /// or similar to `BorrowMut<..>` do, but doing so here simplifies
109 /// alternative implementations.
110 impl<T> SigningTranscript for &mut T
111 where T: SigningTranscript + ?Sized,
112 {
113     #[inline(always)]
commit_bytes(&mut self, label: &'static [u8], bytes: &[u8])114     fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8])
115         {  (**self).commit_bytes(label,bytes)  }
116     #[inline(always)]
proto_name(&mut self, label: &'static [u8])117     fn proto_name(&mut self, label: &'static [u8])
118         {  (**self).proto_name(label)  }
119     #[inline(always)]
commit_point(&mut self, label: &'static [u8], compressed: &CompressedRistretto)120     fn commit_point(&mut self, label: &'static [u8], compressed: &CompressedRistretto)
121         {  (**self).commit_point(label, compressed)  }
122     #[inline(always)]
challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8])123     fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8])
124         {  (**self).challenge_bytes(label,dest)  }
125     #[inline(always)]
challenge_scalar(&mut self, label: &'static [u8]) -> Scalar126     fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar
127         {  (**self).challenge_scalar(label)  }
128     #[inline(always)]
witness_scalar(&self, label: &'static [u8], nonce_seeds: &[&[u8]]) -> Scalar129     fn witness_scalar(&self, label: &'static [u8], nonce_seeds: &[&[u8]]) -> Scalar
130         {  (**self).witness_scalar(label,nonce_seeds)  }
131     #[inline(always)]
witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]])132     fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]])
133         {  (**self).witness_bytes(label,dest,nonce_seeds)  }
134     #[inline(always)]
witness_bytes_rng<R>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: R) where R: RngCore+CryptoRng135     fn witness_bytes_rng<R>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: R)
136     where R: RngCore+CryptoRng
137         {  (**self).witness_bytes_rng(label,dest,nonce_seeds,rng)  }
138 }
139 
140 /// We delegate `SigningTranscript` methods to the corresponding
141 /// inherent methods of `merlin::Transcript` and implement two
142 /// witness methods to avoid overwriting the `merlin::TranscriptRng`
143 /// machinery.
144 impl SigningTranscript for Transcript {
commit_bytes(&mut self, label: &'static [u8], bytes: &[u8])145     fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) {
146         Transcript::append_message(self, label, bytes)
147     }
148 
challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8])149     fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) {
150         Transcript::challenge_bytes(self, label, dest)
151     }
152 
witness_bytes_rng<R>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], mut rng: R) where R: RngCore+CryptoRng153     fn witness_bytes_rng<R>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], mut rng: R)
154     where R: RngCore+CryptoRng
155     {
156         let mut br = self.build_rng();
157         for ns in nonce_seeds {
158             br = br.rekey_with_witness_bytes(label, ns);
159         }
160         let mut r = br.finalize(&mut rng);
161         r.fill_bytes(dest)
162     }
163 }
164 
165 
166 /// Schnorr signing context
167 ///
168 /// We expect users to have seperate `SigningContext`s for each role
169 /// that signature play in their protocol.  These `SigningContext`s
170 /// may be global `lazy_static!`s, or perhaps constants in future.
171 ///
172 /// To sign a message, apply the appropriate inherent method to create
173 /// a signature transcript.
174 ///
175 /// You should use `merlin::Transcript`s directly if you must do
176 /// anything more complex, like use signatures in larger zero-knoweldge
177 /// protocols or sign several components but only reveal one later.
178 ///
179 /// We declare these methods `#[inline(always)]` because rustc does
180 /// not handle large returns as efficently as one might like.
181 /// https://github.com/rust-random/rand/issues/817
182 #[derive(Clone)] // Debug
183 pub struct SigningContext(Transcript);
184 
185 /// Initialize a signing context from a static byte string that
186 /// identifies the signature's role in the larger protocol.
187 #[inline(always)]
signing_context(context : &[u8]) -> SigningContext188 pub fn signing_context(context : &[u8]) -> SigningContext {
189     SigningContext::new(context)
190 }
191 
192 impl SigningContext {
193     /// Initialize a signing context from a static byte string that
194     /// identifies the signature's role in the larger protocol.
195     #[inline(always)]
new(context : &[u8]) -> SigningContext196     pub fn new(context : &[u8]) -> SigningContext {
197         let mut t = Transcript::new(b"SigningContext");
198         t.append_message(b"",context);
199         SigningContext(t)
200     }
201 
202     /// Initalize an owned signing transcript on a message provided as a byte array.
203     ///
204     /// Avoid this method when processing large slices because it
205     /// calls `merlin::Transcript::append_message` directly and
206     /// `merlin` is designed for domain seperation, not performance.
207     #[inline(always)]
bytes(&self, bytes: &[u8]) -> Transcript208     pub fn bytes(&self, bytes: &[u8]) -> Transcript {
209         let mut t = self.0.clone();
210         t.append_message(b"sign-bytes", bytes);
211         t
212     }
213 
214     /// Initalize an owned signing transcript on a message provided
215     /// as a hash function with extensible output mode (XOF) by
216     /// finalizing the hash and extracting 32 bytes from XOF.
217     #[inline(always)]
xof<D: ExtendableOutput>(&self, h: D) -> Transcript218     pub fn xof<D: ExtendableOutput>(&self, h: D) -> Transcript {
219         let mut prehash = [0u8; 32];
220         h.finalize_xof().read(&mut prehash);
221         let mut t = self.0.clone();
222         t.append_message(b"sign-XoF", &prehash);
223         t
224     }
225 
226     /// Initalize an owned signing transcript on a message provided as
227     /// a hash function with 256 bit output.
228     #[inline(always)]
hash256<D: FixedOutput<OutputSize=U32>>(&self, h: D) -> Transcript229     pub fn hash256<D: FixedOutput<OutputSize=U32>>(&self, h: D) -> Transcript {
230         let mut prehash = [0u8; 32];
231         prehash.copy_from_slice(h.finalize_fixed().as_slice());
232         let mut t = self.0.clone();
233         t.append_message(b"sign-256", &prehash);
234         t
235     }
236 
237     /// Initalize an owned signing transcript on a message provided as
238     /// a hash function with 512 bit output, usually a gross over kill.
239     #[inline(always)]
hash512<D: FixedOutput<OutputSize=U64>>(&self, h: D) -> Transcript240     pub fn hash512<D: FixedOutput<OutputSize=U64>>(&self, h: D) -> Transcript {
241         let mut prehash = [0u8; 64];
242         prehash.copy_from_slice(h.finalize_fixed().as_slice());
243         let mut t = self.0.clone();
244         t.append_message(b"sign-256", &prehash);
245         t
246     }
247 }
248 
249 
250 /// Very simple transcript construction from a modern hash fucntion.
251 ///
252 /// We provide this transcript type to directly use conventional hash
253 /// functions with an extensible output mode, like Shake128 and
254 /// Blake2x.
255 ///
256 /// We recommend using `merlin::Transcript` instead because merlin
257 /// provides the transcript abstraction natively and might function
258 /// better in low memory enviroments.  We therefore do not provide
259 /// conveniences like `signing_context` for this.
260 ///
261 /// We note that merlin already uses Keccak, upon which Shake128 is based,
262 /// and that no rust implementation for Blake2x currently exists.
263 ///
264 /// We caution that our transcript abstractions cannot provide the
265 /// protections against hash collisions that Ed25519 provides via
266 /// double hashing, but that prehashed Ed25519 variants lose.
267 /// As such, any hash function used here must be collision resistant.
268 /// We strongly recommend against building XOFs from weaker hash
269 /// functions like SHA1 with HKDF constructions or similar.
270 ///
271 /// In `XoFTranscript` style, we never expose the hash function `H`
272 /// underlying this type, so that developers cannot circumvent the
273 /// domain separation provided by our methods.  We do this to make
274 /// `&mut XoFTranscript : SigningTranscript` safe.
275 pub struct XoFTranscript<H>(H)
276 where H: Update + ExtendableOutput + Clone;
277 
input_bytes<H: Update>(h: &mut H, bytes: &[u8])278 fn input_bytes<H: Update>(h: &mut H, bytes: &[u8]) {
279     let l = bytes.len() as u64;
280     h.update(l.to_le_bytes());
281     h.update(bytes);
282 }
283 
284 impl<H> XoFTranscript<H>
285 where H: Update + ExtendableOutput + Clone
286 {
287     /// Create a `XoFTranscript` from a conventional hash functions with an extensible output mode.
288     ///
289     /// We intentionally consume and never reexpose the hash function
290     /// provided, so that our domain separation works correctly even
291     /// when using `&mut XoFTranscript : SigningTranscript`.
292     #[inline(always)]
new(h: H) -> XoFTranscript<H>293     pub fn new(h: H) -> XoFTranscript<H> { XoFTranscript(h) }
294 }
295 
296 impl<H> From<H> for XoFTranscript<H>
297 where H: Update + ExtendableOutput + Clone
298 {
299     #[inline(always)]
from(h: H) -> XoFTranscript<H>300     fn from(h: H) -> XoFTranscript<H> { XoFTranscript(h) }
301 }
302 
303 impl<H> SigningTranscript for XoFTranscript<H>
304 where H: Update + ExtendableOutput + Clone
305 {
commit_bytes(&mut self, label: &'static [u8], bytes: &[u8])306     fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) {
307         self.0.update(b"co");
308         input_bytes(&mut self.0, label);
309         input_bytes(&mut self.0, bytes);
310     }
311 
challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8])312     fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) {
313         self.0.update(b"ch");
314         input_bytes(&mut self.0, label);
315         let l = dest.len() as u64;
316         self.0.update(l.to_le_bytes());
317         self.0.clone().chain(b"xof").finalize_xof().read(dest);
318     }
319 
witness_bytes_rng<R>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], mut rng: R) where R: RngCore+CryptoRng320     fn witness_bytes_rng<R>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], mut rng: R)
321     where R: RngCore+CryptoRng
322     {
323         let mut h = self.0.clone().chain(b"wb");
324         input_bytes(&mut h, label);
325         for ns in nonce_seeds {
326             input_bytes(&mut h, ns);
327         }
328         let l = dest.len() as u64;
329         h.update(l.to_le_bytes());
330 
331         let mut r = [0u8; 32];
332         rng.fill_bytes(&mut r);
333         h.update(&r);
334         h.finalize_xof().read(dest);
335     }
336 }
337 
338 
339 /// Schnorr signing transcript with the default `ThreadRng` replaced
340 /// by an arbitrary `CryptoRng`.
341 ///
342 /// If `ThreadRng` breaks on your platform, or merely if you're paranoid,
343 /// then you might "upgrade" from `ThreadRng` to `OsRng` by using calls
344 /// like `keypair.sign( attach_rng(t,OSRng::new()) )`.
345 /// However, we recommend instead simply fixing `ThreadRng` for your platform.
346 ///
347 /// There are also derandomization tricks like
348 /// `attach_rng(t,ChaChaRng::from_seed([0u8; 32]))`
349 /// for deterministic signing in tests too.  Although derandomization
350 /// produces secure signatures, we recommend against doing this in
351 /// production because we implement protocols like multi-signatures
352 /// which likely become vulnerable when derandomized.
353 pub struct SigningTranscriptWithRng<T,R>
354 where T: SigningTranscript, R: RngCore+CryptoRng
355 {
356     t: T,
357     rng: RefCell<R>,
358 }
359 
360 impl<T,R> SigningTranscript for SigningTranscriptWithRng<T,R>
361 where T: SigningTranscript, R: RngCore+CryptoRng
362 {
commit_bytes(&mut self, label: &'static [u8], bytes: &[u8])363     fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8])
364         {  self.t.commit_bytes(label, bytes)  }
365 
challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8])366     fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8])
367         {  self.t.challenge_bytes(label, dest)  }
368 
witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]])369     fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]])
370        {  self.witness_bytes_rng(label, dest, nonce_seeds, &mut *self.rng.borrow_mut())  }
371 
witness_bytes_rng<RR>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: RR) where RR: RngCore+CryptoRng372     fn witness_bytes_rng<RR>(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: RR)
373     where RR: RngCore+CryptoRng
374        {  self.t.witness_bytes_rng(label,dest,nonce_seeds,rng)  }
375 
376 }
377 
378 /// Attach a `CryptoRng` to a `SigningTranscript` to replace the default `ThreadRng`.
379 ///
380 /// There are tricks like `attach_rng(t,ChaChaRng::from_seed([0u8; 32]))`
381 /// for deterministic tests.  We warn against doing this in production
382 /// however because, although such derandomization produces secure Schnorr
383 /// signatures, we do implement protocols here like multi-signatures which
384 /// likely become vulnerable when derandomized.
attach_rng<T,R>(t: T, rng: R) -> SigningTranscriptWithRng<T,R> where T: SigningTranscript, R: RngCore+CryptoRng385 pub fn attach_rng<T,R>(t: T, rng: R) -> SigningTranscriptWithRng<T,R>
386 where T: SigningTranscript, R: RngCore+CryptoRng
387 {
388     SigningTranscriptWithRng {
389         t, rng: RefCell::new(rng)
390     }
391 }
392 
393 /// Attach a fake `Rng` that returns all zeros, only for use in test vectors.
394 /// You must never deploy this because some protocols like MuSig become insecure.
395 #[cfg(test)]
attach_test_vector_rng<T>(t: T) -> SigningTranscriptWithRng<T,impl RngCore+CryptoRng> where T: SigningTranscript396 pub(crate) fn attach_test_vector_rng<T>(t: T) -> SigningTranscriptWithRng<T,impl RngCore+CryptoRng>
397 where T: SigningTranscript
398 {
399     // Very insecure hack except this fn only exists in tests
400     struct ZeroFakeRng;
401     impl ::rand::RngCore for ZeroFakeRng {
402         fn next_u32(&mut self) -> u32 {  panic!()  }
403         fn next_u64(&mut self) -> u64 {  panic!()  }
404         fn fill_bytes(&mut self, dest: &mut [u8]) {
405             for i in dest.iter_mut() {  *i = 0;  }
406         }
407         fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), ::rand_core::Error> {
408             self.fill_bytes(dest);
409             Ok(())
410         }
411     }
412     impl ::rand::CryptoRng for ZeroFakeRng {}
413     attach_rng(t, ZeroFakeRng)
414 }
415 
416 
417 #[cfg(feature = "rand_chacha")]
418 use rand_chacha::ChaChaRng;
419 
420 /// Attach a `ChaChaRng` to a `Transcript` to repalce the default `ThreadRng`
421 #[cfg(feature = "rand_chacha")]
attach_chacharng<T>(t: T, seed: [u8; 32]) -> SigningTranscriptWithRng<T,ChaChaRng> where T: SigningTranscript422 pub fn attach_chacharng<T>(t: T, seed: [u8; 32]) -> SigningTranscriptWithRng<T,ChaChaRng>
423 where T: SigningTranscript
424 {
425     use rand_core::SeedableRng;
426     attach_rng(t,ChaChaRng::from_seed(seed))
427 }
428 
429 
430 
431 /*
432 #[cfg(test)]
433 mod test {
434     use sha3::Shake128;
435     use curve25519_dalek::digest::{Update};
436 
437 }
438 */
439