1use crate::{
10 simplex::{
11 scheme::{seed_namespace, Namespace},
12 types::{Finalization, Notarization, Subject},
13 },
14 types::{Epoch, Participant, Round, View},
15 Epochable, Viewable,
16};
17use bytes::{Buf, BufMut};
18use commonware_codec::{Encode, EncodeSize, Error, FixedSize, Read, ReadExt, Write};
19use commonware_cryptography::{
20 bls12381::{
21 primitives::{
22 group::Share,
23 ops::{self, batch, threshold},
24 sharing::Sharing,
25 variant::{PartialSignature, Variant},
26 },
27 tle,
28 },
29 certificate::{self, Attestation, Subject as CertificateSubject, Verification},
30 Digest, PublicKey,
31};
32use commonware_parallel::Strategy;
33use commonware_utils::{ordered::Set, Faults};
34use rand::{rngs::StdRng, SeedableRng};
35use rand_core::CryptoRngCore;
36use std::{
37 collections::{BTreeSet, HashMap},
38 fmt::Debug,
39};
40
41#[derive(Clone, Debug)]
43enum Role<P: PublicKey, V: Variant> {
44 Signer {
45 participants: Set<P>,
47 polynomial: Sharing<V>,
49 share: Share,
51 namespace: Namespace,
53 },
54 Verifier {
55 participants: Set<P>,
57 polynomial: Sharing<V>,
59 namespace: Namespace,
61 },
62 CertificateVerifier {
63 identity: V::Public,
65 namespace: Namespace,
67 },
68}
69
70#[derive(Clone, Debug)]
76pub struct Scheme<P: PublicKey, V: Variant> {
77 role: Role<P, V>,
78}
79
80impl<P: PublicKey, V: Variant> Scheme<P, V> {
81 pub fn signer(
94 namespace: &[u8],
95 participants: Set<P>,
96 polynomial: Sharing<V>,
97 share: Share,
98 ) -> Option<Self> {
99 assert_eq!(
100 polynomial.total().get() as usize,
101 participants.len(),
102 "polynomial total must equal participant len"
103 );
104 polynomial.precompute_partial_publics();
105 let partial_public = polynomial
106 .partial_public(share.index)
107 .expect("share index must match participant indices");
108 if partial_public == share.public::<V>() {
109 Some(Self {
110 role: Role::Signer {
111 participants,
112 polynomial,
113 share,
114 namespace: Namespace::new(namespace),
115 },
116 })
117 } else {
118 None
119 }
120 }
121
122 pub fn verifier(namespace: &[u8], participants: Set<P>, polynomial: Sharing<V>) -> Self {
132 assert_eq!(
133 polynomial.total().get() as usize,
134 participants.len(),
135 "polynomial total must equal participant len"
136 );
137 polynomial.precompute_partial_publics();
138
139 Self {
140 role: Role::Verifier {
141 participants,
142 polynomial,
143 namespace: Namespace::new(namespace),
144 },
145 }
146 }
147
148 pub fn certificate_verifier(namespace: &[u8], identity: V::Public) -> Self {
156 Self {
157 role: Role::CertificateVerifier {
158 identity,
159 namespace: Namespace::new(namespace),
160 },
161 }
162 }
163
164 pub fn participants(&self) -> &Set<P> {
166 match &self.role {
167 Role::Signer { participants, .. } => participants,
168 Role::Verifier { participants, .. } => participants,
169 Role::CertificateVerifier { .. } => {
170 panic!("can only be called for signer and verifier")
171 }
172 }
173 }
174
175 pub fn identity(&self) -> &V::Public {
177 match &self.role {
178 Role::Signer { polynomial, .. } => polynomial.public(),
179 Role::Verifier { polynomial, .. } => polynomial.public(),
180 Role::CertificateVerifier { identity, .. } => identity,
181 }
182 }
183
184 pub const fn share(&self) -> Option<&Share> {
186 match &self.role {
187 Role::Signer { share, .. } => Some(share),
188 _ => None,
189 }
190 }
191
192 pub fn polynomial(&self) -> &Sharing<V> {
194 match &self.role {
195 Role::Signer { polynomial, .. } => polynomial,
196 Role::Verifier { polynomial, .. } => polynomial,
197 Role::CertificateVerifier { .. } => {
198 panic!("can only be called for signer and verifier")
199 }
200 }
201 }
202
203 const fn namespace(&self) -> &Namespace {
205 match &self.role {
206 Role::Signer { namespace, .. } => namespace,
207 Role::Verifier { namespace, .. } => namespace,
208 Role::CertificateVerifier { namespace, .. } => namespace,
209 }
210 }
211
212 pub fn encrypt<R: CryptoRngCore>(
218 &self,
219 rng: &mut R,
220 target: Round,
221 message: impl Into<tle::Block>,
222 ) -> tle::Ciphertext<V> {
223 let block = message.into();
224 let target_message = target.encode();
225 tle::encrypt(
226 rng,
227 *self.identity(),
228 (&self.namespace().seed, &target_message),
229 &block,
230 )
231 }
232}
233
234pub fn encrypt<R: CryptoRngCore, V: Variant>(
240 rng: &mut R,
241 identity: V::Public,
242 namespace: &[u8],
243 target: Round,
244 message: impl Into<tle::Block>,
245) -> tle::Ciphertext<V> {
246 let block = message.into();
247 let seed_ns = seed_namespace(namespace);
248 let target_message = target.encode();
249 tle::encrypt(rng, identity, (&seed_ns, &target_message), &block)
250}
251
252#[cfg(feature = "mocks")]
257pub fn fixture<V, R>(
258 rng: &mut R,
259 namespace: &[u8],
260 n: u32,
261) -> commonware_cryptography::certificate::mocks::Fixture<
262 Scheme<commonware_cryptography::ed25519::PublicKey, V>,
263>
264where
265 V: Variant,
266 R: rand::RngCore + rand::CryptoRng,
267{
268 commonware_cryptography::bls12381::certificate::threshold::mocks::fixture::<_, V, _>(
269 rng,
270 namespace,
271 n,
272 |namespace, participants, polynomial, share| {
273 Scheme::signer(namespace, participants, polynomial, share)
274 },
275 |namespace, participants, polynomial| Scheme::verifier(namespace, participants, polynomial),
276 )
277}
278
279#[derive(Clone, Debug, PartialEq, Eq, Hash)]
281pub struct Signature<V: Variant> {
282 pub vote_signature: V::Signature,
284 pub seed_signature: V::Signature,
286}
287
288impl<V: Variant> Write for Signature<V> {
289 fn write(&self, writer: &mut impl BufMut) {
290 self.vote_signature.write(writer);
291 self.seed_signature.write(writer);
292 }
293}
294
295impl<V: Variant> Read for Signature<V> {
296 type Cfg = ();
297
298 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
299 let vote_signature = V::Signature::read(reader)?;
300 let seed_signature = V::Signature::read(reader)?;
301
302 Ok(Self {
303 vote_signature,
304 seed_signature,
305 })
306 }
307}
308
309impl<V: Variant> FixedSize for Signature<V> {
310 const SIZE: usize = V::Signature::SIZE * 2;
311}
312
313#[cfg(feature = "arbitrary")]
314impl<V: Variant> arbitrary::Arbitrary<'_> for Signature<V>
315where
316 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
317{
318 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
319 Ok(Self {
320 vote_signature: u.arbitrary()?,
321 seed_signature: u.arbitrary()?,
322 })
323 }
324}
325
326#[derive(Clone, Debug, PartialEq, Hash, Eq)]
328pub struct Seed<V: Variant> {
329 pub round: Round,
331 pub signature: V::Signature,
333}
334
335impl<V: Variant> Seed<V> {
336 pub const fn new(round: Round, signature: V::Signature) -> Self {
338 Self { round, signature }
339 }
340
341 pub fn verify<P: PublicKey>(&self, scheme: &Scheme<P, V>) -> bool {
343 let seed_message = self.round.encode();
344
345 ops::verify_message::<V>(
346 scheme.identity(),
347 &scheme.namespace().seed,
348 &seed_message,
349 &self.signature,
350 )
351 .is_ok()
352 }
353
354 pub const fn round(&self) -> Round {
356 self.round
357 }
358
359 pub fn decrypt(&self, ciphertext: &tle::Ciphertext<V>) -> Option<tle::Block> {
364 decrypt(self, ciphertext)
365 }
366}
367
368pub fn decrypt<V: Variant>(seed: &Seed<V>, ciphertext: &tle::Ciphertext<V>) -> Option<tle::Block> {
374 tle::decrypt(&seed.signature, ciphertext)
375}
376
377impl<V: Variant> Epochable for Seed<V> {
378 fn epoch(&self) -> Epoch {
379 self.round.epoch()
380 }
381}
382
383impl<V: Variant> Viewable for Seed<V> {
384 fn view(&self) -> View {
385 self.round.view()
386 }
387}
388
389impl<V: Variant> Write for Seed<V> {
390 fn write(&self, writer: &mut impl BufMut) {
391 self.round.write(writer);
392 self.signature.write(writer);
393 }
394}
395
396impl<V: Variant> Read for Seed<V> {
397 type Cfg = ();
398
399 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
400 let round = Round::read(reader)?;
401 let signature = V::Signature::read(reader)?;
402
403 Ok(Self { round, signature })
404 }
405}
406
407impl<V: Variant> EncodeSize for Seed<V> {
408 fn encode_size(&self) -> usize {
409 self.round.encode_size() + self.signature.encode_size()
410 }
411}
412
413#[cfg(feature = "arbitrary")]
414impl<V: Variant> arbitrary::Arbitrary<'_> for Seed<V>
415where
416 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
417{
418 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
419 Ok(Self {
420 round: u.arbitrary()?,
421 signature: u.arbitrary()?,
422 })
423 }
424}
425
426pub trait Seedable<V: Variant> {
428 fn seed(&self) -> Seed<V>;
430}
431
432impl<P: PublicKey, V: Variant, D: Digest> Seedable<V> for Notarization<Scheme<P, V>, D> {
433 fn seed(&self) -> Seed<V> {
434 Seed::new(self.proposal.round, self.certificate.seed_signature)
435 }
436}
437
438impl<P: PublicKey, V: Variant, D: Digest> Seedable<V> for Finalization<Scheme<P, V>, D> {
439 fn seed(&self) -> Seed<V> {
440 Seed::new(self.proposal.round, self.certificate.seed_signature)
441 }
442}
443
444fn seed_message_from_subject<D: Digest>(subject: &Subject<'_, D>) -> bytes::Bytes {
448 match subject {
449 Subject::Notarize { proposal } | Subject::Finalize { proposal } => proposal.round.encode(),
450 Subject::Nullify { round } => round.encode(),
451 }
452}
453
454impl<P: PublicKey, V: Variant> certificate::Scheme for Scheme<P, V> {
455 type Subject<'a, D: Digest> = Subject<'a, D>;
456 type PublicKey = P;
457 type Signature = Signature<V>;
458 type Certificate = Signature<V>;
459
460 fn me(&self) -> Option<Participant> {
461 match &self.role {
462 Role::Signer { share, .. } => Some(share.index),
463 _ => None,
464 }
465 }
466
467 fn participants(&self) -> &Set<Self::PublicKey> {
468 self.participants()
469 }
470
471 fn sign<D: Digest>(&self, subject: Subject<'_, D>) -> Option<Attestation<Self>> {
472 let share = self.share()?;
473
474 let namespace = self.namespace();
475 let vote_namespace = subject.namespace(namespace);
476 let vote_message = subject.message();
477 let vote_signature =
478 threshold::sign_message::<V>(share, vote_namespace, &vote_message).value;
479
480 let seed_message = seed_message_from_subject(&subject);
481 let seed_signature =
482 threshold::sign_message::<V>(share, &namespace.seed, &seed_message).value;
483
484 let signature = Signature {
485 vote_signature,
486 seed_signature,
487 };
488
489 Some(Attestation {
490 signer: share.index,
491 signature,
492 })
493 }
494
495 fn verify_attestation<R, D>(
496 &self,
497 rng: &mut R,
498 subject: Subject<'_, D>,
499 attestation: &Attestation<Self>,
500 strategy: &impl Strategy,
501 ) -> bool
502 where
503 R: CryptoRngCore,
504 D: Digest,
505 {
506 let Ok(evaluated) = self.polynomial().partial_public(attestation.signer) else {
507 return false;
508 };
509
510 let namespace = self.namespace();
511 let vote_namespace = subject.namespace(namespace);
512 let vote_message = subject.message();
513 let seed_message = seed_message_from_subject(&subject);
514
515 let entries = &[
516 (
517 vote_namespace,
518 vote_message.as_ref(),
519 attestation.signature.vote_signature,
520 ),
521 (
522 &namespace.seed,
523 seed_message.as_ref(),
524 attestation.signature.seed_signature,
525 ),
526 ];
527 batch::verify_same_signer::<_, V, _>(rng, &evaluated, entries, strategy).is_ok()
528 }
529
530 fn verify_attestations<R, D, I>(
531 &self,
532 rng: &mut R,
533 subject: Subject<'_, D>,
534 attestations: I,
535 strategy: &impl Strategy,
536 ) -> Verification<Self>
537 where
538 R: CryptoRngCore,
539 D: Digest,
540 I: IntoIterator<Item = Attestation<Self>>,
541 {
542 let namespace = self.namespace();
543 let (vote_partials, seed_partials): (Vec<_>, Vec<_>) = attestations
544 .into_iter()
545 .map(|attestation| {
546 (
547 PartialSignature::<V> {
548 index: attestation.signer,
549 value: attestation.signature.vote_signature,
550 },
551 PartialSignature::<V> {
552 index: attestation.signer,
553 value: attestation.signature.seed_signature,
554 },
555 )
556 })
557 .unzip();
558
559 let polynomial = self.polynomial();
560 let vote_namespace = subject.namespace(namespace);
561 let vote_message = subject.message();
562 let seed_message = seed_message_from_subject(&subject);
563
564 let mut vote_rng_seed = [0u8; 32];
566 let mut seed_rng_seed = [0u8; 32];
567 rng.fill_bytes(&mut vote_rng_seed);
568 rng.fill_bytes(&mut seed_rng_seed);
569
570 let (vote_invalid, seed_invalid) = strategy.join(
572 || {
573 let mut vote_rng = StdRng::from_seed(vote_rng_seed);
574 match threshold::batch_verify_same_message::<_, V, _>(
575 &mut vote_rng,
576 polynomial,
577 vote_namespace,
578 &vote_message,
579 vote_partials.iter(),
580 strategy,
581 ) {
582 Ok(()) => BTreeSet::new(),
583 Err(errs) => errs.into_iter().map(|p| p.index).collect(),
584 }
585 },
586 || {
587 let mut seed_rng = StdRng::from_seed(seed_rng_seed);
588 match threshold::batch_verify_same_message::<_, V, _>(
589 &mut seed_rng,
590 polynomial,
591 &namespace.seed,
592 &seed_message,
593 seed_partials.iter(),
594 strategy,
595 ) {
596 Ok(()) => BTreeSet::new(),
597 Err(errs) => errs.into_iter().map(|p| p.index).collect(),
598 }
599 },
600 );
601 let invalid: BTreeSet<_> = vote_invalid.union(&seed_invalid).copied().collect();
603
604 let verified = vote_partials
605 .into_iter()
606 .zip(seed_partials)
607 .map(|(vote, seed)| Attestation {
608 signer: vote.index,
609 signature: Signature {
610 vote_signature: vote.value,
611 seed_signature: seed.value,
612 },
613 })
614 .filter(|attestation| !invalid.contains(&attestation.signer))
615 .collect();
616
617 Verification::new(verified, invalid.into_iter().collect())
618 }
619
620 fn assemble<I, M>(&self, attestations: I, strategy: &impl Strategy) -> Option<Self::Certificate>
621 where
622 I: IntoIterator<Item = Attestation<Self>>,
623 M: Faults,
624 {
625 let (vote_partials, seed_partials): (Vec<_>, Vec<_>) = attestations
626 .into_iter()
627 .map(|attestation| {
628 (
629 PartialSignature::<V> {
630 index: attestation.signer,
631 value: attestation.signature.vote_signature,
632 },
633 PartialSignature::<V> {
634 index: attestation.signer,
635 value: attestation.signature.seed_signature,
636 },
637 )
638 })
639 .unzip();
640
641 let quorum = self.polynomial();
642 if vote_partials.len() < quorum.required::<M>() as usize {
643 return None;
644 }
645
646 let (vote_signature, seed_signature) = threshold::recover_pair::<V, _, M>(
647 quorum,
648 vote_partials.iter(),
649 seed_partials.iter(),
650 strategy,
651 )
652 .ok()?;
653
654 Some(Signature {
655 vote_signature,
656 seed_signature,
657 })
658 }
659
660 fn verify_certificate<R, D, M>(
661 &self,
662 rng: &mut R,
663 subject: Subject<'_, D>,
664 certificate: &Self::Certificate,
665 strategy: &impl Strategy,
666 ) -> bool
667 where
668 R: CryptoRngCore,
669 D: Digest,
670 M: Faults,
671 {
672 let identity = self.identity();
673 let namespace = self.namespace();
674
675 let vote_namespace = subject.namespace(namespace);
676 let vote_message = subject.message();
677 let seed_message = seed_message_from_subject(&subject);
678
679 let entries = &[
680 (
681 vote_namespace,
682 vote_message.as_ref(),
683 certificate.vote_signature,
684 ),
685 (
686 &namespace.seed,
687 seed_message.as_ref(),
688 certificate.seed_signature,
689 ),
690 ];
691 batch::verify_same_signer::<_, V, _>(rng, identity, entries, strategy).is_ok()
692 }
693
694 fn verify_certificates<'a, R, D, I, M>(
695 &self,
696 rng: &mut R,
697 certificates: I,
698 strategy: &impl Strategy,
699 ) -> bool
700 where
701 R: CryptoRngCore,
702 D: Digest,
703 I: Iterator<Item = (Subject<'a, D>, &'a Self::Certificate)>,
704 M: Faults,
705 {
706 let identity = self.identity();
707 let namespace = self.namespace();
708
709 let mut seeds = HashMap::new();
710 let mut entries: Vec<_> = Vec::new();
711
712 for (context, certificate) in certificates {
713 let vote_namespace = context.namespace(namespace);
715 let vote_message = context.message();
716 entries.push((vote_namespace, vote_message, certificate.vote_signature));
717
718 if let Some(previous) = seeds.get(&context.view()) {
723 if *previous != certificate.seed_signature {
724 return false;
725 }
726 } else {
727 let seed_message = seed_message_from_subject(&context);
728 entries.push((&namespace.seed, seed_message, certificate.seed_signature));
729 seeds.insert(context.view(), certificate.seed_signature);
730 }
731 }
732
733 let entries_refs: Vec<_> = entries
736 .iter()
737 .map(|(ns, msg, sig)| (*ns, msg.as_ref(), *sig))
738 .collect();
739 batch::verify_same_signer::<_, V, _>(rng, identity, &entries_refs, strategy).is_ok()
740 }
741
742 fn is_attributable() -> bool {
743 false
744 }
745
746 fn is_batchable() -> bool {
747 true
748 }
749
750 fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg {}
751
752 fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg {}
753}
754
755#[cfg(test)]
756mod tests {
757 use super::*;
758 use crate::{
759 simplex::{
760 scheme::{bls12381_threshold, notarize_namespace, seed_namespace},
761 types::{Finalization, Finalize, Notarization, Notarize, Proposal, Subject},
762 },
763 types::{Round, View},
764 };
765 use commonware_codec::{Decode, Encode};
766 use commonware_cryptography::{
767 bls12381::{
768 dkg::{self, deal_anonymous},
769 primitives::{
770 group::Scalar,
771 ops::threshold,
772 variant::{MinPk, MinSig, Variant},
773 },
774 },
775 certificate::{mocks::Fixture, Scheme as _},
776 ed25519,
777 ed25519::certificate::mocks::participants as ed25519_participants,
778 sha256::Digest as Sha256Digest,
779 Hasher, Sha256,
780 };
781 use commonware_math::algebra::{CryptoGroup, Random};
782 use commonware_parallel::Sequential;
783 use commonware_utils::{test_rng, Faults, N3f1, NZU32};
784 use rand::{rngs::StdRng, SeedableRng};
785
786 const NAMESPACE: &[u8] = b"bls-threshold-signing-scheme";
787
788 type Scheme<V> = super::Scheme<ed25519::PublicKey, V>;
789 type Signature<V> = super::Signature<V>;
790
791 fn setup_signers<V: Variant>(n: u32, seed: u64) -> (Vec<Scheme<V>>, Scheme<V>) {
792 let mut rng = StdRng::seed_from_u64(seed);
793 let Fixture {
794 schemes, verifier, ..
795 } = bls12381_threshold::fixture::<V, _>(&mut rng, NAMESPACE, n);
796
797 (schemes, verifier)
798 }
799
800 fn sample_proposal(epoch: Epoch, view: View, tag: u8) -> Proposal<Sha256Digest> {
801 Proposal::new(
802 Round::new(epoch, view),
803 view.previous().unwrap(),
804 Sha256::hash(&[tag]),
805 )
806 }
807
808 fn signer_shares_must_match_participant_indices<V: Variant>() {
809 let mut rng = test_rng();
810 let participants = ed25519_participants(&mut rng, 4);
811 let (polynomial, mut shares) =
812 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(4));
813 shares[0].index = Participant::new(999);
814 Scheme::<V>::signer(
815 NAMESPACE,
816 participants.keys().clone(),
817 polynomial,
818 shares[0].clone(),
819 );
820 }
821
822 #[test]
823 #[should_panic(expected = "share index must match participant indices")]
824 fn test_signer_shares_must_match_participant_indices_min_pk() {
825 signer_shares_must_match_participant_indices::<MinPk>();
826 }
827
828 #[test]
829 #[should_panic(expected = "share index must match participant indices")]
830 fn test_signer_shares_must_match_participant_indices_min_sig() {
831 signer_shares_must_match_participant_indices::<MinSig>();
832 }
833 fn scheme_polynomial_threshold_must_equal_quorum<V: Variant>() {
834 let mut rng = test_rng();
835 let participants = ed25519_participants(&mut rng, 5);
836 let (polynomial, shares) =
837 deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(4));
838 Scheme::<V>::signer(
839 NAMESPACE,
840 participants.keys().clone(),
841 polynomial,
842 shares[0].clone(),
843 );
844 }
845
846 #[test]
847 #[should_panic(expected = "polynomial total must equal participant len")]
848 fn test_scheme_polynomial_threshold_must_equal_quorum_min_pk() {
849 scheme_polynomial_threshold_must_equal_quorum::<MinPk>();
850 }
851
852 #[test]
853 #[should_panic(expected = "polynomial total must equal participant len")]
854 fn test_scheme_polynomial_threshold_must_equal_quorum_min_sig() {
855 scheme_polynomial_threshold_must_equal_quorum::<MinSig>();
856 }
857
858 fn verifier_polynomial_threshold_must_equal_quorum<V: Variant>() {
859 let mut rng = test_rng();
860 let participants = ed25519_participants(&mut rng, 5);
861 let (polynomial, _) = deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(4));
862 Scheme::<V>::verifier(NAMESPACE, participants.keys().clone(), polynomial);
863 }
864
865 #[test]
866 #[should_panic(expected = "polynomial total must equal participant len")]
867 fn test_verifier_polynomial_threshold_must_equal_quorum_min_pk() {
868 verifier_polynomial_threshold_must_equal_quorum::<MinPk>();
869 }
870
871 #[test]
872 #[should_panic(expected = "polynomial total must equal participant len")]
873 fn test_verifier_polynomial_threshold_must_equal_quorum_min_sig() {
874 verifier_polynomial_threshold_must_equal_quorum::<MinSig>();
875 }
876
877 #[test]
878 fn test_is_not_attributable() {
879 assert!(!Scheme::<MinPk>::is_attributable());
880 assert!(!Scheme::<MinSig>::is_attributable());
881 }
882
883 #[test]
884 fn test_is_batchable() {
885 assert!(Scheme::<MinPk>::is_batchable());
886 assert!(Scheme::<MinSig>::is_batchable());
887 }
888
889 fn sign_vote_roundtrip_for_each_context<V: Variant>() {
890 let (schemes, _) = setup_signers::<V>(4, 7);
891 let scheme = &schemes[0];
892 let mut rng = test_rng();
893
894 let proposal = sample_proposal(Epoch::new(0), View::new(2), 1);
895 let notarize_vote = scheme
896 .sign(Subject::Notarize {
897 proposal: &proposal,
898 })
899 .unwrap();
900 assert!(scheme.verify_attestation::<_, Sha256Digest>(
901 &mut rng,
902 Subject::Notarize {
903 proposal: &proposal,
904 },
905 ¬arize_vote,
906 &Sequential,
907 ));
908
909 let nullify_vote = scheme
910 .sign::<Sha256Digest>(Subject::Nullify {
911 round: proposal.round,
912 })
913 .unwrap();
914 assert!(scheme.verify_attestation::<_, Sha256Digest>(
915 &mut rng,
916 Subject::Nullify {
917 round: proposal.round,
918 },
919 &nullify_vote,
920 &Sequential,
921 ));
922
923 let finalize_vote = scheme
924 .sign(Subject::Finalize {
925 proposal: &proposal,
926 })
927 .unwrap();
928 assert!(scheme.verify_attestation::<_, Sha256Digest>(
929 &mut rng,
930 Subject::Finalize {
931 proposal: &proposal,
932 },
933 &finalize_vote,
934 &Sequential,
935 ));
936 }
937
938 #[test]
939 fn test_sign_vote_roundtrip_for_each_context() {
940 sign_vote_roundtrip_for_each_context::<MinPk>();
941 sign_vote_roundtrip_for_each_context::<MinSig>();
942 }
943
944 fn verifier_cannot_sign<V: Variant>() {
945 let (_, verifier) = setup_signers::<V>(4, 11);
946
947 let proposal = sample_proposal(Epoch::new(0), View::new(3), 2);
948 assert!(
949 verifier
950 .sign(Subject::Notarize {
951 proposal: &proposal,
952 })
953 .is_none(),
954 "verifier should not produce signatures"
955 );
956 }
957
958 #[test]
959 fn test_verifier_cannot_sign() {
960 verifier_cannot_sign::<MinPk>();
961 verifier_cannot_sign::<MinSig>();
962 }
963
964 fn verifier_accepts_votes<V: Variant>() {
965 let (schemes, verifier) = setup_signers::<V>(4, 11);
966 let proposal = sample_proposal(Epoch::new(0), View::new(3), 2);
967 let vote = schemes[1]
968 .sign(Subject::Notarize {
969 proposal: &proposal,
970 })
971 .unwrap();
972 assert!(verifier.verify_attestation::<_, Sha256Digest>(
973 &mut test_rng(),
974 Subject::Notarize {
975 proposal: &proposal,
976 },
977 &vote,
978 &Sequential,
979 ));
980 }
981
982 #[test]
983 fn test_verifier_accepts_votes() {
984 verifier_accepts_votes::<MinPk>();
985 verifier_accepts_votes::<MinSig>();
986 }
987
988 fn verify_votes_filters_bad_signers<V: Variant>() {
989 let mut rng = test_rng();
990 let (schemes, _) = setup_signers::<V>(5, 13);
991 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
992 let proposal = sample_proposal(Epoch::new(0), View::new(5), 3);
993
994 let mut votes: Vec<_> = schemes
995 .iter()
996 .take(quorum)
997 .map(|scheme| {
998 scheme
999 .sign(Subject::Notarize {
1000 proposal: &proposal,
1001 })
1002 .unwrap()
1003 })
1004 .collect();
1005
1006 let verification = schemes[0].verify_attestations(
1007 &mut rng,
1008 Subject::Notarize {
1009 proposal: &proposal,
1010 },
1011 votes.clone(),
1012 &Sequential,
1013 );
1014 assert!(verification.invalid.is_empty());
1015 assert_eq!(verification.verified.len(), quorum);
1016
1017 votes[0].signer = Participant::new(999);
1018 let verification = schemes[0].verify_attestations(
1019 &mut rng,
1020 Subject::Notarize {
1021 proposal: &proposal,
1022 },
1023 votes,
1024 &Sequential,
1025 );
1026 assert_eq!(verification.invalid, vec![Participant::new(999)]);
1027 assert_eq!(verification.verified.len(), quorum - 1);
1028 }
1029
1030 #[test]
1031 fn test_verify_votes_filters_bad_signers() {
1032 verify_votes_filters_bad_signers::<MinPk>();
1033 verify_votes_filters_bad_signers::<MinSig>();
1034 }
1035
1036 fn assemble_certificate_requires_quorum<V: Variant>() {
1037 let (schemes, _) = setup_signers::<V>(4, 17);
1038 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1039 let proposal = sample_proposal(Epoch::new(0), View::new(7), 4);
1040
1041 let votes: Vec<_> = schemes
1042 .iter()
1043 .take(quorum - 1)
1044 .map(|scheme| {
1045 scheme
1046 .sign(Subject::Notarize {
1047 proposal: &proposal,
1048 })
1049 .unwrap()
1050 })
1051 .collect();
1052
1053 assert!(schemes[0].assemble::<_, N3f1>(votes, &Sequential).is_none());
1054 }
1055
1056 #[test]
1057 fn test_assemble_certificate_requires_quorum() {
1058 assemble_certificate_requires_quorum::<MinPk>();
1059 assemble_certificate_requires_quorum::<MinSig>();
1060 }
1061
1062 fn verify_certificate<V: Variant>() {
1063 let (schemes, verifier) = setup_signers::<V>(4, 19);
1064 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1065 let proposal = sample_proposal(Epoch::new(0), View::new(9), 5);
1066
1067 let votes: Vec<_> = schemes
1068 .iter()
1069 .take(quorum)
1070 .map(|scheme| {
1071 scheme
1072 .sign(Subject::Finalize {
1073 proposal: &proposal,
1074 })
1075 .unwrap()
1076 })
1077 .collect();
1078
1079 let certificate = schemes[0]
1080 .assemble::<_, N3f1>(votes, &Sequential)
1081 .expect("assemble certificate");
1082
1083 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1084 &mut test_rng(),
1085 Subject::Finalize {
1086 proposal: &proposal,
1087 },
1088 &certificate,
1089 &Sequential,
1090 ));
1091 }
1092
1093 #[test]
1094 fn test_verify_certificate() {
1095 verify_certificate::<MinPk>();
1096 verify_certificate::<MinSig>();
1097 }
1098
1099 fn verify_certificate_detects_corruption<V: Variant>() {
1100 let mut rng = test_rng();
1101 let (schemes, verifier) = setup_signers::<V>(4, 23);
1102 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1103 let proposal = sample_proposal(Epoch::new(0), View::new(11), 6);
1104
1105 let votes: Vec<_> = schemes
1106 .iter()
1107 .take(quorum)
1108 .map(|scheme| {
1109 scheme
1110 .sign(Subject::Notarize {
1111 proposal: &proposal,
1112 })
1113 .unwrap()
1114 })
1115 .collect();
1116
1117 let certificate = schemes[0]
1118 .assemble::<_, N3f1>(votes, &Sequential)
1119 .expect("assemble certificate");
1120
1121 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1122 &mut rng,
1123 Subject::Notarize {
1124 proposal: &proposal,
1125 },
1126 &certificate,
1127 &Sequential,
1128 ));
1129
1130 let mut corrupted = certificate;
1131 corrupted.vote_signature = corrupted.seed_signature;
1132 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1133 &mut rng,
1134 Subject::Notarize {
1135 proposal: &proposal,
1136 },
1137 &corrupted,
1138 &Sequential,
1139 ));
1140 }
1141
1142 #[test]
1143 fn test_verify_certificate_detects_corruption() {
1144 verify_certificate_detects_corruption::<MinPk>();
1145 verify_certificate_detects_corruption::<MinSig>();
1146 }
1147
1148 fn certificate_codec_roundtrip<V: Variant>() {
1149 let (schemes, _) = setup_signers::<V>(5, 29);
1150 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1151 let proposal = sample_proposal(Epoch::new(0), View::new(13), 7);
1152
1153 let votes: Vec<_> = schemes
1154 .iter()
1155 .take(quorum)
1156 .map(|scheme| {
1157 scheme
1158 .sign(Subject::Notarize {
1159 proposal: &proposal,
1160 })
1161 .unwrap()
1162 })
1163 .collect();
1164
1165 let certificate = schemes[0]
1166 .assemble::<_, N3f1>(votes, &Sequential)
1167 .expect("assemble certificate");
1168
1169 let encoded = certificate.encode();
1170 let decoded = Signature::<V>::decode_cfg(encoded, &()).expect("decode certificate");
1171 assert_eq!(decoded, certificate);
1172 }
1173
1174 #[test]
1175 fn test_certificate_codec_roundtrip() {
1176 certificate_codec_roundtrip::<MinPk>();
1177 certificate_codec_roundtrip::<MinSig>();
1178 }
1179
1180 fn seed_codec_roundtrip<V: Variant>() {
1181 let (schemes, _) = setup_signers::<V>(4, 5);
1182 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1183 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1184
1185 let votes: Vec<_> = schemes
1186 .iter()
1187 .take(quorum)
1188 .map(|scheme| {
1189 scheme
1190 .sign(Subject::Finalize {
1191 proposal: &proposal,
1192 })
1193 .unwrap()
1194 })
1195 .collect();
1196
1197 let certificate = schemes[0]
1198 .assemble::<_, N3f1>(votes, &Sequential)
1199 .expect("assemble certificate");
1200
1201 let seed = Seed::new(proposal.round, certificate.seed_signature);
1202
1203 let encoded = seed.encode();
1204 let decoded = Seed::<V>::decode_cfg(encoded, &()).expect("decode seed");
1205 assert_eq!(decoded, seed);
1206 }
1207
1208 #[test]
1209 fn test_seed_codec_roundtrip() {
1210 seed_codec_roundtrip::<MinPk>();
1211 seed_codec_roundtrip::<MinSig>();
1212 }
1213
1214 fn seed_verify<V: Variant>() {
1215 let (schemes, _) = setup_signers::<V>(4, 5);
1216 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1217 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1218
1219 let votes: Vec<_> = schemes
1220 .iter()
1221 .take(quorum)
1222 .map(|scheme| {
1223 scheme
1224 .sign(Subject::Finalize {
1225 proposal: &proposal,
1226 })
1227 .unwrap()
1228 })
1229 .collect();
1230
1231 let certificate = schemes[0]
1232 .assemble::<_, N3f1>(votes, &Sequential)
1233 .expect("assemble certificate");
1234
1235 let seed = Seed::new(proposal.round, certificate.seed_signature);
1236
1237 assert!(seed.verify(&schemes[0]));
1238
1239 let invalid_seed = Seed::new(
1241 Round::new(proposal.epoch(), proposal.view().next()),
1242 certificate.seed_signature,
1243 );
1244
1245 assert!(!invalid_seed.verify(&schemes[0]));
1246 }
1247
1248 #[test]
1249 fn test_seed_verify() {
1250 seed_verify::<MinPk>();
1251 seed_verify::<MinSig>();
1252 }
1253
1254 fn seedable<V: Variant>() {
1255 let (schemes, _) = setup_signers::<V>(4, 5);
1256 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1257 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1258
1259 let notarizes: Vec<_> = schemes
1260 .iter()
1261 .take(quorum)
1262 .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
1263 .collect();
1264
1265 let notarization =
1266 Notarization::from_notarizes(&schemes[0], ¬arizes, &Sequential).unwrap();
1267
1268 let finalizes: Vec<_> = schemes
1269 .iter()
1270 .take(quorum)
1271 .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap())
1272 .collect();
1273
1274 let finalization =
1275 Finalization::from_finalizes(&schemes[0], &finalizes, &Sequential).unwrap();
1276
1277 assert_eq!(notarization.seed(), finalization.seed());
1278 assert!(notarization.seed().verify(&schemes[0]));
1279 }
1280
1281 #[test]
1282 fn test_seedable() {
1283 seedable::<MinPk>();
1284 seedable::<MinSig>();
1285 }
1286
1287 fn scheme_clone_and_verifier<V: Variant>() {
1288 let (schemes, verifier) = setup_signers::<V>(4, 31);
1289 let signer = schemes[0].clone();
1290 let proposal = sample_proposal(Epoch::new(0), View::new(21), 9);
1291
1292 assert!(
1293 signer
1294 .sign(Subject::Notarize {
1295 proposal: &proposal,
1296 })
1297 .is_some(),
1298 "signer should produce votes"
1299 );
1300
1301 assert!(
1302 verifier
1303 .sign(Subject::Notarize {
1304 proposal: &proposal,
1305 })
1306 .is_none(),
1307 "verifier should not produce votes"
1308 );
1309 }
1310
1311 #[test]
1312 fn test_scheme_clone_and_verifier() {
1313 scheme_clone_and_verifier::<MinPk>();
1314 scheme_clone_and_verifier::<MinSig>();
1315 }
1316
1317 fn certificate_verifier_accepts_certificates<V: Variant>() {
1318 let (schemes, _) = setup_signers::<V>(4, 37);
1319 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1320 let proposal = sample_proposal(Epoch::new(0), View::new(15), 8);
1321
1322 let votes: Vec<_> = schemes
1323 .iter()
1324 .take(quorum)
1325 .map(|scheme| {
1326 scheme
1327 .sign(Subject::Finalize {
1328 proposal: &proposal,
1329 })
1330 .unwrap()
1331 })
1332 .collect();
1333
1334 let certificate = schemes[0]
1335 .assemble::<_, N3f1>(votes, &Sequential)
1336 .expect("assemble certificate");
1337
1338 let certificate_verifier =
1339 Scheme::<V>::certificate_verifier(NAMESPACE, *schemes[0].identity());
1340 assert!(
1341 certificate_verifier
1342 .sign(Subject::Finalize {
1343 proposal: &proposal,
1344 })
1345 .is_none(),
1346 "certificate verifier should not produce votes"
1347 );
1348 assert!(
1349 certificate_verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1350 &mut test_rng(),
1351 Subject::Finalize {
1352 proposal: &proposal,
1353 },
1354 &certificate,
1355 &Sequential,
1356 )
1357 );
1358 }
1359
1360 #[test]
1361 fn test_certificate_verifier_accepts_certificates() {
1362 certificate_verifier_accepts_certificates::<MinPk>();
1363 certificate_verifier_accepts_certificates::<MinSig>();
1364 }
1365
1366 fn certificate_verifier_panics_on_vote<V: Variant>() {
1367 let (schemes, _) = setup_signers::<V>(4, 37);
1368 let certificate_verifier =
1369 Scheme::<V>::certificate_verifier(NAMESPACE, *schemes[0].identity());
1370 let proposal = sample_proposal(Epoch::new(0), View::new(15), 8);
1371 let vote = schemes[1]
1372 .sign(Subject::Finalize {
1373 proposal: &proposal,
1374 })
1375 .unwrap();
1376
1377 certificate_verifier.verify_attestation::<_, Sha256Digest>(
1378 &mut test_rng(),
1379 Subject::Finalize {
1380 proposal: &proposal,
1381 },
1382 &vote,
1383 &Sequential,
1384 );
1385 }
1386
1387 #[test]
1388 #[should_panic(expected = "can only be called for signer and verifier")]
1389 fn test_certificate_verifier_panics_on_vote_min_pk() {
1390 certificate_verifier_panics_on_vote::<MinPk>();
1391 }
1392
1393 #[test]
1394 #[should_panic(expected = "can only be called for signer and verifier")]
1395 fn test_certificate_verifier_panics_on_vote_min_sig() {
1396 certificate_verifier_panics_on_vote::<MinSig>();
1397 }
1398
1399 fn verify_certificate_returns_seed_randomness<V: Variant>() {
1400 let (schemes, _) = setup_signers::<V>(4, 43);
1401 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1402 let proposal = sample_proposal(Epoch::new(0), View::new(19), 10);
1403
1404 let votes: Vec<_> = schemes
1405 .iter()
1406 .take(quorum)
1407 .map(|scheme| {
1408 scheme
1409 .sign(Subject::Notarize {
1410 proposal: &proposal,
1411 })
1412 .unwrap()
1413 })
1414 .collect();
1415
1416 let certificate = schemes[0]
1417 .assemble::<_, N3f1>(votes, &Sequential)
1418 .expect("assemble certificate");
1419
1420 let seed = Seed::<V>::new(proposal.round, certificate.seed_signature);
1421 assert_eq!(seed.signature, certificate.seed_signature);
1422 }
1423
1424 #[test]
1425 fn test_verify_certificate_returns_seed_randomness() {
1426 verify_certificate_returns_seed_randomness::<MinPk>();
1427 verify_certificate_returns_seed_randomness::<MinSig>();
1428 }
1429
1430 fn certificate_decode_rejects_length_mismatch<V: Variant>() {
1431 let (schemes, _) = setup_signers::<V>(4, 47);
1432 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1433 let proposal = sample_proposal(Epoch::new(0), View::new(21), 11);
1434
1435 let votes: Vec<_> = schemes
1436 .iter()
1437 .take(quorum)
1438 .map(|scheme| {
1439 scheme
1440 .sign::<Sha256Digest>(Subject::Nullify {
1441 round: proposal.round,
1442 })
1443 .unwrap()
1444 })
1445 .collect();
1446
1447 let certificate = schemes[0]
1448 .assemble::<_, N3f1>(votes, &Sequential)
1449 .expect("assemble certificate");
1450
1451 let mut encoded = certificate.encode();
1452 let truncated = encoded.split_to(encoded.len() - 1);
1453 assert!(Signature::<V>::decode_cfg(truncated, &()).is_err());
1454 }
1455
1456 #[test]
1457 fn test_certificate_decode_rejects_length_mismatch() {
1458 certificate_decode_rejects_length_mismatch::<MinPk>();
1459 certificate_decode_rejects_length_mismatch::<MinSig>();
1460 }
1461
1462 fn sign_vote_partial_matches_share<V: Variant>() {
1463 let (schemes, _) = setup_signers::<V>(4, 53);
1464 let scheme = &schemes[0];
1465 let share = scheme.share().expect("has share");
1466
1467 let proposal = sample_proposal(Epoch::new(0), View::new(23), 12);
1468 let vote = scheme
1469 .sign(Subject::Notarize {
1470 proposal: &proposal,
1471 })
1472 .unwrap();
1473
1474 let notarize_namespace = notarize_namespace(NAMESPACE);
1475 let notarize_message = proposal.encode();
1476 let expected_message = threshold::sign_message::<V>(
1477 share,
1478 notarize_namespace.as_ref(),
1479 notarize_message.as_ref(),
1480 )
1481 .value;
1482
1483 let seed_namespace = seed_namespace(NAMESPACE);
1484 let seed_message = proposal.round.encode();
1485 let expected_seed =
1486 threshold::sign_message::<V>(share, seed_namespace.as_ref(), seed_message.as_ref())
1487 .value;
1488
1489 assert_eq!(vote.signer, share.index);
1490 assert_eq!(vote.signature.vote_signature, expected_message);
1491 assert_eq!(vote.signature.seed_signature, expected_seed);
1492 }
1493
1494 #[test]
1495 fn test_sign_vote_partial_matches_share() {
1496 sign_vote_partial_matches_share::<MinPk>();
1497 sign_vote_partial_matches_share::<MinSig>();
1498 }
1499
1500 fn verify_certificate_detects_seed_corruption<V: Variant>() {
1501 let mut rng = test_rng();
1502 let (schemes, verifier) = setup_signers::<V>(4, 59);
1503 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1504 let proposal = sample_proposal(Epoch::new(0), View::new(25), 13);
1505
1506 let votes: Vec<_> = schemes
1507 .iter()
1508 .take(quorum)
1509 .map(|scheme| {
1510 scheme
1511 .sign::<Sha256Digest>(Subject::Nullify {
1512 round: proposal.round,
1513 })
1514 .unwrap()
1515 })
1516 .collect();
1517
1518 let certificate = schemes[0]
1519 .assemble::<_, N3f1>(votes, &Sequential)
1520 .expect("assemble certificate");
1521
1522 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1523 &mut rng,
1524 Subject::Nullify {
1525 round: proposal.round,
1526 },
1527 &certificate,
1528 &Sequential,
1529 ));
1530
1531 let mut corrupted = certificate;
1532 corrupted.seed_signature = corrupted.vote_signature;
1533 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1534 &mut rng,
1535 Subject::Nullify {
1536 round: proposal.round,
1537 },
1538 &corrupted,
1539 &Sequential,
1540 ));
1541 }
1542
1543 #[test]
1544 fn test_verify_certificate_detects_seed_corruption() {
1545 verify_certificate_detects_seed_corruption::<MinPk>();
1546 verify_certificate_detects_seed_corruption::<MinSig>();
1547 }
1548
1549 fn encrypt_decrypt<V: Variant>() {
1550 let mut rng = test_rng();
1551 let (schemes, verifier) = setup_signers::<V>(4, 61);
1552 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1553
1554 let message = b"Secret message for future view10";
1556
1557 let target = Round::new(Epoch::new(333), View::new(10));
1559
1560 let ciphertext = schemes[0].encrypt(&mut rng, target, *message);
1562
1563 let ciphertext_verifier = verifier.encrypt(&mut rng, target, *message);
1565
1566 let proposal = sample_proposal(target.epoch(), target.view(), 14);
1568 let notarizes: Vec<_> = schemes
1569 .iter()
1570 .take(quorum)
1571 .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
1572 .collect();
1573
1574 let notarization =
1575 Notarization::from_notarizes(&schemes[0], ¬arizes, &Sequential).unwrap();
1576
1577 let seed = notarization.seed();
1579 let decrypted = seed.decrypt(&ciphertext).unwrap();
1580 assert_eq!(message, decrypted.as_ref());
1581
1582 let decrypted_verifier = seed.decrypt(&ciphertext_verifier).unwrap();
1583 assert_eq!(message, decrypted_verifier.as_ref());
1584 }
1585
1586 #[test]
1587 fn test_encrypt_decrypt() {
1588 encrypt_decrypt::<MinPk>();
1589 encrypt_decrypt::<MinSig>();
1590 }
1591
1592 fn verify_attestation_rejects_malleability<V: Variant>() {
1593 let mut rng = test_rng();
1594 let (schemes, _) = setup_signers::<V>(4, 67);
1595 let proposal = sample_proposal(Epoch::new(0), View::new(27), 14);
1596
1597 let attestation = schemes[0]
1598 .sign(Subject::Notarize {
1599 proposal: &proposal,
1600 })
1601 .unwrap();
1602
1603 assert!(schemes[0].verify_attestation::<_, Sha256Digest>(
1604 &mut rng,
1605 Subject::Notarize {
1606 proposal: &proposal,
1607 },
1608 &attestation,
1609 &Sequential,
1610 ));
1611
1612 let random_scalar = Scalar::random(&mut rng);
1613 let delta = V::Signature::generator() * &random_scalar;
1614 let forged_attestation: Attestation<Scheme<V>> = Attestation {
1615 signer: attestation.signer,
1616 signature: Signature {
1617 vote_signature: attestation.signature.vote_signature - &delta,
1618 seed_signature: attestation.signature.seed_signature + &delta,
1619 },
1620 };
1621
1622 let forged_sum = forged_attestation.signature.vote_signature
1623 + &forged_attestation.signature.seed_signature;
1624 let valid_sum =
1625 attestation.signature.vote_signature + &attestation.signature.seed_signature;
1626 assert_eq!(forged_sum, valid_sum, "signature sums should be equal");
1627
1628 assert!(
1629 !schemes[0].verify_attestation::<_, Sha256Digest>(
1630 &mut rng,
1631 Subject::Notarize {
1632 proposal: &proposal,
1633 },
1634 &forged_attestation,
1635 &Sequential,
1636 ),
1637 "forged attestation should be rejected"
1638 );
1639 }
1640
1641 #[test]
1642 fn test_verify_attestation_rejects_malleability() {
1643 verify_attestation_rejects_malleability::<MinPk>();
1644 verify_attestation_rejects_malleability::<MinSig>();
1645 }
1646
1647 fn verify_attestations_rejects_malleability<V: Variant>() {
1648 let mut rng = test_rng();
1649 let (schemes, _) = setup_signers::<V>(4, 71);
1650 let proposal = sample_proposal(Epoch::new(0), View::new(29), 15);
1651
1652 let attestation1 = schemes[0]
1653 .sign(Subject::Notarize {
1654 proposal: &proposal,
1655 })
1656 .unwrap();
1657 let attestation2 = schemes[1]
1658 .sign(Subject::Notarize {
1659 proposal: &proposal,
1660 })
1661 .unwrap();
1662
1663 let verification = schemes[0].verify_attestations(
1664 &mut rng,
1665 Subject::Notarize {
1666 proposal: &proposal,
1667 },
1668 vec![attestation1.clone(), attestation2.clone()],
1669 &Sequential,
1670 );
1671 assert!(verification.invalid.is_empty());
1672 assert_eq!(verification.verified.len(), 2);
1673
1674 let random_scalar = Scalar::random(&mut rng);
1675 let delta = V::Signature::generator() * &random_scalar;
1676 let forged_attestation1: Attestation<Scheme<V>> = Attestation {
1677 signer: attestation1.signer,
1678 signature: Signature {
1679 vote_signature: attestation1.signature.vote_signature - &delta,
1680 seed_signature: attestation1.signature.seed_signature,
1681 },
1682 };
1683 let forged_attestation2: Attestation<Scheme<V>> = Attestation {
1684 signer: attestation2.signer,
1685 signature: Signature {
1686 vote_signature: attestation2.signature.vote_signature + &delta,
1687 seed_signature: attestation2.signature.seed_signature,
1688 },
1689 };
1690
1691 let forged_vote_sum = forged_attestation1.signature.vote_signature
1692 + &forged_attestation2.signature.vote_signature;
1693 let valid_vote_sum =
1694 attestation1.signature.vote_signature + &attestation2.signature.vote_signature;
1695 assert_eq!(
1696 forged_vote_sum, valid_vote_sum,
1697 "vote signature sums should be equal"
1698 );
1699
1700 let verification = schemes[0].verify_attestations(
1701 &mut rng,
1702 Subject::Notarize {
1703 proposal: &proposal,
1704 },
1705 vec![forged_attestation1, forged_attestation2],
1706 &Sequential,
1707 );
1708 assert!(
1709 !verification.invalid.is_empty(),
1710 "forged attestations should be detected"
1711 );
1712 }
1713
1714 #[test]
1715 fn test_verify_attestations_rejects_malleability() {
1716 verify_attestations_rejects_malleability::<MinPk>();
1717 verify_attestations_rejects_malleability::<MinSig>();
1718 }
1719
1720 fn verify_certificate_rejects_malleability<V: Variant>() {
1721 let mut rng = test_rng();
1722 let (schemes, verifier) = setup_signers::<V>(4, 73);
1723 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1724 let proposal = sample_proposal(Epoch::new(0), View::new(31), 16);
1725
1726 let votes: Vec<_> = schemes
1727 .iter()
1728 .take(quorum)
1729 .map(|scheme| {
1730 scheme
1731 .sign(Subject::Notarize {
1732 proposal: &proposal,
1733 })
1734 .unwrap()
1735 })
1736 .collect();
1737
1738 let certificate = schemes[0]
1739 .assemble::<_, N3f1>(votes, &Sequential)
1740 .expect("assemble certificate");
1741
1742 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1743 &mut rng,
1744 Subject::Notarize {
1745 proposal: &proposal,
1746 },
1747 &certificate,
1748 &Sequential,
1749 ));
1750
1751 let random_scalar = Scalar::random(&mut rng);
1752 let delta = V::Signature::generator() * &random_scalar;
1753 let forged_certificate = Signature {
1754 vote_signature: certificate.vote_signature - &delta,
1755 seed_signature: certificate.seed_signature + &delta,
1756 };
1757
1758 let forged_sum = forged_certificate.vote_signature + &forged_certificate.seed_signature;
1759 let valid_sum = certificate.vote_signature + &certificate.seed_signature;
1760 assert_eq!(forged_sum, valid_sum, "signature sums should be equal");
1761
1762 assert!(
1763 !verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1764 &mut rng,
1765 Subject::Notarize {
1766 proposal: &proposal,
1767 },
1768 &forged_certificate,
1769 &Sequential,
1770 ),
1771 "forged certificate should be rejected"
1772 );
1773 }
1774
1775 #[test]
1776 fn test_verify_certificate_rejects_malleability() {
1777 verify_certificate_rejects_malleability::<MinPk>();
1778 verify_certificate_rejects_malleability::<MinSig>();
1779 }
1780
1781 fn verify_certificates_rejects_malleability<V: Variant>() {
1782 let mut rng = test_rng();
1783 let (schemes, verifier) = setup_signers::<V>(4, 79);
1784 let quorum = N3f1::quorum_from_slice(&schemes) as usize;
1785 let proposal1 = sample_proposal(Epoch::new(0), View::new(33), 17);
1786 let proposal2 = sample_proposal(Epoch::new(0), View::new(34), 18);
1787
1788 let votes1: Vec<_> = schemes
1789 .iter()
1790 .take(quorum)
1791 .map(|scheme| {
1792 scheme
1793 .sign(Subject::Notarize {
1794 proposal: &proposal1,
1795 })
1796 .unwrap()
1797 })
1798 .collect();
1799 let votes2: Vec<_> = schemes
1800 .iter()
1801 .take(quorum)
1802 .map(|scheme| {
1803 scheme
1804 .sign(Subject::Notarize {
1805 proposal: &proposal2,
1806 })
1807 .unwrap()
1808 })
1809 .collect();
1810
1811 let certificate1 = schemes[0]
1812 .assemble::<_, N3f1>(votes1, &Sequential)
1813 .expect("assemble certificate1");
1814 let certificate2 = schemes[0]
1815 .assemble::<_, N3f1>(votes2, &Sequential)
1816 .expect("assemble certificate2");
1817
1818 assert!(verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1819 &mut rng,
1820 [
1821 (
1822 Subject::Notarize {
1823 proposal: &proposal1,
1824 },
1825 &certificate1
1826 ),
1827 (
1828 Subject::Notarize {
1829 proposal: &proposal2,
1830 },
1831 &certificate2
1832 ),
1833 ]
1834 .into_iter(),
1835 &Sequential,
1836 ));
1837
1838 let random_scalar = Scalar::random(&mut rng);
1839 let delta = V::Signature::generator() * &random_scalar;
1840 let forged_certificate1 = Signature {
1841 vote_signature: certificate1.vote_signature - &delta,
1842 seed_signature: certificate1.seed_signature,
1843 };
1844 let forged_certificate2 = Signature {
1845 vote_signature: certificate2.vote_signature + &delta,
1846 seed_signature: certificate2.seed_signature,
1847 };
1848
1849 let forged_vote_sum =
1850 forged_certificate1.vote_signature + &forged_certificate2.vote_signature;
1851 let valid_vote_sum = certificate1.vote_signature + &certificate2.vote_signature;
1852 assert_eq!(
1853 forged_vote_sum, valid_vote_sum,
1854 "vote signature sums should be equal"
1855 );
1856
1857 assert!(
1858 !verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1859 &mut rng,
1860 [
1861 (
1862 Subject::Notarize {
1863 proposal: &proposal1,
1864 },
1865 &forged_certificate1
1866 ),
1867 (
1868 Subject::Notarize {
1869 proposal: &proposal2,
1870 },
1871 &forged_certificate2
1872 ),
1873 ]
1874 .into_iter(),
1875 &Sequential,
1876 ),
1877 "forged certificates should be rejected"
1878 );
1879 }
1880
1881 #[test]
1882 fn test_verify_certificates_rejects_malleability() {
1883 verify_certificates_rejects_malleability::<MinPk>();
1884 verify_certificates_rejects_malleability::<MinSig>();
1885 }
1886
1887 #[cfg(feature = "arbitrary")]
1888 mod conformance {
1889 use super::*;
1890 use commonware_codec::conformance::CodecConformance;
1891
1892 commonware_conformance::conformance_tests! {
1893 CodecConformance<Signature<MinSig>>,
1894 CodecConformance<Seed<MinSig>>,
1895 }
1896 }
1897}