1use curve25519_dalek::constants;
15use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
16use curve25519_dalek::scalar::Scalar;
17
18use super::*;
19use crate::context::{SigningTranscript};
20
21#[cfg(feature = "alloc")]
22use alloc::vec::Vec;
23#[cfg(feature = "std")]
24use std::vec::Vec;
25
26
27const ASSERT_MESSAGE: &'static str = "The number of messages/transcripts, signatures, and public keys must be equal.";
28
29
30pub fn verify_batch<T,I>(
70 transcripts: I,
71 signatures: &[Signature],
72 public_keys: &[PublicKey],
73 deduplicate_public_keys: bool,
74) -> SignatureResult<()>
75where
76 T: SigningTranscript,
77 I: IntoIterator<Item=T>,
78{
79 verify_batch_rng(transcripts, signatures, public_keys, deduplicate_public_keys, rand_hack())
80}
81
82struct NotAnRng;
83impl rand_core::RngCore for NotAnRng {
84 fn next_u32(&mut self) -> u32 { rand_core::impls::next_u32_via_fill(self) }
85
86 fn next_u64(&mut self) -> u64 { rand_core::impls::next_u64_via_fill(self) }
87
88 fn fill_bytes(&mut self, dest: &mut [u8]) { zeroize::Zeroize::zeroize(dest) }
90
91 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
92 self.fill_bytes(dest);
93 Ok(())
94 }
95}
96impl rand_core::CryptoRng for NotAnRng {}
97
98pub fn verify_batch_deterministic<T,I>(
111 transcripts: I,
112 signatures: &[Signature],
113 public_keys: &[PublicKey],
114 deduplicate_public_keys: bool,
115) -> SignatureResult<()>
116where
117 T: SigningTranscript,
118 I: IntoIterator<Item=T>,
119{
120 verify_batch_rng(transcripts, signatures, public_keys, deduplicate_public_keys, NotAnRng)
121}
122
123pub fn verify_batch_rng<T,I,R>(
127 transcripts: I,
128 signatures: &[Signature],
129 public_keys: &[PublicKey],
130 deduplicate_public_keys: bool,
131 rng: R,
132) -> SignatureResult<()>
133where
134 T: SigningTranscript,
135 I: IntoIterator<Item=T>,
136 R: RngCore+CryptoRng,
137{
138 assert!(signatures.len() == public_keys.len(), "{}", ASSERT_MESSAGE); let (zs, hrams) = prepare_batch(transcripts, signatures, public_keys, rng);
141
142 let bs: Scalar = signatures.iter()
144 .map(|sig| sig.s)
145 .zip(zs.iter())
146 .map(|(s, z)| z * s)
147 .sum();
148
149 verify_batch_equation( bs, zs, hrams, signatures, public_keys, deduplicate_public_keys )
150}
151
152
153trait HasR {
154 #[allow(non_snake_case)]
155 fn get_R(&self) -> &CompressedRistretto;
156}
157impl HasR for Signature {
158 #[allow(non_snake_case)]
159 fn get_R(&self) -> &CompressedRistretto { &self.R }
160}
161impl HasR for CompressedRistretto {
162 #[allow(non_snake_case)]
163 fn get_R(&self) -> &CompressedRistretto { self }
164}
165
166#[allow(non_snake_case)]
169fn prepare_batch<T,I,R>(
170 transcripts: I,
171 signatures: &[impl HasR],
172 public_keys: &[PublicKey],
173 mut rng: R,
174) -> (Vec<Scalar>,Vec<Scalar>)
175where
176 T: SigningTranscript,
177 I: IntoIterator<Item=T>,
178 R: RngCore+CryptoRng,
179{
180
181 let mut zs_t = merlin::Transcript::new(b"V-RNG");
183 for pk in public_keys {
184 zs_t.commit_point(b"",pk.as_compressed());
185 }
186 for sig in signatures {
187 zs_t.commit_point(b"",sig.get_R());
188 }
189
190 let mut transcripts = transcripts.into_iter();
193 let hrams: Vec<Scalar> = transcripts.by_ref()
195 .zip(0..signatures.len())
196 .map( |(mut t,i)| {
197 let mut d = [0u8; 16];
198 t.witness_bytes_rng(b"", &mut d, &[&[]], NotAnRng); zs_t.append_message(b"",&d);
200
201 t.proto_name(b"Schnorr-sig");
202 t.commit_point(b"sign:pk",public_keys[i].as_compressed());
203 t.commit_point(b"sign:R",signatures[i].get_R());
204 t.challenge_scalar(b"sign:c") } ).collect();
206 assert!(transcripts.next().is_none(), "{}", ASSERT_MESSAGE);
207 assert!(hrams.len() == public_keys.len(), "{}", ASSERT_MESSAGE);
208
209 let mut csprng = zs_t.build_rng().finalize(&mut rng);
212 let rnd_128bit_scalar = |_| {
216 let mut r = [0u8; 16];
217 csprng.fill_bytes(&mut r);
218 Scalar::from(u128::from_le_bytes(r))
219 };
220 let zs: Vec<Scalar> = signatures.iter().map(rnd_128bit_scalar).collect();
221
222 (zs, hrams)
223}
224
225#[allow(non_snake_case)]
227fn verify_batch_equation(
228 bs: Scalar,
229 zs: Vec<Scalar>,
230 mut hrams: Vec<Scalar>,
231 signatures: &[impl HasR],
232 public_keys: &[PublicKey],
233 deduplicate_public_keys: bool,
234) -> SignatureResult<()>
235{
236 use curve25519_dalek::traits::IsIdentity;
237 use curve25519_dalek::traits::VartimeMultiscalarMul;
238
239 use core::iter::once;
240
241 let B = once(Some(constants::RISTRETTO_BASEPOINT_POINT));
242
243 let Rs = signatures.iter().map(|sig| sig.get_R().decompress());
244
245 let mut ppks = Vec::new();
246 let As = if ! deduplicate_public_keys {
247 for (hram, z) in hrams.iter_mut().zip(zs.iter()) {
249 *hram = &*hram * z;
250 }
251 public_keys
252 } else {
253 ppks.reserve( public_keys.len() );
255 for i in 0..public_keys.len() {
257 let zhram = &hrams[i] * zs[i];
258 let j = ppks.len().checked_sub(1);
259 if j.is_none() || ppks[j.unwrap()] != public_keys[i] {
260 ppks.push(public_keys[i]);
261 hrams[ppks.len()-1] = zhram;
262 } else {
263 hrams[ppks.len()-1] = &hrams[ppks.len()-1] + zhram;
264 }
265 }
266 hrams.truncate(ppks.len());
267 ppks.as_slice()
268 }.iter().map(|pk| Some(pk.as_point().clone()));
269
270 let b = RistrettoPoint::optional_multiscalar_mul(
272 once(-bs).chain(zs.iter().cloned()).chain(hrams),
273 B.chain(Rs).chain(As),
274 ).map(|id| id.is_identity()).unwrap_or(false);
275 if b { Ok(()) } else { Err(SignatureError::EquationFalse) }
279}
280
281
282
283#[allow(non_snake_case)]
290pub struct PreparedBatch {
291 bs: Scalar,
292 Rs: Vec<CompressedRistretto>,
293}
294
295impl PreparedBatch{
296
297 #[allow(non_snake_case)]
299 pub fn new<T,I,R>(
300 transcripts: I,
301 signatures: &[Signature],
302 public_keys: &[PublicKey],
303 ) -> PreparedBatch
304 where
305 T: SigningTranscript,
306 I: IntoIterator<Item=T>,
307 {
308 assert!(signatures.len() == public_keys.len(), "{}", ASSERT_MESSAGE); let (zs, _hrams) = prepare_batch(transcripts, signatures, public_keys, NotAnRng);
311
312 let bs: Scalar = signatures.iter()
314 .map(|sig| sig.s)
315 .zip(zs.iter())
316 .map(|(s, z)| z * s)
317 .sum();
318
319 let Rs = signatures.iter().map(|sig| sig.R).collect();
320 PreparedBatch { bs, Rs, }
321 }
322
323 #[allow(non_snake_case)]
325 pub fn verify<T,I>(
326 &self,
327 transcripts: I,
328 public_keys: &[PublicKey],
329 deduplicate_public_keys: bool,
330 ) -> SignatureResult<()>
331 where
332 T: SigningTranscript,
333 I: IntoIterator<Item=T>,
334 {
335 assert!(self.Rs.len() == public_keys.len(), "{}", ASSERT_MESSAGE); let (zs, hrams) = prepare_batch(transcripts, self.Rs.as_slice(), public_keys, NotAnRng);
338
339 verify_batch_equation(
340 self.bs,
341 zs, hrams,
342 self.Rs.as_slice(),
343 public_keys, deduplicate_public_keys
344 )
345 }
346
347 #[allow(non_snake_case)]
349 pub fn read_bytes(mut bytes: &[u8]) -> SignatureResult<PreparedBatch> {
350 use arrayref::array_ref;
351 if bytes.len() % 32 != 0 || bytes.len() < 64 {
352 return Err(SignatureError::BytesLengthError {
353 name: "PreparedBatch",
354 description: "A Prepared batched signature",
355 length: 0 });
357 }
358 let l = (bytes.len() % 32) - 1;
359 let mut read = || {
360 let (head,tail) = bytes.split_at(32);
361 bytes = tail;
362 array_ref![head,0,32].clone()
363 };
364 let mut bs = read();
365 bs[31] &= 127;
366 let bs = super::sign::check_scalar(bs) ?;
367 let mut Rs = Vec::with_capacity(l);
368 for _ in 0..l {
369 Rs.push( CompressedRistretto(read()) );
370 }
371 Ok(PreparedBatch { bs, Rs })
372 }
373
374 #[allow(non_snake_case)]
376 pub fn byte_len(&self) -> usize {
377 32 + 32 * self.Rs.len()
378 }
379
380 #[allow(non_snake_case)]
382 pub fn write_bytes(&self, mut bytes: &mut [u8]) {
383 assert!(bytes.len() == self.byte_len());
384 let mut place = |s: &[u8]| reserve_mut(&mut bytes,32).copy_from_slice(s);
385 let mut bs = self.bs.to_bytes();
386 bs[31] |= 128;
387 place(&bs[..]);
388 for R in self.Rs.iter() {
389 place(R.as_bytes());
390 }
391 }
392}
393
394
395pub fn reserve_mut<'heap, T>(heap: &mut &'heap mut [T], len: usize) -> &'heap mut [T] {
396 let tmp: &'heap mut [T] = ::std::mem::replace(&mut *heap, &mut []);
397 let (reserved, tmp) = tmp.split_at_mut(len);
398 *heap = tmp;
399 reserved
400}
401
402
403#[cfg(test)]
404mod test {
405 #[cfg(feature = "alloc")]
406 use alloc::vec::Vec;
407 #[cfg(feature = "std")]
408 use std::vec::Vec;
409
410 use rand::prelude::*; use super::super::*;
413
414 #[cfg(any(feature = "alloc", feature = "std"))]
415 #[test]
416 fn verify_batch_seven_signatures() {
417 let ctx = signing_context(b"my batch context");
418
419 let messages: [&[u8]; 7] = [
420 b"Watch closely everyone, I'm going to show you how to kill a god.",
421 b"I'm not a cryptographer I just encrypt a lot.",
422 b"Still not a cryptographer.",
423 b"This is a test of the tsunami alert system. This is only a test.",
424 b"Fuck dumbin' it down, spit ice, skip jewellery: Molotov cocktails on me like accessories.",
425 b"Hey, I never cared about your bucks, so if I run up with a mask on, probably got a gas can too.",
426 b"And I'm not here to fill 'er up. Nope, we came to riot, here to incite, we don't want any of your stuff.", ];
427 let mut csprng: ThreadRng = thread_rng();
428 let mut keypairs: Vec<Keypair> = Vec::new();
429 let mut signatures: Vec<Signature> = Vec::new();
430
431 for i in 0..messages.len() {
432 let mut keypair: Keypair = Keypair::generate_with(&mut csprng);
433 if i == 3 || i == 4 { keypair = keypairs[0].clone(); }
434 signatures.push(keypair.sign(ctx.bytes(messages[i])));
435 keypairs.push(keypair);
436 }
437 let mut public_keys: Vec<PublicKey> = keypairs.iter().map(|key| key.public).collect();
438
439 public_keys.swap(1,2);
440 let transcripts = messages.iter().map(|m| ctx.bytes(m));
441 assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], false).is_err() );
442 let transcripts = messages.iter().map(|m| ctx.bytes(m));
443 assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], true).is_err() );
444
445 public_keys.swap(1,2);
446 let transcripts = messages.iter().map(|m| ctx.bytes(m));
447 assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], false).is_ok() );
448 let transcripts = messages.iter().map(|m| ctx.bytes(m));
449 assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], true).is_ok() );
450
451 signatures.swap(1,2);
452 let transcripts = messages.iter().map(|m| ctx.bytes(m));
453 assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], false).is_err() );
454 let transcripts = messages.iter().map(|m| ctx.bytes(m));
455 assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], true).is_err() );
456 }
457}