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