1use super::{
12 super::{
13 group::Share,
14 sharing::Sharing,
15 variant::{PartialSignature, Variant},
16 Error,
17 },
18 batch,
19};
20#[cfg(not(feature = "std"))]
21use alloc::{vec, vec::Vec};
22use commonware_codec::Encode;
23use commonware_parallel::Strategy;
24use commonware_utils::{ordered::Map, union_unique, Faults, Participant};
25use rand_core::CryptoRngCore;
26
27fn prepare_evaluations<'a, V: Variant>(
29 threshold: u32,
30 partials: impl IntoIterator<Item = &'a PartialSignature<V>>,
31) -> Result<Map<Participant, V::Signature>, Error> {
32 let mut out = Map::from_iter_dedup(partials.into_iter().map(|eval| (eval.index, eval.value)));
33 let t = threshold as usize;
34 out.truncate(t);
35 if out.len() < t {
36 return Err(Error::NotEnoughPartialSignatures(t, out.len()));
37 }
38 Ok(out)
39}
40
41pub fn sign_message<V: Variant>(
43 share: &Share,
44 namespace: &[u8],
45 message: &[u8],
46) -> PartialSignature<V> {
47 let sig = super::sign_message::<V>(&share.private, namespace, message);
48
49 PartialSignature {
50 value: sig,
51 index: share.index,
52 }
53}
54
55pub fn sign_proof_of_possession<V: Variant>(
61 sharing: &Sharing<V>,
62 share: &Share,
63 namespace: &[u8],
64) -> PartialSignature<V> {
65 let sig = super::sign::<V>(
66 &share.private,
67 V::PROOF_OF_POSSESSION,
68 &union_unique(namespace, &sharing.public().encode()),
69 );
70
71 PartialSignature {
72 value: sig,
73 index: share.index,
74 }
75}
76
77pub fn verify_message<V: Variant>(
83 sharing: &Sharing<V>,
84 namespace: &[u8],
85 message: &[u8],
86 partial: &PartialSignature<V>,
87) -> Result<(), Error> {
88 super::verify_message::<V>(
89 &sharing.partial_public(partial.index)?,
90 namespace,
91 message,
92 &partial.value,
93 )
94}
95
96pub fn verify_proof_of_possession<V: Variant>(
102 sharing: &Sharing<V>,
103 namespace: &[u8],
104 partial: &PartialSignature<V>,
105) -> Result<(), Error> {
106 super::verify::<V>(
107 &sharing.partial_public(partial.index)?,
108 V::PROOF_OF_POSSESSION,
109 &union_unique(namespace, &sharing.public().encode()),
110 &partial.value,
111 )
112}
113
114pub fn batch_verify_same_signer<'a, R, V, I>(
127 rng: &mut R,
128 sharing: &Sharing<V>,
129 index: Participant,
130 entries: I,
131 strategy: &impl Strategy,
132) -> Result<(), Error>
133where
134 R: CryptoRngCore,
135 V: Variant,
136 I: IntoIterator<Item = &'a (&'a [u8], &'a [u8], PartialSignature<V>)>,
137{
138 let combined: Vec<_> = entries
140 .into_iter()
141 .map(|(ns, msg, ps)| {
142 if ps.index != index {
143 Err(Error::InvalidSignature)
144 } else {
145 Ok((*ns, *msg, ps.value))
146 }
147 })
148 .collect::<Result<_, _>>()?;
149
150 let public = sharing.partial_public(index)?;
151
152 batch::verify_same_signer::<_, V, _>(rng, &public, &combined, strategy)
153}
154
155fn batch_verify_same_message_bisect<'a, R, V>(
168 rng: &mut R,
169 pending: &[(V::Public, &'a PartialSignature<V>)],
170 namespace: &[u8],
171 message: &[u8],
172 strategy: &impl Strategy,
173) -> Vec<&'a PartialSignature<V>>
174where
175 R: CryptoRngCore,
176 V: Variant,
177{
178 let entries: Vec<(V::Public, V::Signature)> = pending
180 .iter()
181 .map(|(pk, partial)| (*pk, partial.value))
182 .collect();
183
184 let invalid_indices =
186 batch::verify_same_message::<_, V>(rng, namespace, message, &entries, strategy);
187
188 invalid_indices
190 .into_iter()
191 .map(|idx| pending[idx].1)
192 .collect()
193}
194
195pub fn batch_verify_same_message<'a, R, V, I>(
207 rng: &mut R,
208 sharing: &Sharing<V>,
209 namespace: &[u8],
210 message: &[u8],
211 partials: I,
212 strategy: &impl Strategy,
213) -> Result<(), Vec<&'a PartialSignature<V>>>
214where
215 R: CryptoRngCore,
216 V: Variant,
217 I: IntoIterator<Item = &'a PartialSignature<V>>,
218{
219 let partials = partials.into_iter();
220 let mut pending = Vec::with_capacity(partials.size_hint().0);
221 let mut invalid = Vec::new();
222 for partial in partials {
223 match sharing.partial_public(partial.index) {
224 Ok(p) => pending.push((p, partial)),
225 Err(_) => invalid.push(partial),
226 }
227 }
228
229 let bad = batch_verify_same_message_bisect::<_, V>(
231 rng,
232 pending.as_slice(),
233 namespace,
234 message,
235 strategy,
236 );
237 invalid.extend(bad);
238
239 if invalid.is_empty() {
240 Ok(())
241 } else {
242 Err(invalid)
243 }
244}
245
246pub fn recover<'a, V, I, M>(
257 sharing: &Sharing<V>,
258 partials: I,
259 strategy: &impl Strategy,
260) -> Result<V::Signature, Error>
261where
262 V: Variant,
263 I: IntoIterator<Item = &'a PartialSignature<V>>,
264 V::Signature: 'a,
265 M: Faults,
266{
267 let evals = prepare_evaluations::<V>(sharing.required::<M>(), partials)?;
268 sharing
269 .interpolator(evals.keys())?
270 .interpolate(&evals, strategy)
271 .ok_or(Error::InvalidRecovery)
272}
273
274pub fn recover_multiple<'a, V, I, M>(
287 sharing: &Sharing<V>,
288 many_evals: Vec<I>,
289 strategy: &impl Strategy,
290) -> Result<Vec<V::Signature>, Error>
291where
292 V: Variant,
293 I: IntoIterator<Item = &'a PartialSignature<V>>,
294 V::Signature: 'a,
295 M: Faults,
296{
297 let prepared_evals = many_evals
298 .into_iter()
299 .map(|evals| prepare_evaluations::<V>(sharing.required::<M>(), evals))
300 .collect::<Result<Vec<_>, _>>()?;
301 let Some(first_eval) = prepared_evals.first() else {
302 return Ok(Vec::new());
303 };
304 if !prepared_evals
305 .iter()
306 .skip(1)
307 .all(|other_eval| other_eval.keys() == first_eval.keys())
308 {
309 return Err(Error::InvalidIndex);
310 }
311
312 let interpolator = sharing.interpolator(first_eval.keys())?;
313 let results: Vec<_> = strategy.map_init_collect_vec(
314 &prepared_evals,
315 || &interpolator,
316 |interpolator, evals| {
317 interpolator
318 .interpolate(evals, strategy)
319 .ok_or(Error::InvalidRecovery)
320 },
321 );
322 results.into_iter().collect()
323}
324
325pub fn recover_pair<'a, V, I, M>(
329 sharing: &Sharing<V>,
330 first: I,
331 second: I,
332 strategy: &impl Strategy,
333) -> Result<(V::Signature, V::Signature), Error>
334where
335 V: Variant,
336 I: IntoIterator<Item = &'a PartialSignature<V>>,
337 V::Signature: 'a,
338 M: Faults,
339{
340 let mut sigs = recover_multiple::<V, _, M>(sharing, vec![first, second], strategy)?;
341 let second_sig = sigs.pop().unwrap();
342 let first_sig = sigs.pop().unwrap();
343 Ok((first_sig, second_sig))
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349 use crate::bls12381::{
350 dkg,
351 primitives::{
352 group::{Private, Scalar, G1_MESSAGE, G2_MESSAGE},
353 ops::{self, hash_with_namespace},
354 variant::{MinPk, MinSig},
355 },
356 };
357 use blst::BLST_ERROR;
358 use commonware_codec::Encode;
359 use commonware_math::algebra::{CryptoGroup, Field as _, Random, Ring, Space};
360 use commonware_parallel::{Rayon, Sequential};
361 use commonware_utils::{test_rng, union_unique, Faults, N3f1, NZUsize, NZU32};
362
363 fn blst_verify_proof_of_possession<V: Variant>(
364 public: &V::Public,
365 namespace: &[u8],
366 signature: &V::Signature,
367 ) -> Result<(), BLST_ERROR> {
368 let msg = union_unique(namespace, &public.encode());
369 match V::MESSAGE {
370 G1_MESSAGE => {
371 let public = blst::min_sig::PublicKey::from_bytes(&public.encode()).unwrap();
372 let signature = blst::min_sig::Signature::from_bytes(&signature.encode()).unwrap();
373 match signature.verify(true, &msg, V::PROOF_OF_POSSESSION, &[], &public, true) {
374 BLST_ERROR::BLST_SUCCESS => Ok(()),
375 e => Err(e),
376 }
377 }
378 G2_MESSAGE => {
379 let public = blst::min_pk::PublicKey::from_bytes(&public.encode()).unwrap();
380 let signature = blst::min_pk::Signature::from_bytes(&signature.encode()).unwrap();
381 match signature.verify(true, &msg, V::PROOF_OF_POSSESSION, &[], &public, true) {
382 BLST_ERROR::BLST_SUCCESS => Ok(()),
383 e => Err(e),
384 }
385 }
386 _ => panic!("Unsupported Variant"),
387 }
388 }
389
390 fn threshold_proof_of_possession<V: Variant>() {
391 let n = 5;
392 let mut rng = test_rng();
393 let namespace = b"test";
394 let (sharing, shares) =
395 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(n));
396 let partials: Vec<_> = shares
397 .iter()
398 .map(|s| sign_proof_of_possession::<V>(&sharing, s, namespace))
399 .collect();
400 for p in &partials {
401 verify_proof_of_possession::<V>(&sharing, namespace, p)
402 .expect("signature should be valid");
403 }
404 let threshold_sig = recover::<V, _, N3f1>(&sharing, &partials, &Sequential).unwrap();
405 let threshold_pub = sharing.public();
406
407 ops::verify_proof_of_possession::<V>(threshold_pub, namespace, &threshold_sig)
408 .expect("signature should be valid");
409
410 blst_verify_proof_of_possession::<V>(threshold_pub, namespace, &threshold_sig)
411 .expect("signature should be valid");
412 }
413
414 #[test]
415 fn test_threshold_proof_of_possession() {
416 threshold_proof_of_possession::<MinPk>();
417 threshold_proof_of_possession::<MinSig>();
418 }
419
420 fn blst_verify_message<V: Variant>(
421 public: &V::Public,
422 msg: &[u8],
423 signature: &V::Signature,
424 ) -> Result<(), BLST_ERROR> {
425 match V::MESSAGE {
426 G1_MESSAGE => {
427 let public = blst::min_sig::PublicKey::from_bytes(&public.encode()).unwrap();
428 let signature = blst::min_sig::Signature::from_bytes(&signature.encode()).unwrap();
429 match signature.verify(true, msg, V::MESSAGE, &[], &public, true) {
430 BLST_ERROR::BLST_SUCCESS => Ok(()),
431 e => Err(e),
432 }
433 }
434 G2_MESSAGE => {
435 let public = blst::min_pk::PublicKey::from_bytes(&public.encode()).unwrap();
436 let signature = blst::min_pk::Signature::from_bytes(&signature.encode()).unwrap();
437 match signature.verify(true, msg, V::MESSAGE, &[], &public, true) {
438 BLST_ERROR::BLST_SUCCESS => Ok(()),
439 e => Err(e),
440 }
441 }
442 _ => panic!("Unsupported Variant"),
443 }
444 }
445
446 fn threshold_message<V: Variant>() {
447 let n = 5;
448 let mut rng = test_rng();
449 let (sharing, shares) =
450 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(n));
451 let msg = &[1, 9, 6, 9];
452 let namespace = b"test";
453 let partials: Vec<_> = shares
454 .iter()
455 .map(|s| sign_message::<V>(s, namespace, msg))
456 .collect();
457 for p in &partials {
458 verify_message::<V>(&sharing, namespace, msg, p).expect("signature should be valid");
459 }
460 let threshold_sig = recover::<V, _, N3f1>(&sharing, &partials, &Sequential).unwrap();
461 let threshold_pub = sharing.public();
462
463 ops::verify_message::<V>(threshold_pub, namespace, msg, &threshold_sig)
464 .expect("signature should be valid");
465
466 let payload = union_unique(namespace, msg);
467 blst_verify_message::<V>(threshold_pub, &payload, &threshold_sig)
468 .expect("signature should be valid");
469 }
470
471 #[test]
472 fn test_threshold_message() {
473 threshold_message::<MinPk>();
474 threshold_message::<MinSig>();
475 }
476
477 fn batch_verify_same_signer_correct<V: Variant>() {
478 let mut rng = test_rng();
479 let n = 5;
480 let (public, shares) =
481 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(n));
482
483 let signer = &shares[0];
484
485 let messages: &[(&[u8], &[u8])] = &[(b"ns", b"msg1"), (b"ns", b"msg2"), (b"ns", b"msg3")];
486 let entries: Vec<_> = messages
487 .iter()
488 .map(|(ns, msg)| (*ns, *msg, sign_message::<V>(signer, ns, msg)))
489 .collect();
490 batch_verify_same_signer::<_, V, _>(&mut rng, &public, signer.index, &entries, &Sequential)
491 .expect("Verification with namespaced messages should succeed");
492
493 let strategy = Rayon::new(NZUsize!(4)).unwrap();
494 batch_verify_same_signer::<_, V, _>(&mut rng, &public, signer.index, &entries, &strategy)
495 .expect("Verification with parallel strategy should succeed");
496
497 let messages_alt_ns: &[(&[u8], &[u8])] =
498 &[(b"alt", b"msg1"), (b"alt", b"msg2"), (b"alt", b"msg3")];
499 let entries_alt_ns: Vec<_> = messages_alt_ns
500 .iter()
501 .map(|(ns, msg)| (*ns, *msg, sign_message::<V>(signer, ns, msg)))
502 .collect();
503 batch_verify_same_signer::<_, V, _>(
504 &mut rng,
505 &public,
506 signer.index,
507 &entries_alt_ns,
508 &Sequential,
509 )
510 .expect("Verification with alternate namespace messages should succeed");
511
512 let messages_mixed: &[(&[u8], &[u8])] =
513 &[(b"ns1", b"msg1"), (b"ns2", b"msg2"), (b"ns3", b"msg3")];
514 let entries_mixed: Vec<_> = messages_mixed
515 .iter()
516 .map(|(ns, msg)| (*ns, *msg, sign_message::<V>(signer, ns, msg)))
517 .collect();
518 batch_verify_same_signer::<_, V, _>(
519 &mut rng,
520 &public,
521 signer.index,
522 &entries_mixed,
523 &Sequential,
524 )
525 .expect("Verification with mixed namespaces should succeed");
526
527 assert!(matches!(
528 batch_verify_same_signer::<_, V, _>(
529 &mut rng,
530 &public,
531 Participant::new(1),
532 &entries,
533 &Sequential
534 ),
535 Err(Error::InvalidSignature)
536 ));
537
538 let mut entries_swapped = entries.clone();
539 let temp_sig = entries_swapped[0].2.clone();
540 entries_swapped[0].2 = entries_swapped[1].2.clone();
541 entries_swapped[1].2 = temp_sig;
542 assert!(
543 batch_verify_same_signer::<_, V, _>(
544 &mut rng,
545 &public,
546 signer.index,
547 &entries_swapped,
548 &Sequential,
549 )
550 .is_err(),
551 "Verification with swapped signatures should fail"
552 );
553
554 let signer2 = &shares[1];
555 let partial2 = sign_message::<V>(signer2, messages[0].0, messages[0].1);
556 let mut entries_mixed_signers = entries;
557 entries_mixed_signers[0].2 = partial2;
558 assert!(matches!(
559 batch_verify_same_signer::<_, V, _>(
560 &mut rng,
561 &public,
562 signer.index,
563 &entries_mixed_signers,
564 &Sequential,
565 ),
566 Err(Error::InvalidSignature)
567 ));
568 }
569
570 #[test]
571 fn test_batch_verify_same_signer() {
572 batch_verify_same_signer_correct::<MinPk>();
573 batch_verify_same_signer_correct::<MinSig>();
574 }
575
576 fn recover_with_weights_correct<V: Variant>() {
577 let mut rng = test_rng();
578 let (n, t) = (6, N3f1::quorum(6));
579 let (sharing, shares) =
580 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(n));
581
582 let partials: Vec<_> = shares
583 .iter()
584 .take(t as usize)
585 .map(|s| sign_message::<V>(s, b"test", b"payload"))
586 .collect();
587
588 let sig1 = recover::<V, _, N3f1>(&sharing, &partials, &Sequential).unwrap();
589
590 ops::verify_message::<V>(sharing.public(), b"test", b"payload", &sig1).unwrap();
591 }
592
593 #[test]
594 fn test_recover_with_weights() {
595 recover_with_weights_correct::<MinPk>();
596 recover_with_weights_correct::<MinSig>();
597 }
598
599 fn recover_multiple_test<V: Variant>() {
600 let mut rng = test_rng();
601 let (n, t) = (6, N3f1::quorum(6));
602 let (sharing, shares) =
603 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(n));
604
605 let partials_1: Vec<_> = shares
606 .iter()
607 .take(t as usize)
608 .map(|s| sign_message::<V>(s, b"test", b"payload1"))
609 .collect();
610 let partials_2: Vec<_> = shares
611 .iter()
612 .take(t as usize)
613 .map(|s| sign_message::<V>(s, b"test", b"payload2"))
614 .collect();
615
616 let (sig_1, sig_2) =
617 recover_pair::<V, _, N3f1>(&sharing, &partials_1, &partials_2, &Sequential).unwrap();
618
619 ops::verify_message::<V>(sharing.public(), b"test", b"payload1", &sig_1).unwrap();
620 ops::verify_message::<V>(sharing.public(), b"test", b"payload2", &sig_2).unwrap();
621
622 let parallel = Rayon::new(NZUsize!(4)).unwrap();
623 let (sig_1_par, sig_2_par) =
624 recover_pair::<V, _, N3f1>(&sharing, &partials_1, &partials_2, ¶llel).unwrap();
625
626 assert_eq!(sig_1, sig_1_par);
627 assert_eq!(sig_2, sig_2_par);
628 }
629
630 #[test]
631 fn test_recover_multiple() {
632 recover_multiple_test::<MinPk>();
633 recover_multiple_test::<MinSig>();
634 }
635
636 fn recover_with_verification<V: Variant>() {
637 let (n, _) = (5, 4);
638 let mut rng = test_rng();
639
640 let (sharing, shares) =
641 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(n));
642
643 let namespace = b"test";
644 let msg = b"hello";
645 let partials = shares
646 .iter()
647 .map(|s| sign_message::<V>(s, namespace, msg))
648 .collect::<Vec<_>>();
649
650 partials.iter().for_each(|partial| {
651 verify_message::<V>(&sharing, namespace, msg, partial).unwrap();
652 });
653
654 let threshold_sig = recover::<V, _, N3f1>(&sharing, &partials, &Sequential).unwrap();
655 ops::verify_message::<V>(sharing.public(), namespace, msg, &threshold_sig).unwrap();
656 }
657
658 #[test]
659 fn test_recover_with_verification() {
660 recover_with_verification::<MinPk>();
661 recover_with_verification::<MinSig>();
662 }
663
664 fn recover_bad_namespace<V: Variant>() {
665 let n = 5;
666 let mut rng = test_rng();
667
668 let (sharing, shares) =
669 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(n));
670
671 let namespace = b"test";
672 let msg = b"hello";
673 let partials = shares
674 .iter()
675 .map(|s| sign_message::<V>(s, namespace, msg))
676 .collect::<Vec<_>>();
677
678 let namespace = b"bad";
679 partials.iter().for_each(|partial| {
680 assert!(matches!(
681 verify_message::<V>(&sharing, namespace, msg, partial).unwrap_err(),
682 Error::InvalidSignature
683 ));
684 });
685
686 let threshold_sig = recover::<V, _, N3f1>(&sharing, &partials, &Sequential).unwrap();
687 assert!(matches!(
688 ops::verify_message::<V>(sharing.public(), namespace, msg, &threshold_sig).unwrap_err(),
689 Error::InvalidSignature
690 ));
691 }
692
693 #[test]
694 fn test_recover_bad_namespace() {
695 recover_bad_namespace::<MinPk>();
696 recover_bad_namespace::<MinSig>();
697 }
698
699 fn recover_insufficient<V: Variant>() {
700 let (n, t) = (5, 4);
701 let mut rng = test_rng();
702
703 let (group, shares) =
704 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(n));
705
706 let shares = shares.into_iter().take(t as usize - 1).collect::<Vec<_>>();
707
708 let namespace = b"test";
709 let msg = b"hello";
710 let partials = shares
711 .iter()
712 .map(|s| sign_message::<V>(s, namespace, msg))
713 .collect::<Vec<_>>();
714
715 partials.iter().for_each(|partial| {
716 verify_message::<V>(&group, namespace, msg, partial).unwrap();
717 });
718
719 assert!(matches!(
720 recover::<V, _, N3f1>(&group, &partials, &Sequential).unwrap_err(),
721 Error::NotEnoughPartialSignatures(4, 3)
722 ));
723 }
724
725 #[test]
726 fn test_recover_insufficient() {
727 recover_insufficient::<MinPk>();
728 recover_insufficient::<MinSig>();
729 }
730
731 fn recover_bad_share<V: Variant>() {
732 let n = 5;
733 let mut rng = test_rng();
734
735 let (sharing, mut shares) =
736 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(n));
737
738 let share = shares.get_mut(3).unwrap();
739 share.private = Private::random(&mut rng);
740
741 let namespace = b"test";
742 let msg = b"hello";
743 let partials = shares
744 .iter()
745 .map(|s| sign_message::<V>(s, namespace, msg))
746 .collect::<Vec<_>>();
747
748 partials.iter().for_each(|partial| {
749 verify_message::<V>(&sharing, namespace, msg, partial).unwrap();
750 });
751
752 let threshold_sig = recover::<V, _, N3f1>(&sharing, &partials, &Sequential).unwrap();
753 ops::verify_message::<V>(sharing.public(), namespace, msg, &threshold_sig).unwrap();
754 }
755
756 #[test]
757 #[should_panic(expected = "InvalidSignature")]
758 fn test_recover_bad_share() {
759 recover_bad_share::<MinPk>();
760 recover_bad_share::<MinSig>();
761 }
762
763 #[test]
764 fn test_batch_verify_same_message() {
765 let mut rng = test_rng();
766 let n = 5;
767 let (sharing, shares) =
768 dkg::deal_anonymous::<MinSig, N3f1>(&mut rng, Default::default(), NZU32!(n));
769 let namespace = b"test";
770 let msg = b"hello";
771
772 let partials: Vec<_> = shares
773 .iter()
774 .map(|s| sign_message::<MinSig>(s, namespace, msg))
775 .collect();
776 sharing.precompute_partial_publics();
777
778 batch_verify_same_message::<_, MinSig, _>(
779 &mut rng,
780 &sharing,
781 namespace,
782 msg,
783 &partials,
784 &Sequential,
785 )
786 .expect("all signatures should be valid");
787 }
788
789 #[test]
790 fn test_batch_verify_same_message_one_invalid() {
791 let mut rng = test_rng();
792 let n = 5;
793 let (sharing, mut shares) =
794 dkg::deal_anonymous::<MinSig, N3f1>(&mut rng, Default::default(), NZU32!(n));
795 let namespace = b"test";
796 let msg = b"hello";
797
798 let corrupted_index = 1;
799 shares[corrupted_index].private = Private::random(&mut rng);
800
801 let partials: Vec<_> = shares
802 .iter()
803 .map(|s| sign_message::<MinSig>(s, namespace, msg))
804 .collect();
805
806 sharing.precompute_partial_publics();
807 let result = batch_verify_same_message::<_, MinSig, _>(
808 &mut rng,
809 &sharing,
810 namespace,
811 msg,
812 &partials,
813 &Sequential,
814 );
815 match result {
816 Err(invalid_sigs) => {
817 assert_eq!(
818 invalid_sigs.len(),
819 1,
820 "Exactly one signature should be invalid"
821 );
822 assert_eq!(
823 invalid_sigs[0].index,
824 Participant::from_usize(corrupted_index),
825 "The invalid signature should match the corrupted share's index"
826 );
827 }
828 _ => panic!("Expected an error with invalid signatures"),
829 }
830 }
831
832 #[test]
833 fn test_batch_verify_same_message_many_invalid() {
834 let mut rng = test_rng();
835 let n = 6;
836 let (sharing, mut shares) =
837 dkg::deal_anonymous::<MinSig, N3f1>(&mut rng, Default::default(), NZU32!(n));
838 let namespace = b"test";
839 let msg = b"hello";
840
841 let corrupted_indices = vec![1, 3];
842 for &idx in &corrupted_indices {
843 shares[idx].private = Private::random(&mut rng);
844 }
845
846 let partials: Vec<_> = shares
847 .iter()
848 .map(|s| sign_message::<MinSig>(s, namespace, msg))
849 .collect();
850 sharing.precompute_partial_publics();
851
852 let result = batch_verify_same_message::<_, MinSig, _>(
853 &mut rng,
854 &sharing,
855 namespace,
856 msg,
857 &partials,
858 &Sequential,
859 );
860 match result {
861 Err(invalid_sigs) => {
862 assert_eq!(
863 invalid_sigs.len(),
864 corrupted_indices.len(),
865 "Number of invalid signatures should match number of corrupted shares"
866 );
867 let invalid_indices: Vec<Participant> =
868 invalid_sigs.iter().map(|sig| sig.index).collect();
869 let expected_indices: Vec<Participant> = corrupted_indices
870 .iter()
871 .map(|&i| Participant::from_usize(i))
872 .collect();
873 assert_eq!(
874 invalid_indices, expected_indices,
875 "Invalid signature indices should match corrupted share indices"
876 );
877 }
878 _ => panic!("Expected an error with invalid signatures"),
879 }
880 }
881
882 #[test]
883 fn test_batch_verify_same_message_out_of_range() {
884 let mut rng = test_rng();
885 let n = 5;
886 let (sharing, shares) =
887 dkg::deal_anonymous::<MinSig, N3f1>(&mut rng, Default::default(), NZU32!(n));
888 let namespace = b"test";
889 let msg = b"hello";
890
891 let mut partials: Vec<_> = shares
892 .iter()
893 .map(|s| sign_message::<MinSig>(s, namespace, msg))
894 .collect();
895
896 partials[0].index = Participant::new(100);
897
898 sharing.precompute_partial_publics();
899 let result = batch_verify_same_message::<_, MinSig, _>(
900 &mut rng,
901 &sharing,
902 namespace,
903 msg,
904 &partials,
905 &Sequential,
906 );
907 match result {
908 Err(invalid_sigs) => {
909 assert_eq!(
910 invalid_sigs.len(),
911 1,
912 "Exactly one signature should be invalid"
913 );
914 assert_eq!(
915 invalid_sigs[0].index,
916 Participant::new(100),
917 "The invalid signature should match the corrupted index"
918 );
919 }
920 _ => panic!("Expected an error with invalid signatures"),
921 }
922 }
923
924 #[test]
925 fn test_batch_verify_same_message_single() {
926 let mut rng = test_rng();
927 let (sharing, shares) =
928 dkg::deal_anonymous::<MinSig, N3f1>(&mut rng, Default::default(), NZU32!(1));
929 let namespace = b"test";
930 let msg = b"hello";
931
932 let partials: Vec<_> = shares
933 .iter()
934 .map(|s| sign_message::<MinSig>(s, namespace, msg))
935 .collect();
936
937 batch_verify_same_message::<_, MinSig, _>(
938 &mut rng,
939 &sharing,
940 namespace,
941 msg,
942 &partials,
943 &Sequential,
944 )
945 .expect("signature should be valid");
946 }
947
948 #[test]
949 fn test_batch_verify_same_message_single_invalid() {
950 let mut rng = test_rng();
951 let (sharing, mut shares) =
952 dkg::deal_anonymous::<MinSig, N3f1>(&mut rng, Default::default(), NZU32!(1));
953 let namespace = b"test";
954 let msg = b"hello";
955
956 shares[0].private = Private::random(&mut rng);
957
958 let partials: Vec<_> = shares
959 .iter()
960 .map(|s| sign_message::<MinSig>(s, namespace, msg))
961 .collect();
962
963 let result = batch_verify_same_message::<_, MinSig, _>(
964 &mut rng,
965 &sharing,
966 namespace,
967 msg,
968 &partials,
969 &Sequential,
970 );
971 match result {
972 Err(invalid_sigs) => {
973 assert_eq!(invalid_sigs.len(), 1);
974 assert_eq!(invalid_sigs[0].index, Participant::new(0));
975 }
976 _ => panic!("Expected an error with invalid signatures"),
977 }
978 }
979
980 #[test]
981 fn test_batch_verify_same_message_last_invalid() {
982 let mut rng = test_rng();
983 let n = 5;
984 let (sharing, mut shares) =
985 dkg::deal_anonymous::<MinSig, N3f1>(&mut rng, Default::default(), NZU32!(n));
986 let namespace = b"test";
987 let msg = b"hello";
988
989 let corrupted_index = n - 1;
990 shares[corrupted_index as usize].private = Private::random(&mut rng);
991
992 let partials: Vec<_> = shares
993 .iter()
994 .map(|s| sign_message::<MinSig>(s, namespace, msg))
995 .collect();
996
997 let result = batch_verify_same_message::<_, MinSig, _>(
998 &mut rng,
999 &sharing,
1000 namespace,
1001 msg,
1002 &partials,
1003 &Sequential,
1004 );
1005 match result {
1006 Err(invalid_sigs) => {
1007 assert_eq!(invalid_sigs.len(), 1);
1008 assert_eq!(invalid_sigs[0].index, Participant::new(corrupted_index));
1009 }
1010 _ => panic!("Expected an error with invalid signatures"),
1011 }
1012 }
1013
1014 fn threshold_derive_missing_partials<V: Variant>() {
1015 fn lagrange_coeff(
1016 scalars: &[Scalar],
1017 eval_x: Participant,
1018 i_x: Participant,
1019 x_coords: &[Participant],
1020 ) -> Scalar {
1021 let mut num = Scalar::one();
1022 let mut den = Scalar::one();
1023
1024 let eval_x = scalars[usize::from(eval_x)].clone();
1025 let xi = scalars[usize::from(i_x)].clone();
1026
1027 for &j_x in x_coords {
1028 if i_x == j_x {
1029 continue;
1030 }
1031
1032 let xj = scalars[usize::from(j_x)].clone();
1033
1034 let mut term = eval_x.clone();
1035 term -= &xj;
1036 num *= &term;
1037
1038 let mut diff = xi.clone();
1039 diff -= &xj;
1040 den *= &diff;
1041 }
1042
1043 num *= &den.inv();
1044 num
1045 }
1046
1047 let mut rng = test_rng();
1048 let (n, t) = (NZU32!(5), N3f1::quorum(5));
1049 let (public, shares) = dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), n);
1050 let scalars = public.mode().all_scalars(n).collect::<Vec<_>>();
1051
1052 let namespace = b"test";
1053 let msg = b"hello";
1054 let all_partials: Vec<_> = shares
1055 .iter()
1056 .map(|s| sign_message::<V>(s, namespace, msg))
1057 .collect();
1058
1059 let recovery_partials: Vec<_> = all_partials.iter().take(t as usize).collect();
1060 let recovery_indices: Vec<Participant> =
1061 recovery_partials.iter().map(|p| p.index).collect();
1062
1063 for target in &shares {
1064 let target = target.index;
1065
1066 let weights: Vec<Scalar> = recovery_indices
1067 .iter()
1068 .map(|&recovery_index| {
1069 lagrange_coeff(&scalars, target, recovery_index, &recovery_indices)
1070 })
1071 .collect();
1072
1073 let points: Vec<_> = recovery_partials.iter().map(|p| p.value).collect();
1074 let derived =
1075 <<V as Variant>::Signature as Space<Scalar>>::msm(&points, &weights, &Sequential);
1076 let derived = PartialSignature {
1077 index: target,
1078 value: derived,
1079 };
1080
1081 verify_message::<V>(&public, namespace, msg, &derived)
1082 .expect("derived signature should be valid");
1083
1084 let original = all_partials.iter().find(|p| p.index == target).unwrap();
1085 assert_eq!(derived.value, original.value);
1086 }
1087 }
1088
1089 #[test]
1090 fn test_threshold_derive_missing_partials() {
1091 threshold_derive_missing_partials::<MinPk>();
1092 threshold_derive_missing_partials::<MinSig>();
1093 }
1094
1095 fn batch_verify_same_message_rejects_malleability<V: Variant>() {
1096 let mut rng = test_rng();
1097 let n = 5;
1098 let (sharing, shares) =
1099 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(n));
1100 let namespace = b"test";
1101 let msg = b"message";
1102
1103 let partial1 = sign_message::<V>(&shares[0], namespace, msg);
1104 let partial2 = sign_message::<V>(&shares[1], namespace, msg);
1105
1106 verify_message::<V>(&sharing, namespace, msg, &partial1).expect("partial1 should be valid");
1107 verify_message::<V>(&sharing, namespace, msg, &partial2).expect("partial2 should be valid");
1108
1109 let random_scalar = Scalar::random(&mut rng);
1110 let delta = V::Signature::generator() * &random_scalar;
1111 let forged_partial1 = PartialSignature {
1112 index: partial1.index,
1113 value: partial1.value - &delta,
1114 };
1115 let forged_partial2 = PartialSignature {
1116 index: partial2.index,
1117 value: partial2.value + &delta,
1118 };
1119
1120 assert!(
1121 verify_message::<V>(&sharing, namespace, msg, &forged_partial1).is_err(),
1122 "forged partial1 should be invalid individually"
1123 );
1124 assert!(
1125 verify_message::<V>(&sharing, namespace, msg, &forged_partial2).is_err(),
1126 "forged partial2 should be invalid individually"
1127 );
1128
1129 let forged_sum = forged_partial1.value + &forged_partial2.value;
1130 let valid_sum = partial1.value + &partial2.value;
1131 assert_eq!(
1132 forged_sum, valid_sum,
1133 "signature value sums should be equal"
1134 );
1135
1136 let pk1 = sharing.partial_public(partial1.index).unwrap();
1137 let pk2 = sharing.partial_public(partial2.index).unwrap();
1138 let pk_sum = pk1 + &pk2;
1139 let hm = hash_with_namespace::<V>(V::MESSAGE, namespace, msg);
1140 V::verify(&pk_sum, &hm, &forged_sum)
1141 .expect("vulnerable naive verification accepts forged aggregate");
1142
1143 let forged_partials = [forged_partial1, forged_partial2];
1144 let result = batch_verify_same_message::<_, V, _>(
1145 &mut rng,
1146 &sharing,
1147 namespace,
1148 msg,
1149 &forged_partials,
1150 &Sequential,
1151 );
1152 assert!(
1153 result.is_err(),
1154 "secure function should reject forged partial signatures"
1155 );
1156
1157 let valid_partials = [partial1, partial2];
1158 batch_verify_same_message::<_, V, _>(
1159 &mut rng,
1160 &sharing,
1161 namespace,
1162 msg,
1163 &valid_partials,
1164 &Sequential,
1165 )
1166 .expect("secure function should accept valid partial signatures");
1167 }
1168
1169 #[test]
1170 fn test_batch_verify_same_message_rejects_malleability() {
1171 batch_verify_same_message_rejects_malleability::<MinPk>();
1172 batch_verify_same_message_rejects_malleability::<MinSig>();
1173 }
1174
1175 fn batch_verify_same_signer_rejects_malleability<V: Variant>() {
1176 let mut rng = test_rng();
1177 let n = 5;
1178 let (sharing, shares) =
1179 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(n));
1180 let namespace: &[u8] = b"test";
1181 let msg1: &[u8] = b"message 1";
1182 let msg2: &[u8] = b"message 2";
1183
1184 let signer = &shares[0];
1185 let partial1 = sign_message::<V>(signer, namespace, msg1);
1186 let partial2 = sign_message::<V>(signer, namespace, msg2);
1187
1188 verify_message::<V>(&sharing, namespace, msg1, &partial1)
1189 .expect("partial1 should be valid");
1190 verify_message::<V>(&sharing, namespace, msg2, &partial2)
1191 .expect("partial2 should be valid");
1192
1193 let random_scalar = Scalar::random(&mut rng);
1194 let delta = V::Signature::generator() * &random_scalar;
1195 let forged_partial1 = PartialSignature {
1196 index: partial1.index,
1197 value: partial1.value - &delta,
1198 };
1199 let forged_partial2 = PartialSignature {
1200 index: partial2.index,
1201 value: partial2.value + &delta,
1202 };
1203
1204 assert!(
1205 verify_message::<V>(&sharing, namespace, msg1, &forged_partial1).is_err(),
1206 "forged partial1 should be invalid individually"
1207 );
1208 assert!(
1209 verify_message::<V>(&sharing, namespace, msg2, &forged_partial2).is_err(),
1210 "forged partial2 should be invalid individually"
1211 );
1212
1213 let forged_sum = forged_partial1.value + &forged_partial2.value;
1214 let valid_sum = partial1.value + &partial2.value;
1215 assert_eq!(
1216 forged_sum, valid_sum,
1217 "signature value sums should be equal"
1218 );
1219
1220 let pk = sharing.partial_public(signer.index).unwrap();
1221 let hm1 = hash_with_namespace::<V>(V::MESSAGE, namespace, msg1);
1222 let hm2 = hash_with_namespace::<V>(V::MESSAGE, namespace, msg2);
1223 let hm_sum = hm1 + &hm2;
1224 V::verify(&pk, &hm_sum, &forged_sum)
1225 .expect("vulnerable naive verification accepts forged aggregate");
1226
1227 let forged_entries = vec![
1228 (namespace, msg1, forged_partial1),
1229 (namespace, msg2, forged_partial2),
1230 ];
1231 let result = batch_verify_same_signer::<_, V, _>(
1232 &mut rng,
1233 &sharing,
1234 signer.index,
1235 &forged_entries,
1236 &Sequential,
1237 );
1238 assert!(
1239 result.is_err(),
1240 "secure function should reject forged partial signatures"
1241 );
1242
1243 let valid_entries = vec![(namespace, msg1, partial1), (namespace, msg2, partial2)];
1244 batch_verify_same_signer::<_, V, _>(
1245 &mut rng,
1246 &sharing,
1247 signer.index,
1248 &valid_entries,
1249 &Sequential,
1250 )
1251 .expect("secure function should accept valid partial signatures");
1252 }
1253
1254 #[test]
1255 fn test_batch_verify_same_signer_rejects_malleability() {
1256 batch_verify_same_signer_rejects_malleability::<MinPk>();
1257 batch_verify_same_signer_rejects_malleability::<MinSig>();
1258 }
1259}