1#[commonware_macros::stability(ALPHA)]
52use crate::simplex::scheme::seed_namespace;
53use crate::{
54 Epochable, Viewable,
55 simplex::{
56 scheme::Namespace,
57 types::{Finalization, Notarization, Subject},
58 },
59 types::{Epoch, Participant, Round, View},
60};
61use bytes::{Buf, BufMut};
62use commonware_codec::{
63 Encode, EncodeSize, Error, FixedSize, Read, ReadExt, Write, types::lazy::Lazy,
64};
65#[commonware_macros::stability(ALPHA)]
66use commonware_cryptography::bls12381::tle;
67use commonware_cryptography::{
68 Digest, PublicKey,
69 bls12381::primitives::{
70 group::Share,
71 ops::{self, batch, threshold},
72 sharing::Sharing,
73 variant::{PartialSignature, Variant},
74 },
75 certificate::{
76 self, AssemblyError, Attestation, Signers, Subject as CertificateSubject, Verification,
77 },
78};
79use commonware_macros::stability;
80use commonware_parallel::Strategy;
81use commonware_utils::{
82 N3f1,
83 iter::NonEmpty,
84 non_empty,
85 ordered::{Quorum, Set},
86};
87use rand::rngs::StdRng;
88use rand_core::{CryptoRng, SeedableRng};
89use std::{
90 collections::{BTreeSet, HashMap},
91 fmt::Debug,
92};
93
94#[derive(Clone, Debug)]
96enum Role<P: PublicKey, V: Variant> {
97 Signer {
98 participants: Set<P>,
100 polynomial: Sharing<V>,
102 share: Share,
104 namespace: Namespace,
106 },
107 Verifier {
108 participants: Set<P>,
110 polynomial: Sharing<V>,
112 namespace: Namespace,
114 },
115 CertificateVerifier {
116 identity: V::Public,
118 namespace: Namespace,
120 },
121}
122
123#[derive(Clone, Debug)]
132pub struct Scheme<P: PublicKey, V: Variant> {
133 role: Role<P, V>,
134}
135
136impl<P: PublicKey, V: Variant> Scheme<P, V> {
137 pub fn signer(
155 namespace: &[u8],
156 participants: Set<P>,
157 polynomial: Sharing<V>,
158 share: Share,
159 ) -> Option<Self> {
160 assert_eq!(
161 polynomial.total().get() as usize,
162 participants.len(),
163 "polynomial total must equal participant len"
164 );
165 assert_eq!(
166 polynomial.required(),
167 participants.quorum::<N3f1>(),
168 "polynomial threshold must equal quorum"
169 );
170 polynomial.precompute_partial_publics();
171 let partial_public = polynomial
172 .partial_public(share.index)
173 .expect("share index must match participant indices");
174 if partial_public == share.public::<V>() {
175 Some(Self {
176 role: Role::Signer {
177 participants,
178 polynomial,
179 share,
180 namespace: Namespace::new(namespace),
181 },
182 })
183 } else {
184 None
185 }
186 }
187
188 pub fn verifier(namespace: &[u8], participants: Set<P>, polynomial: Sharing<V>) -> Self {
203 assert_eq!(
204 polynomial.total().get() as usize,
205 participants.len(),
206 "polynomial total must equal participant len"
207 );
208 assert_eq!(
209 polynomial.required(),
210 participants.quorum::<N3f1>(),
211 "polynomial threshold must equal quorum"
212 );
213 polynomial.precompute_partial_publics();
214
215 Self {
216 role: Role::Verifier {
217 participants,
218 polynomial,
219 namespace: Namespace::new(namespace),
220 },
221 }
222 }
223
224 pub fn certificate_verifier(namespace: &[u8], identity: V::Public) -> Self {
232 Self {
233 role: Role::CertificateVerifier {
234 identity,
235 namespace: Namespace::new(namespace),
236 },
237 }
238 }
239
240 pub fn participants(&self) -> &Set<P> {
242 match &self.role {
243 Role::Signer { participants, .. } => participants,
244 Role::Verifier { participants, .. } => participants,
245 Role::CertificateVerifier { .. } => {
246 panic!("can only be called for signer and verifier")
247 }
248 }
249 }
250
251 pub fn identity(&self) -> &V::Public {
253 match &self.role {
254 Role::Signer { polynomial, .. } => polynomial.public(),
255 Role::Verifier { polynomial, .. } => polynomial.public(),
256 Role::CertificateVerifier { identity, .. } => identity,
257 }
258 }
259
260 pub const fn share(&self) -> Option<&Share> {
262 match &self.role {
263 Role::Signer { share, .. } => Some(share),
264 _ => None,
265 }
266 }
267
268 pub fn polynomial(&self) -> &Sharing<V> {
270 match &self.role {
271 Role::Signer { polynomial, .. } => polynomial,
272 Role::Verifier { polynomial, .. } => polynomial,
273 Role::CertificateVerifier { .. } => {
274 panic!("can only be called for signer and verifier")
275 }
276 }
277 }
278
279 const fn namespace(&self) -> &Namespace {
281 match &self.role {
282 Role::Signer { namespace, .. } => namespace,
283 Role::Verifier { namespace, .. } => namespace,
284 Role::CertificateVerifier { namespace, .. } => namespace,
285 }
286 }
287
288 #[stability(ALPHA)]
296 pub fn encrypt<R: CryptoRng>(
297 &self,
298 rng: &mut R,
299 target: Round,
300 message: impl Into<tle::Block>,
301 ) -> Result<tle::Ciphertext<V>, tle::Error> {
302 let block = message.into();
303 let target_message = target.encode();
304 tle::encrypt(
305 rng,
306 *self.identity(),
307 (&self.namespace().seed, &target_message),
308 &block,
309 )
310 }
311}
312
313#[stability(ALPHA)]
321pub fn encrypt<R: CryptoRng, V: Variant>(
322 rng: &mut R,
323 identity: V::Public,
324 namespace: &[u8],
325 target: Round,
326 message: impl Into<tle::Block>,
327) -> Result<tle::Ciphertext<V>, tle::Error> {
328 let block = message.into();
329 let seed_ns = seed_namespace(namespace);
330 let target_message = target.encode();
331 tle::encrypt(rng, identity, (&seed_ns, &target_message), &block)
332}
333
334#[cfg(feature = "mocks")]
339pub fn fixture<V, R>(
340 rng: &mut R,
341 namespace: &[u8],
342 n: u32,
343) -> commonware_cryptography::certificate::mocks::Fixture<
344 Scheme<commonware_cryptography::ed25519::PublicKey, V>,
345>
346where
347 V: Variant,
348 R: rand_core::CryptoRng,
349{
350 commonware_cryptography::bls12381::certificate::threshold::mocks::fixture::<_, V, _, N3f1>(
351 rng,
352 namespace,
353 n,
354 |namespace, participants, polynomial, share| {
355 Scheme::signer(namespace, participants, polynomial, share)
356 },
357 |namespace, participants, polynomial| Scheme::verifier(namespace, participants, polynomial),
358 )
359}
360
361#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
363pub struct Signature<V: Variant> {
364 pub vote_signature: V::Signature,
366 pub seed_signature: V::Signature,
368}
369
370impl<V: Variant> Write for Signature<V> {
371 fn write(&self, writer: &mut impl BufMut) {
372 self.vote_signature.write(writer);
373 self.seed_signature.write(writer);
374 }
375}
376
377impl<V: Variant> Read for Signature<V> {
378 type Cfg = ();
379
380 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
381 let vote_signature = V::Signature::read(reader)?;
382 let seed_signature = V::Signature::read(reader)?;
383
384 Ok(Self {
385 vote_signature,
386 seed_signature,
387 })
388 }
389}
390
391impl<V: Variant> FixedSize for Signature<V> {
392 const SIZE: usize = V::Signature::SIZE * 2;
393}
394
395#[cfg(feature = "arbitrary")]
396impl<V: Variant> arbitrary::Arbitrary<'_> for Signature<V>
397where
398 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
399{
400 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
401 Ok(Self {
402 vote_signature: u.arbitrary()?,
403 seed_signature: u.arbitrary()?,
404 })
405 }
406}
407
408#[derive(Clone, Debug, PartialEq, Eq, Hash)]
410pub struct Certificate<V: Variant> {
411 pub signature: Lazy<Signature<V>>,
413}
414
415impl<V: Variant> Certificate<V> {
416 pub fn get(&self) -> Option<&Signature<V>> {
420 self.signature.get()
421 }
422}
423
424impl<V: Variant> From<Signature<V>> for Certificate<V> {
425 fn from(signature: Signature<V>) -> Self {
426 Self {
427 signature: Lazy::from(signature),
428 }
429 }
430}
431
432impl<V: Variant> Write for Certificate<V> {
433 fn write(&self, writer: &mut impl BufMut) {
434 self.signature.write(writer);
435 }
436}
437
438impl<V: Variant> Read for Certificate<V> {
439 type Cfg = ();
440
441 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
442 let signature = Lazy::<Signature<V>>::read(reader)?;
443 Ok(Self { signature })
444 }
445}
446
447impl<V: Variant> FixedSize for Certificate<V> {
448 const SIZE: usize = Signature::<V>::SIZE;
449}
450
451#[cfg(feature = "arbitrary")]
452impl<V: Variant> arbitrary::Arbitrary<'_> for Certificate<V>
453where
454 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
455{
456 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
457 Ok(Self {
458 signature: Lazy::from(u.arbitrary::<Signature<V>>()?),
459 })
460 }
461}
462
463#[derive(Clone, Debug, PartialEq, Hash, Eq)]
465pub struct Seed<V: Variant> {
466 pub round: Round,
468 pub signature: V::Signature,
470}
471
472impl<V: Variant> Seed<V> {
473 pub const fn new(round: Round, signature: V::Signature) -> Self {
475 Self { round, signature }
476 }
477
478 pub fn verify<P: PublicKey>(&self, scheme: &Scheme<P, V>) -> bool {
480 let seed_message = self.round.encode();
481
482 ops::verify_message::<V>(
483 scheme.identity(),
484 &scheme.namespace().seed,
485 &seed_message,
486 &self.signature,
487 )
488 .is_ok()
489 }
490
491 pub const fn round(&self) -> Round {
493 self.round
494 }
495
496 #[stability(ALPHA)]
501 pub fn decrypt(&self, ciphertext: &tle::Ciphertext<V>) -> Option<tle::Block> {
502 decrypt(self, ciphertext)
503 }
504}
505
506#[stability(ALPHA)]
511pub fn decrypt<V: Variant>(seed: &Seed<V>, ciphertext: &tle::Ciphertext<V>) -> Option<tle::Block> {
512 tle::decrypt(&seed.signature, ciphertext)
513}
514
515impl<V: Variant> Epochable for Seed<V> {
516 fn epoch(&self) -> Epoch {
517 self.round.epoch()
518 }
519}
520
521impl<V: Variant> Viewable for Seed<V> {
522 fn view(&self) -> View {
523 self.round.view()
524 }
525}
526
527impl<V: Variant> Write for Seed<V> {
528 fn write(&self, writer: &mut impl BufMut) {
529 self.round.write(writer);
530 self.signature.write(writer);
531 }
532}
533
534impl<V: Variant> Read for Seed<V> {
535 type Cfg = ();
536
537 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
538 let round = Round::read(reader)?;
539 let signature = V::Signature::read(reader)?;
540
541 Ok(Self { round, signature })
542 }
543}
544
545impl<V: Variant> EncodeSize for Seed<V> {
546 fn encode_size(&self) -> usize {
547 self.round.encode_size() + self.signature.encode_size()
548 }
549}
550
551#[cfg(feature = "arbitrary")]
552impl<V: Variant> arbitrary::Arbitrary<'_> for Seed<V>
553where
554 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
555{
556 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
557 Ok(Self {
558 round: u.arbitrary()?,
559 signature: u.arbitrary()?,
560 })
561 }
562}
563
564pub trait Seedable<V: Variant> {
566 fn seed(&self) -> Seed<V>;
568}
569
570impl<P: PublicKey, V: Variant, D: Digest> Seedable<V> for Notarization<Scheme<P, V>, D> {
571 fn seed(&self) -> Seed<V> {
572 let cert = self
573 .certificate
574 .get()
575 .expect("verified certificate must decode");
576 Seed::new(self.proposal.round, cert.seed_signature)
577 }
578}
579
580impl<P: PublicKey, V: Variant, D: Digest> Seedable<V> for Finalization<Scheme<P, V>, D> {
581 fn seed(&self) -> Seed<V> {
582 let cert = self
583 .certificate
584 .get()
585 .expect("verified certificate must decode");
586 Seed::new(self.proposal.round, cert.seed_signature)
587 }
588}
589
590fn seed_message_from_subject<D: Digest>(subject: &Subject<'_, D>) -> bytes::Bytes {
594 match subject {
595 Subject::Notarize { proposal } | Subject::Finalize { proposal } => proposal.round.encode(),
596 Subject::Nullify { round } => round.encode(),
597 }
598}
599
600impl<P: PublicKey, V: Variant> certificate::Verifier for Scheme<P, V> {
601 type Subject<'a, D: Digest> = Subject<'a, D>;
602 type Faults = N3f1;
603 type PublicKey = P;
604 type Certificate = Certificate<V>;
605
606 fn verify_certificate<R, D>(
607 &self,
608 rng: &mut R,
609 subject: Subject<'_, D>,
610 certificate: &Self::Certificate,
611 strategy: &impl Strategy,
612 ) -> bool
613 where
614 R: CryptoRng,
615 D: Digest,
616 {
617 let Some(cert) = certificate.get() else {
618 return false;
619 };
620
621 let identity = self.identity();
622 let namespace = self.namespace();
623
624 let vote_namespace = subject.namespace(namespace);
625 let vote_message = subject.message();
626 let seed_message = seed_message_from_subject(&subject);
627
628 let entries = non_empty![
629 (vote_namespace, vote_message.as_ref(), cert.vote_signature),
630 (
631 namespace.seed.as_ref(),
632 seed_message.as_ref(),
633 cert.seed_signature,
634 ),
635 ];
636 batch::verify_same_signer::<_, V, _>(rng, identity, entries, strategy).is_ok()
637 }
638
639 fn verify_certificates<'a, R, D, I>(
640 &self,
641 rng: &mut R,
642 certificates: NonEmpty<I>,
643 strategy: &impl Strategy,
644 ) -> bool
645 where
646 R: CryptoRng,
647 D: Digest,
648 I: Iterator<Item = (Subject<'a, D>, &'a Self::Certificate)>,
649 {
650 let identity = self.identity();
651 let namespace = self.namespace();
652
653 let mut seeds = HashMap::new();
654 let mut entries: Vec<_> = Vec::new();
655
656 for (context, certificate) in certificates {
657 let Some(cert) = certificate.get() else {
658 return false;
659 };
660
661 let vote_namespace = context.namespace(namespace);
663 let vote_message = context.message();
664 entries.push((vote_namespace, vote_message, cert.vote_signature));
665
666 let seed_message = seed_message_from_subject(&context);
671 if let Some(previous) = seeds.get(&seed_message) {
672 if *previous != cert.seed_signature {
673 return false;
674 }
675 } else {
676 entries.push((&namespace.seed, seed_message.clone(), cert.seed_signature));
677 seeds.insert(seed_message, cert.seed_signature);
678 }
679 }
680
681 batch::verify_same_signer::<_, V, _>(
684 rng,
685 identity,
686 non_empty![@entries
687 .iter()
688 .map(|(ns, msg, sig)| (*ns, msg.as_ref(), *sig))],
689 strategy,
690 )
691 .is_ok()
692 }
693
694 fn is_batchable() -> bool {
695 true
696 }
697
698 fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg {}
699
700 fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg {}
701}
702
703impl<P: PublicKey, V: Variant> certificate::Scheme for Scheme<P, V> {
704 type Signature = Signature<V>;
705
706 fn me(&self) -> Option<Participant> {
707 match &self.role {
708 Role::Signer { share, .. } => Some(share.index),
709 _ => None,
710 }
711 }
712
713 fn participants(&self) -> &Set<Self::PublicKey> {
714 self.participants()
715 }
716
717 fn sign<D: Digest>(&self, subject: Subject<'_, D>) -> Option<Attestation<Self>> {
718 let share = self.share()?;
719
720 let namespace = self.namespace();
721 let vote_namespace = subject.namespace(namespace);
722 let vote_message = subject.message();
723 let vote_signature =
724 threshold::sign_message::<V>(share, vote_namespace, &vote_message).value;
725
726 let seed_message = seed_message_from_subject(&subject);
727 let seed_signature =
728 threshold::sign_message::<V>(share, &namespace.seed, &seed_message).value;
729
730 let signature = Signature {
731 vote_signature,
732 seed_signature,
733 };
734
735 Some(Attestation {
736 signer: share.index,
737 signature: signature.into(),
738 })
739 }
740
741 fn verify_attestation<R, D>(
742 &self,
743 rng: &mut R,
744 subject: Subject<'_, D>,
745 attestation: &Attestation<Self>,
746 strategy: &impl Strategy,
747 ) -> bool
748 where
749 R: CryptoRng,
750 D: Digest,
751 {
752 let Ok(evaluated) = self.polynomial().partial_public(attestation.signer) else {
753 return false;
754 };
755
756 let namespace = self.namespace();
757 let vote_namespace = subject.namespace(namespace);
758 let vote_message = subject.message();
759 let seed_message = seed_message_from_subject(&subject);
760
761 let Some(signature) = attestation.signature.get() else {
762 return false;
763 };
764
765 let entries = non_empty![
766 (
767 vote_namespace,
768 vote_message.as_ref(),
769 signature.vote_signature,
770 ),
771 (
772 namespace.seed.as_ref(),
773 seed_message.as_ref(),
774 signature.seed_signature,
775 ),
776 ];
777 batch::verify_same_signer::<_, V, _>(rng, &evaluated, entries, strategy).is_ok()
778 }
779
780 fn verify_attestations<R, D, I>(
781 &self,
782 rng: &mut R,
783 subject: Subject<'_, D>,
784 attestations: I,
785 strategy: &impl Strategy,
786 ) -> Verification<Self>
787 where
788 R: CryptoRng,
789 D: Digest,
790 I: IntoIterator<Item = Attestation<Self>>,
791 I::IntoIter: Send,
792 {
793 let namespace = self.namespace();
794 let (partials, failures) =
795 strategy.map_partition_collect_vec(attestations.into_iter(), |attestation| {
796 let index = attestation.signer;
797 let value = attestation.signature.get().map(|sig| {
798 (
799 PartialSignature::<V> {
800 index,
801 value: sig.vote_signature,
802 },
803 PartialSignature::<V> {
804 index,
805 value: sig.seed_signature,
806 },
807 )
808 });
809 (index, value)
810 });
811
812 let polynomial = self.polynomial();
813 let vote_namespace = subject.namespace(namespace);
814 let vote_message = subject.message();
815 let seed_message = seed_message_from_subject(&subject);
816
817 let mut invalid: BTreeSet<_> = failures.into_iter().collect();
821 if partials.is_empty() {
822 return Verification::new(Vec::new(), invalid.into_iter().collect());
823 }
824 let vote_partials = non_empty![@partials.iter().map(|(vote, _)| vote)];
825 let seed_partials = non_empty![@partials.iter().map(|(_, seed)| seed)];
826
827 let mut vote_rng_seed = [0u8; 32];
829 let mut seed_rng_seed = [0u8; 32];
830 rng.fill_bytes(&mut vote_rng_seed);
831 rng.fill_bytes(&mut seed_rng_seed);
832
833 let (vote_invalid, seed_invalid) = strategy.join(
835 || {
836 let mut vote_rng = StdRng::from_seed(vote_rng_seed);
837 match threshold::batch_verify_same_message::<_, V, _>(
838 &mut vote_rng,
839 polynomial,
840 vote_namespace,
841 &vote_message,
842 vote_partials,
843 strategy,
844 ) {
845 Ok(()) => BTreeSet::new(),
846 Err(errs) => errs.into_iter().map(|p| p.index).collect(),
847 }
848 },
849 || {
850 let mut seed_rng = StdRng::from_seed(seed_rng_seed);
851 match threshold::batch_verify_same_message::<_, V, _>(
852 &mut seed_rng,
853 polynomial,
854 &namespace.seed,
855 &seed_message,
856 seed_partials,
857 strategy,
858 ) {
859 Ok(()) => BTreeSet::new(),
860 Err(errs) => errs.into_iter().map(|p| p.index).collect(),
861 }
862 },
863 );
864
865 invalid.extend(vote_invalid.union(&seed_invalid).copied());
867
868 let verified = partials
870 .into_iter()
871 .filter(|(vote, _)| !invalid.contains(&vote.index))
872 .map(|(vote, seed)| Attestation {
873 signer: vote.index,
874 signature: Signature {
875 vote_signature: vote.value,
876 seed_signature: seed.value,
877 }
878 .into(),
879 })
880 .collect();
881
882 Verification::new(verified, invalid.into_iter().collect())
883 }
884
885 fn assemble<I>(
886 &self,
887 attestations: NonEmpty<I>,
888 strategy: &impl Strategy,
889 ) -> Result<Self::Certificate, AssemblyError>
890 where
891 I: Iterator<Item = Attestation<Self>> + Send,
892 {
893 let partials = strategy.try_map_collect_vec(attestations, |attestation| {
895 let index = attestation.signer;
896 attestation
897 .signature
898 .get()
899 .map(|sig| {
900 (
901 PartialSignature::<V> {
902 index,
903 value: sig.vote_signature,
904 },
905 PartialSignature::<V> {
906 index,
907 value: sig.seed_signature,
908 },
909 )
910 })
911 .ok_or(AssemblyError::MalformedSignature(index))
912 })?;
913
914 let quorum = self.polynomial();
916 Signers::try_from((quorum, partials.iter().map(|(vote, _)| vote.index)))?;
917
918 let (vote_partials, seed_partials): (Vec<_>, Vec<_>) = partials.into_iter().unzip();
920 let (vote_signature, seed_signature) =
921 threshold::recover_pair(quorum, vote_partials.iter(), seed_partials.iter(), strategy)
922 .map_err(|_| AssemblyError::RecoveryFailed)?;
923 Ok(Signature {
924 vote_signature,
925 seed_signature,
926 }
927 .into())
928 }
929
930 fn is_attributable() -> bool {
931 false
932 }
933}
934
935#[cfg(test)]
936mod tests {
937 use super::*;
938 use crate::{
939 simplex::{
940 scheme::{notarize_namespace, seed_namespace},
941 types::{Finalization, Finalize, Notarization, Notarize, Proposal, Subject},
942 },
943 types::{Round, View},
944 };
945 use commonware_codec::{Decode, Encode};
946 use commonware_cryptography::{
947 Hasher, Sha256,
948 bls12381::{
949 dkg::feldman_desmedt as dkg,
950 primitives::{
951 group::Scalar,
952 ops::threshold,
953 sharing::Mode,
954 variant::{MinPk, MinSig, Variant},
955 },
956 },
957 certificate::{Scheme as _, Verifier as _, mocks::Fixture},
958 ed25519,
959 ed25519::certificate::mocks::participants as ed25519_participants,
960 sha256::Digest as Sha256Digest,
961 };
962 use commonware_math::algebra::{Additive, CryptoGroup, Random};
963 use commonware_parallel::Sequential;
964 use commonware_utils::{Faults, N3f1, N5f1, NZU32, test_rng};
965 use rand::{SeedableRng, rngs::StdRng};
966
967 const NAMESPACE: &[u8] = b"bls-threshold-signing-scheme";
968
969 type Scheme<V> = super::Scheme<ed25519::PublicKey, V>;
970 type Signature<V> = super::Signature<V>;
971
972 fn setup_signers<V: Variant>(n: u32, seed: u64) -> (Vec<Scheme<V>>, Scheme<V>) {
973 let mut rng = StdRng::seed_from_u64(seed);
974 let Fixture {
975 schemes, verifier, ..
976 } = fixture::<V, _>(&mut rng, NAMESPACE, n);
977
978 (schemes, verifier)
979 }
980
981 fn sample_proposal(epoch: Epoch, view: View, tag: u8) -> Proposal<Sha256Digest> {
982 Proposal::new(
983 Round::new(epoch, view),
984 view.previous().unwrap(),
985 Sha256::hash(&[&[tag]]),
986 )
987 }
988
989 fn signer_shares_must_match_participant_indices<V: Variant>() {
990 let mut rng = test_rng();
991 let participants = ed25519_participants(&mut rng, 4);
992 let (polynomial, mut shares) =
993 dkg::deal_anonymous::<V, N3f1>(&mut rng, Mode::NonZeroCounter, NZU32!(4));
994 shares[0].index = Participant::new(999);
995 Scheme::<V>::signer(
996 NAMESPACE,
997 participants.keys().clone(),
998 polynomial,
999 shares[0].clone(),
1000 );
1001 }
1002
1003 #[test]
1004 #[should_panic(expected = "share index must match participant indices")]
1005 fn test_signer_shares_must_match_participant_indices_min_pk() {
1006 signer_shares_must_match_participant_indices::<MinPk>();
1007 }
1008
1009 #[test]
1010 #[should_panic(expected = "share index must match participant indices")]
1011 fn test_signer_shares_must_match_participant_indices_min_sig() {
1012 signer_shares_must_match_participant_indices::<MinSig>();
1013 }
1014 fn scheme_polynomial_threshold_must_equal_quorum<V: Variant>() {
1015 let mut rng = test_rng();
1016 let participants = ed25519_participants(&mut rng, 5);
1017 let (polynomial, shares) =
1018 dkg::deal_anonymous::<V, N3f1>(&mut rng, Mode::NonZeroCounter, NZU32!(4));
1019 Scheme::<V>::signer(
1020 NAMESPACE,
1021 participants.keys().clone(),
1022 polynomial,
1023 shares[0].clone(),
1024 );
1025 }
1026
1027 #[test]
1028 #[should_panic(expected = "polynomial total must equal participant len")]
1029 fn test_scheme_polynomial_threshold_must_equal_quorum_min_pk() {
1030 scheme_polynomial_threshold_must_equal_quorum::<MinPk>();
1031 }
1032
1033 #[test]
1034 #[should_panic(expected = "polynomial total must equal participant len")]
1035 fn test_scheme_polynomial_threshold_must_equal_quorum_min_sig() {
1036 scheme_polynomial_threshold_must_equal_quorum::<MinSig>();
1037 }
1038
1039 fn verifier_polynomial_threshold_must_equal_quorum<V: Variant>() {
1040 let mut rng = test_rng();
1041 let participants = ed25519_participants(&mut rng, 5);
1042 let (polynomial, _) =
1043 dkg::deal_anonymous::<V, N3f1>(&mut rng, Mode::NonZeroCounter, NZU32!(4));
1044 Scheme::<V>::verifier(NAMESPACE, participants.keys().clone(), polynomial);
1045 }
1046
1047 #[test]
1048 #[should_panic(expected = "polynomial total must equal participant len")]
1049 fn test_verifier_polynomial_threshold_must_equal_quorum_min_pk() {
1050 verifier_polynomial_threshold_must_equal_quorum::<MinPk>();
1051 }
1052
1053 #[test]
1054 #[should_panic(expected = "polynomial total must equal participant len")]
1055 fn test_verifier_polynomial_threshold_must_equal_quorum_min_sig() {
1056 verifier_polynomial_threshold_must_equal_quorum::<MinSig>();
1057 }
1058
1059 fn signer_validates_polynomial_degree<V: Variant>() {
1060 let mut rng = test_rng();
1061 let participants = ed25519_participants(&mut rng, 4);
1062
1063 let (polynomial, shares) =
1066 dkg::deal_anonymous::<V, N5f1>(&mut rng, Mode::NonZeroCounter, NZU32!(4));
1067
1068 Scheme::<V>::signer(
1069 NAMESPACE,
1070 participants.keys().clone(),
1071 polynomial,
1072 shares[0].clone(),
1073 );
1074 }
1075
1076 #[test]
1077 #[should_panic(expected = "polynomial threshold must equal quorum")]
1078 fn test_signer_validates_polynomial_degree_min_pk() {
1079 signer_validates_polynomial_degree::<MinPk>();
1080 }
1081
1082 #[test]
1083 #[should_panic(expected = "polynomial threshold must equal quorum")]
1084 fn test_signer_validates_polynomial_degree_min_sig() {
1085 signer_validates_polynomial_degree::<MinSig>();
1086 }
1087
1088 fn verifier_validates_polynomial_degree<V: Variant>() {
1089 let mut rng = test_rng();
1090 let participants = ed25519_participants(&mut rng, 4);
1091
1092 let (polynomial, _) =
1095 dkg::deal_anonymous::<V, N5f1>(&mut rng, Mode::NonZeroCounter, NZU32!(4));
1096
1097 Scheme::<V>::verifier(NAMESPACE, participants.keys().clone(), polynomial);
1098 }
1099
1100 #[test]
1101 #[should_panic(expected = "polynomial threshold must equal quorum")]
1102 fn test_verifier_validates_polynomial_degree_min_pk() {
1103 verifier_validates_polynomial_degree::<MinPk>();
1104 }
1105
1106 #[test]
1107 #[should_panic(expected = "polynomial threshold must equal quorum")]
1108 fn test_verifier_validates_polynomial_degree_min_sig() {
1109 verifier_validates_polynomial_degree::<MinSig>();
1110 }
1111
1112 #[test]
1113 fn test_is_not_attributable() {
1114 assert!(!Scheme::<MinPk>::is_attributable());
1115 assert!(!Scheme::<MinSig>::is_attributable());
1116 }
1117
1118 #[test]
1119 fn test_is_batchable() {
1120 assert!(Scheme::<MinPk>::is_batchable());
1121 assert!(Scheme::<MinSig>::is_batchable());
1122 }
1123
1124 fn sign_vote_roundtrip_for_each_context<V: Variant>() {
1125 let (schemes, _) = setup_signers::<V>(4, 7);
1126 let scheme = &schemes[0];
1127 let mut rng = test_rng();
1128
1129 let proposal = sample_proposal(Epoch::new(0), View::new(2), 1);
1130 let notarize_vote = scheme
1131 .sign(Subject::Notarize {
1132 proposal: &proposal,
1133 })
1134 .unwrap();
1135 assert!(scheme.verify_attestation::<_, Sha256Digest>(
1136 &mut rng,
1137 Subject::Notarize {
1138 proposal: &proposal,
1139 },
1140 ¬arize_vote,
1141 &Sequential,
1142 ));
1143
1144 let nullify_vote = scheme
1145 .sign::<Sha256Digest>(Subject::Nullify {
1146 round: proposal.round,
1147 })
1148 .unwrap();
1149 assert!(scheme.verify_attestation::<_, Sha256Digest>(
1150 &mut rng,
1151 Subject::Nullify {
1152 round: proposal.round,
1153 },
1154 &nullify_vote,
1155 &Sequential,
1156 ));
1157
1158 let finalize_vote = scheme
1159 .sign(Subject::Finalize {
1160 proposal: &proposal,
1161 })
1162 .unwrap();
1163 assert!(scheme.verify_attestation::<_, Sha256Digest>(
1164 &mut rng,
1165 Subject::Finalize {
1166 proposal: &proposal,
1167 },
1168 &finalize_vote,
1169 &Sequential,
1170 ));
1171 }
1172
1173 #[test]
1174 fn test_sign_vote_roundtrip_for_each_context() {
1175 sign_vote_roundtrip_for_each_context::<MinPk>();
1176 sign_vote_roundtrip_for_each_context::<MinSig>();
1177 }
1178
1179 fn verifier_cannot_sign<V: Variant>() {
1180 let (_, verifier) = setup_signers::<V>(4, 11);
1181
1182 let proposal = sample_proposal(Epoch::new(0), View::new(3), 2);
1183 assert!(
1184 verifier
1185 .sign(Subject::Notarize {
1186 proposal: &proposal,
1187 })
1188 .is_none(),
1189 "verifier should not produce signatures"
1190 );
1191 }
1192
1193 #[test]
1194 fn test_verifier_cannot_sign() {
1195 verifier_cannot_sign::<MinPk>();
1196 verifier_cannot_sign::<MinSig>();
1197 }
1198
1199 fn verifier_accepts_votes<V: Variant>() {
1200 let (schemes, verifier) = setup_signers::<V>(4, 11);
1201 let proposal = sample_proposal(Epoch::new(0), View::new(3), 2);
1202 let vote = schemes[1]
1203 .sign(Subject::Notarize {
1204 proposal: &proposal,
1205 })
1206 .unwrap();
1207 assert!(verifier.verify_attestation::<_, Sha256Digest>(
1208 &mut test_rng(),
1209 Subject::Notarize {
1210 proposal: &proposal,
1211 },
1212 &vote,
1213 &Sequential,
1214 ));
1215 }
1216
1217 #[test]
1218 fn test_verifier_accepts_votes() {
1219 verifier_accepts_votes::<MinPk>();
1220 verifier_accepts_votes::<MinSig>();
1221 }
1222
1223 fn verify_votes_filters_bad_signers<V: Variant>() {
1224 let mut rng = test_rng();
1225 let (schemes, _) = setup_signers::<V>(5, 13);
1226 let quorum = N3f1::quorum(schemes.len()) as usize;
1227 let proposal = sample_proposal(Epoch::new(0), View::new(5), 3);
1228
1229 let mut votes: Vec<_> = schemes
1230 .iter()
1231 .take(quorum)
1232 .map(|scheme| {
1233 scheme
1234 .sign(Subject::Notarize {
1235 proposal: &proposal,
1236 })
1237 .unwrap()
1238 })
1239 .collect();
1240
1241 let verification = schemes[0].verify_attestations(
1242 &mut rng,
1243 Subject::Notarize {
1244 proposal: &proposal,
1245 },
1246 votes.clone(),
1247 &Sequential,
1248 );
1249 assert!(verification.invalid.is_empty());
1250 assert_eq!(verification.verified.len(), quorum);
1251
1252 votes[0].signer = Participant::new(999);
1253 let verification = schemes[0].verify_attestations(
1254 &mut rng,
1255 Subject::Notarize {
1256 proposal: &proposal,
1257 },
1258 votes,
1259 &Sequential,
1260 );
1261 assert_eq!(verification.invalid, vec![Participant::new(999)]);
1262 assert_eq!(verification.verified.len(), quorum - 1);
1263 }
1264
1265 #[test]
1266 fn test_verify_votes_filters_bad_signers() {
1267 verify_votes_filters_bad_signers::<MinPk>();
1268 verify_votes_filters_bad_signers::<MinSig>();
1269 }
1270
1271 fn assemble_certificate_requires_quorum<V: Variant>() {
1272 let (schemes, _) = setup_signers::<V>(4, 17);
1273 let quorum = N3f1::quorum(schemes.len());
1274 let subquorum = usize::try_from(quorum - 1).expect("quorum exceeds usize::MAX");
1275 let proposal = sample_proposal(Epoch::new(0), View::new(7), 4);
1276
1277 let votes: Vec<_> = schemes
1278 .iter()
1279 .take(subquorum)
1280 .map(|scheme| {
1281 scheme
1282 .sign(Subject::Notarize {
1283 proposal: &proposal,
1284 })
1285 .unwrap()
1286 })
1287 .collect();
1288
1289 assert_eq!(
1290 schemes[0].assemble(non_empty![@votes], &Sequential),
1291 Err(AssemblyError::InsufficientAttestations(quorum, quorum - 1))
1292 );
1293 }
1294
1295 #[test]
1296 fn test_assemble_certificate_requires_quorum() {
1297 assemble_certificate_requires_quorum::<MinPk>();
1298 assemble_certificate_requires_quorum::<MinSig>();
1299 }
1300
1301 fn assemble_certificate_rejects_duplicate_signers<V: Variant>() {
1302 let (schemes, _) = setup_signers::<V>(4, 18);
1303 let quorum =
1304 usize::try_from(N3f1::quorum(schemes.len())).expect("quorum exceeds usize::MAX");
1305 let proposal = sample_proposal(Epoch::new(0), View::new(8), 4);
1306 let mut votes: Vec<_> = schemes
1307 .iter()
1308 .take(quorum)
1309 .map(|scheme| {
1310 scheme
1311 .sign(Subject::Notarize {
1312 proposal: &proposal,
1313 })
1314 .unwrap()
1315 })
1316 .collect();
1317 let duplicate = votes[0].signer;
1318 votes.push(votes[0].clone());
1319
1320 assert_eq!(
1321 schemes[0].assemble(non_empty![@votes], &Sequential),
1322 Err(AssemblyError::DuplicateSigner(duplicate))
1323 );
1324 }
1325
1326 #[test]
1327 fn test_assemble_certificate_rejects_duplicate_signers() {
1328 assemble_certificate_rejects_duplicate_signers::<MinPk>();
1329 assemble_certificate_rejects_duplicate_signers::<MinSig>();
1330 }
1331
1332 fn assemble_certificate_rejects_unknown_signer<V: Variant>() {
1333 let (schemes, _) = setup_signers::<V>(4, 19);
1334 let quorum =
1335 usize::try_from(N3f1::quorum(schemes.len())).expect("quorum exceeds usize::MAX");
1336 let proposal = sample_proposal(Epoch::new(0), View::new(9), 4);
1337 let mut votes: Vec<_> = schemes
1338 .iter()
1339 .take(quorum)
1340 .map(|scheme| {
1341 scheme
1342 .sign(Subject::Notarize {
1343 proposal: &proposal,
1344 })
1345 .unwrap()
1346 })
1347 .collect();
1348 let unknown_signer = Participant::from_usize(schemes.len());
1349 let mut unknown = votes[0].clone();
1350 unknown.signer = unknown_signer;
1351 votes.push(unknown);
1352
1353 assert_eq!(
1354 schemes[0].assemble(non_empty![@votes], &Sequential),
1355 Err(AssemblyError::UnknownSigner(unknown_signer))
1356 );
1357 }
1358
1359 #[test]
1360 fn test_assemble_certificate_rejects_unknown_signer() {
1361 assemble_certificate_rejects_unknown_signer::<MinPk>();
1362 assemble_certificate_rejects_unknown_signer::<MinSig>();
1363 }
1364
1365 fn assemble_certificate_rejects_malformed_signature<V: Variant>() {
1366 let (schemes, _) = setup_signers::<V>(4, 20);
1367 let quorum =
1368 usize::try_from(N3f1::quorum(schemes.len())).expect("quorum exceeds usize::MAX");
1369 let proposal = sample_proposal(Epoch::new(0), View::new(10), 4);
1370 let mut votes: Vec<_> = schemes
1371 .iter()
1372 .take(quorum)
1373 .map(|scheme| {
1374 scheme
1375 .sign(Subject::Notarize {
1376 proposal: &proposal,
1377 })
1378 .unwrap()
1379 })
1380 .collect();
1381 let malformed_signer = votes[0].signer;
1382 let mut malformed = &[0u8][..];
1383 votes[0].signature = Lazy::deferred(&mut malformed, ());
1384
1385 assert_eq!(
1386 schemes[0].assemble(non_empty![@votes], &Sequential),
1387 Err(AssemblyError::MalformedSignature(malformed_signer))
1388 );
1389 }
1390
1391 #[test]
1392 fn test_assemble_certificate_rejects_malformed_signature() {
1393 assemble_certificate_rejects_malformed_signature::<MinPk>();
1394 assemble_certificate_rejects_malformed_signature::<MinSig>();
1395 }
1396
1397 fn verify_certificate<V: Variant>() {
1398 let (schemes, verifier) = setup_signers::<V>(4, 19);
1399 let quorum = N3f1::quorum(schemes.len()) as usize;
1400 let proposal = sample_proposal(Epoch::new(0), View::new(9), 5);
1401
1402 let votes: Vec<_> = schemes
1403 .iter()
1404 .take(quorum)
1405 .map(|scheme| {
1406 scheme
1407 .sign(Subject::Finalize {
1408 proposal: &proposal,
1409 })
1410 .unwrap()
1411 })
1412 .collect();
1413
1414 let certificate = schemes[0]
1415 .assemble(non_empty![@votes], &Sequential)
1416 .expect("assemble certificate");
1417
1418 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1419 &mut test_rng(),
1420 Subject::Finalize {
1421 proposal: &proposal,
1422 },
1423 &certificate,
1424 &Sequential,
1425 ));
1426 }
1427
1428 #[test]
1429 fn test_verify_certificate() {
1430 verify_certificate::<MinPk>();
1431 verify_certificate::<MinSig>();
1432 }
1433
1434 fn verify_certificate_detects_corruption<V: Variant>() {
1435 let mut rng = test_rng();
1436 let (schemes, verifier) = setup_signers::<V>(4, 23);
1437 let quorum = N3f1::quorum(schemes.len()) as usize;
1438 let proposal = sample_proposal(Epoch::new(0), View::new(11), 6);
1439
1440 let votes: Vec<_> = schemes
1441 .iter()
1442 .take(quorum)
1443 .map(|scheme| {
1444 scheme
1445 .sign(Subject::Notarize {
1446 proposal: &proposal,
1447 })
1448 .unwrap()
1449 })
1450 .collect();
1451
1452 let certificate = schemes[0]
1453 .assemble(non_empty![@votes], &Sequential)
1454 .expect("assemble certificate");
1455
1456 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1457 &mut rng,
1458 Subject::Notarize {
1459 proposal: &proposal,
1460 },
1461 &certificate,
1462 &Sequential,
1463 ));
1464
1465 let cert = certificate.get().unwrap();
1466 let corrupted: Certificate<V> = Signature {
1467 vote_signature: cert.seed_signature,
1468 seed_signature: cert.seed_signature,
1469 }
1470 .into();
1471 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1472 &mut rng,
1473 Subject::Notarize {
1474 proposal: &proposal,
1475 },
1476 &corrupted,
1477 &Sequential,
1478 ));
1479 }
1480
1481 #[test]
1482 fn test_verify_certificate_detects_corruption() {
1483 verify_certificate_detects_corruption::<MinPk>();
1484 verify_certificate_detects_corruption::<MinSig>();
1485 }
1486
1487 fn verify_certificate_rejects_identity_components<V: Variant>() {
1488 let mut rng = test_rng();
1489 let (schemes, verifier) = setup_signers::<V>(4, 27);
1490 let quorum =
1491 usize::try_from(N3f1::quorum(schemes.len())).expect("quorum exceeds usize::MAX");
1492 let proposal = sample_proposal(Epoch::new(0), View::new(12), 7);
1493 let votes: Vec<_> = schemes
1494 .iter()
1495 .take(quorum)
1496 .map(|scheme| {
1497 scheme
1498 .sign(Subject::Notarize {
1499 proposal: &proposal,
1500 })
1501 .unwrap()
1502 })
1503 .collect();
1504 let certificate = schemes[0]
1505 .assemble(non_empty![@votes], &Sequential)
1506 .expect("assemble certificate");
1507 let signature = certificate.get().unwrap();
1508 let corrupted = [
1509 Signature {
1510 vote_signature: V::Signature::zero(),
1511 seed_signature: signature.seed_signature,
1512 }
1513 .into(),
1514 Signature {
1515 vote_signature: signature.vote_signature,
1516 seed_signature: V::Signature::zero(),
1517 }
1518 .into(),
1519 ];
1520
1521 for certificate in &corrupted {
1522 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1523 &mut rng,
1524 Subject::Notarize {
1525 proposal: &proposal,
1526 },
1527 certificate,
1528 &Sequential,
1529 ));
1530 }
1531 }
1532
1533 #[test]
1534 fn test_verify_certificate_rejects_identity_components() {
1535 verify_certificate_rejects_identity_components::<MinPk>();
1536 verify_certificate_rejects_identity_components::<MinSig>();
1537 }
1538
1539 fn certificate_codec_roundtrip<V: Variant>() {
1540 let (schemes, _) = setup_signers::<V>(5, 29);
1541 let quorum = N3f1::quorum(schemes.len()) as usize;
1542 let proposal = sample_proposal(Epoch::new(0), View::new(13), 7);
1543
1544 let votes: Vec<_> = schemes
1545 .iter()
1546 .take(quorum)
1547 .map(|scheme| {
1548 scheme
1549 .sign(Subject::Notarize {
1550 proposal: &proposal,
1551 })
1552 .unwrap()
1553 })
1554 .collect();
1555
1556 let certificate = schemes[0]
1557 .assemble(non_empty![@votes], &Sequential)
1558 .expect("assemble certificate");
1559
1560 let encoded = certificate.encode();
1561 let decoded = Certificate::<V>::decode_cfg(encoded, &()).expect("decode certificate");
1562 assert_eq!(decoded, certificate);
1563 }
1564
1565 #[test]
1566 fn test_certificate_codec_roundtrip() {
1567 certificate_codec_roundtrip::<MinPk>();
1568 certificate_codec_roundtrip::<MinSig>();
1569 }
1570
1571 fn seed_codec_roundtrip<V: Variant>() {
1572 let (schemes, _) = setup_signers::<V>(4, 5);
1573 let quorum = N3f1::quorum(schemes.len()) as usize;
1574 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1575
1576 let votes: Vec<_> = schemes
1577 .iter()
1578 .take(quorum)
1579 .map(|scheme| {
1580 scheme
1581 .sign(Subject::Finalize {
1582 proposal: &proposal,
1583 })
1584 .unwrap()
1585 })
1586 .collect();
1587
1588 let certificate = schemes[0]
1589 .assemble(non_empty![@votes], &Sequential)
1590 .expect("assemble certificate");
1591 let cert = certificate.get().unwrap();
1592
1593 let seed = Seed::new(proposal.round, cert.seed_signature);
1594
1595 let encoded = seed.encode();
1596 let decoded = Seed::<V>::decode_cfg(encoded, &()).expect("decode seed");
1597 assert_eq!(decoded, seed);
1598 }
1599
1600 #[test]
1601 fn test_seed_codec_roundtrip() {
1602 seed_codec_roundtrip::<MinPk>();
1603 seed_codec_roundtrip::<MinSig>();
1604 }
1605
1606 fn seed_verify<V: Variant>() {
1607 let (schemes, _) = setup_signers::<V>(4, 5);
1608 let quorum = N3f1::quorum(schemes.len()) as usize;
1609 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1610
1611 let votes: Vec<_> = schemes
1612 .iter()
1613 .take(quorum)
1614 .map(|scheme| {
1615 scheme
1616 .sign(Subject::Finalize {
1617 proposal: &proposal,
1618 })
1619 .unwrap()
1620 })
1621 .collect();
1622
1623 let certificate = schemes[0]
1624 .assemble(non_empty![@votes], &Sequential)
1625 .expect("assemble certificate");
1626 let cert = certificate.get().unwrap();
1627
1628 let seed = Seed::new(proposal.round, cert.seed_signature);
1629
1630 assert!(seed.verify(&schemes[0]));
1631
1632 let invalid_seed = Seed::new(
1634 Round::new(proposal.epoch(), proposal.view().next()),
1635 cert.seed_signature,
1636 );
1637
1638 assert!(!invalid_seed.verify(&schemes[0]));
1639 }
1640
1641 #[test]
1642 fn test_seed_verify() {
1643 seed_verify::<MinPk>();
1644 seed_verify::<MinSig>();
1645 }
1646
1647 fn seedable<V: Variant>() {
1648 let (schemes, _) = setup_signers::<V>(4, 5);
1649 let quorum = N3f1::quorum(schemes.len()) as usize;
1650 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1651
1652 let notarizes: Vec<_> = schemes
1653 .iter()
1654 .take(quorum)
1655 .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
1656 .collect();
1657
1658 let notarization =
1659 Notarization::from_notarizes(&schemes[0], non_empty![@¬arizes], &Sequential)
1660 .unwrap();
1661
1662 let finalizes: Vec<_> = schemes
1663 .iter()
1664 .take(quorum)
1665 .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap())
1666 .collect();
1667
1668 let finalization =
1669 Finalization::from_finalizes(&schemes[0], non_empty![@&finalizes], &Sequential)
1670 .unwrap();
1671
1672 assert_eq!(notarization.seed(), finalization.seed());
1673 assert!(notarization.seed().verify(&schemes[0]));
1674 }
1675
1676 #[test]
1677 fn test_seedable() {
1678 seedable::<MinPk>();
1679 seedable::<MinSig>();
1680 }
1681
1682 fn scheme_clone_and_verifier<V: Variant>() {
1683 let (schemes, verifier) = setup_signers::<V>(4, 31);
1684 let signer = schemes[0].clone();
1685 let proposal = sample_proposal(Epoch::new(0), View::new(21), 9);
1686
1687 assert!(
1688 signer
1689 .sign(Subject::Notarize {
1690 proposal: &proposal,
1691 })
1692 .is_some(),
1693 "signer should produce votes"
1694 );
1695
1696 assert!(
1697 verifier
1698 .sign(Subject::Notarize {
1699 proposal: &proposal,
1700 })
1701 .is_none(),
1702 "verifier should not produce votes"
1703 );
1704 }
1705
1706 #[test]
1707 fn test_scheme_clone_and_verifier() {
1708 scheme_clone_and_verifier::<MinPk>();
1709 scheme_clone_and_verifier::<MinSig>();
1710 }
1711
1712 fn certificate_verifier_accepts_certificates<V: Variant>() {
1713 let (schemes, _) = setup_signers::<V>(4, 37);
1714 let quorum = N3f1::quorum(schemes.len()) as usize;
1715 let proposal = sample_proposal(Epoch::new(0), View::new(15), 8);
1716
1717 let votes: Vec<_> = schemes
1718 .iter()
1719 .take(quorum)
1720 .map(|scheme| {
1721 scheme
1722 .sign(Subject::Finalize {
1723 proposal: &proposal,
1724 })
1725 .unwrap()
1726 })
1727 .collect();
1728
1729 let certificate = schemes[0]
1730 .assemble(non_empty![@votes], &Sequential)
1731 .expect("assemble certificate");
1732
1733 let certificate_verifier =
1734 Scheme::<V>::certificate_verifier(NAMESPACE, *schemes[0].identity());
1735 assert!(
1736 certificate_verifier
1737 .sign(Subject::Finalize {
1738 proposal: &proposal,
1739 })
1740 .is_none(),
1741 "certificate verifier should not produce votes"
1742 );
1743 assert!(certificate_verifier.verify_certificate::<_, Sha256Digest>(
1744 &mut test_rng(),
1745 Subject::Finalize {
1746 proposal: &proposal,
1747 },
1748 &certificate,
1749 &Sequential,
1750 ));
1751 }
1752
1753 #[test]
1754 fn test_certificate_verifier_accepts_certificates() {
1755 certificate_verifier_accepts_certificates::<MinPk>();
1756 certificate_verifier_accepts_certificates::<MinSig>();
1757 }
1758
1759 fn certificate_verifier_panics_on_vote<V: Variant>() {
1760 let (schemes, _) = setup_signers::<V>(4, 37);
1761 let certificate_verifier =
1762 Scheme::<V>::certificate_verifier(NAMESPACE, *schemes[0].identity());
1763 let proposal = sample_proposal(Epoch::new(0), View::new(15), 8);
1764 let vote = schemes[1]
1765 .sign(Subject::Finalize {
1766 proposal: &proposal,
1767 })
1768 .unwrap();
1769
1770 certificate_verifier.verify_attestation::<_, Sha256Digest>(
1771 &mut test_rng(),
1772 Subject::Finalize {
1773 proposal: &proposal,
1774 },
1775 &vote,
1776 &Sequential,
1777 );
1778 }
1779
1780 #[test]
1781 #[should_panic(expected = "can only be called for signer and verifier")]
1782 fn test_certificate_verifier_panics_on_vote_min_pk() {
1783 certificate_verifier_panics_on_vote::<MinPk>();
1784 }
1785
1786 #[test]
1787 #[should_panic(expected = "can only be called for signer and verifier")]
1788 fn test_certificate_verifier_panics_on_vote_min_sig() {
1789 certificate_verifier_panics_on_vote::<MinSig>();
1790 }
1791
1792 fn verify_certificate_returns_seed_randomness<V: Variant>() {
1793 let (schemes, _) = setup_signers::<V>(4, 43);
1794 let quorum = N3f1::quorum(schemes.len()) as usize;
1795 let proposal = sample_proposal(Epoch::new(0), View::new(19), 10);
1796
1797 let votes: Vec<_> = schemes
1798 .iter()
1799 .take(quorum)
1800 .map(|scheme| {
1801 scheme
1802 .sign(Subject::Notarize {
1803 proposal: &proposal,
1804 })
1805 .unwrap()
1806 })
1807 .collect();
1808
1809 let certificate = schemes[0]
1810 .assemble(non_empty![@votes], &Sequential)
1811 .expect("assemble certificate");
1812 let cert = certificate.get().unwrap();
1813
1814 let seed = Seed::<V>::new(proposal.round, cert.seed_signature);
1815 assert_eq!(seed.signature, cert.seed_signature);
1816 }
1817
1818 #[test]
1819 fn test_verify_certificate_returns_seed_randomness() {
1820 verify_certificate_returns_seed_randomness::<MinPk>();
1821 verify_certificate_returns_seed_randomness::<MinSig>();
1822 }
1823
1824 fn certificate_decode_rejects_length_mismatch<V: Variant>() {
1825 let (schemes, _) = setup_signers::<V>(4, 47);
1826 let quorum = N3f1::quorum(schemes.len()) as usize;
1827 let proposal = sample_proposal(Epoch::new(0), View::new(21), 11);
1828
1829 let votes: Vec<_> = schemes
1830 .iter()
1831 .take(quorum)
1832 .map(|scheme| {
1833 scheme
1834 .sign::<Sha256Digest>(Subject::Nullify {
1835 round: proposal.round,
1836 })
1837 .unwrap()
1838 })
1839 .collect();
1840
1841 let certificate = schemes[0]
1842 .assemble(non_empty![@votes], &Sequential)
1843 .expect("assemble certificate");
1844
1845 let mut encoded = certificate.encode();
1846 let truncated = encoded.split_to(encoded.len() - 1);
1847 assert!(Signature::<V>::decode_cfg(truncated, &()).is_err());
1848 }
1849
1850 #[test]
1851 fn test_certificate_decode_rejects_length_mismatch() {
1852 certificate_decode_rejects_length_mismatch::<MinPk>();
1853 certificate_decode_rejects_length_mismatch::<MinSig>();
1854 }
1855
1856 fn sign_vote_partial_matches_share<V: Variant>() {
1857 let (schemes, _) = setup_signers::<V>(4, 53);
1858 let scheme = &schemes[0];
1859 let share = scheme.share().expect("has share");
1860
1861 let proposal = sample_proposal(Epoch::new(0), View::new(23), 12);
1862 let vote = scheme
1863 .sign(Subject::Notarize {
1864 proposal: &proposal,
1865 })
1866 .unwrap();
1867
1868 let notarize_namespace = notarize_namespace(NAMESPACE);
1869 let notarize_message = proposal.encode();
1870 let expected_message = threshold::sign_message::<V>(
1871 share,
1872 notarize_namespace.as_ref(),
1873 notarize_message.as_ref(),
1874 )
1875 .value;
1876
1877 let seed_namespace = seed_namespace(NAMESPACE);
1878 let seed_message = proposal.round.encode();
1879 let expected_seed =
1880 threshold::sign_message::<V>(share, seed_namespace.as_ref(), seed_message.as_ref())
1881 .value;
1882
1883 assert_eq!(vote.signer, share.index);
1884 let sig = vote.signature.get().unwrap();
1885 assert_eq!(sig.vote_signature, expected_message);
1886 assert_eq!(sig.seed_signature, expected_seed);
1887 }
1888
1889 #[test]
1890 fn test_sign_vote_partial_matches_share() {
1891 sign_vote_partial_matches_share::<MinPk>();
1892 sign_vote_partial_matches_share::<MinSig>();
1893 }
1894
1895 fn verify_certificate_detects_seed_corruption<V: Variant>() {
1896 let mut rng = test_rng();
1897 let (schemes, verifier) = setup_signers::<V>(4, 59);
1898 let quorum = N3f1::quorum(schemes.len()) as usize;
1899 let proposal = sample_proposal(Epoch::new(0), View::new(25), 13);
1900
1901 let votes: Vec<_> = schemes
1902 .iter()
1903 .take(quorum)
1904 .map(|scheme| {
1905 scheme
1906 .sign::<Sha256Digest>(Subject::Nullify {
1907 round: proposal.round,
1908 })
1909 .unwrap()
1910 })
1911 .collect();
1912
1913 let certificate = schemes[0]
1914 .assemble(non_empty![@votes], &Sequential)
1915 .expect("assemble certificate");
1916
1917 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1918 &mut rng,
1919 Subject::Nullify {
1920 round: proposal.round,
1921 },
1922 &certificate,
1923 &Sequential,
1924 ));
1925
1926 let cert = certificate.get().unwrap();
1927 let corrupted: Certificate<V> = Signature {
1928 vote_signature: cert.vote_signature,
1929 seed_signature: cert.vote_signature,
1930 }
1931 .into();
1932 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1933 &mut rng,
1934 Subject::Nullify {
1935 round: proposal.round,
1936 },
1937 &corrupted,
1938 &Sequential,
1939 ));
1940 }
1941
1942 #[test]
1943 fn test_verify_certificate_detects_seed_corruption() {
1944 verify_certificate_detects_seed_corruption::<MinPk>();
1945 verify_certificate_detects_seed_corruption::<MinSig>();
1946 }
1947
1948 fn encrypt_decrypt<V: Variant>() {
1949 let mut rng = test_rng();
1950 let (schemes, verifier) = setup_signers::<V>(4, 61);
1951 let quorum = N3f1::quorum(schemes.len()) as usize;
1952
1953 let message = b"Secret message for future view10";
1955
1956 let target = Round::new(Epoch::new(333), View::new(10));
1958
1959 let ciphertext = schemes[0]
1961 .encrypt(&mut rng, target, *message)
1962 .expect("valid TLE encryption inputs");
1963
1964 let ciphertext_verifier = verifier
1966 .encrypt(&mut rng, target, *message)
1967 .expect("valid TLE encryption inputs");
1968
1969 let proposal = sample_proposal(target.epoch(), target.view(), 14);
1971 let notarizes: Vec<_> = schemes
1972 .iter()
1973 .take(quorum)
1974 .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
1975 .collect();
1976
1977 let notarization =
1978 Notarization::from_notarizes(&schemes[0], non_empty![@¬arizes], &Sequential)
1979 .unwrap();
1980
1981 let seed = notarization.seed();
1983 let decrypted = seed.decrypt(&ciphertext).unwrap();
1984 assert_eq!(message, decrypted.as_ref());
1985
1986 let decrypted_verifier = seed.decrypt(&ciphertext_verifier).unwrap();
1987 assert_eq!(message, decrypted_verifier.as_ref());
1988 }
1989
1990 #[test]
1991 fn test_encrypt_decrypt() {
1992 encrypt_decrypt::<MinPk>();
1993 encrypt_decrypt::<MinSig>();
1994 }
1995
1996 fn encrypt_with_identity_returns_error<V: Variant>() {
1997 let mut rng = test_rng();
1998 let verifier = Scheme::<V>::certificate_verifier(NAMESPACE, V::Public::zero());
1999 let target = Round::new(Epoch::new(333), View::new(10));
2000
2001 assert!(matches!(
2002 verifier.encrypt(&mut rng, target, [0u8; 32]),
2003 Err(tle::Error::InvalidPublicKey)
2004 ));
2005 }
2006
2007 #[test]
2008 fn test_encrypt_with_identity_returns_error() {
2009 encrypt_with_identity_returns_error::<MinPk>();
2010 encrypt_with_identity_returns_error::<MinSig>();
2011 }
2012
2013 fn verify_attestation_rejects_malleability<V: Variant>() {
2014 let mut rng = test_rng();
2015 let (schemes, _) = setup_signers::<V>(4, 67);
2016 let proposal = sample_proposal(Epoch::new(0), View::new(27), 14);
2017
2018 let attestation = schemes[0]
2019 .sign(Subject::Notarize {
2020 proposal: &proposal,
2021 })
2022 .unwrap();
2023
2024 assert!(schemes[0].verify_attestation::<_, Sha256Digest>(
2025 &mut rng,
2026 Subject::Notarize {
2027 proposal: &proposal,
2028 },
2029 &attestation,
2030 &Sequential,
2031 ));
2032
2033 let random_scalar = Scalar::random(&mut rng);
2034 let delta = V::Signature::generator() * &random_scalar;
2035 let att_sig = attestation.signature.get().unwrap();
2036 let forged_attestation: Attestation<Scheme<V>> = Attestation {
2037 signer: attestation.signer,
2038 signature: Signature {
2039 vote_signature: att_sig.vote_signature - &delta,
2040 seed_signature: att_sig.seed_signature + &delta,
2041 }
2042 .into(),
2043 };
2044
2045 let forged_sig = forged_attestation.signature.get().unwrap();
2046 let forged_sum = forged_sig.vote_signature + &forged_sig.seed_signature;
2047 let valid_sum = att_sig.vote_signature + &att_sig.seed_signature;
2048 assert_eq!(forged_sum, valid_sum, "signature sums should be equal");
2049
2050 assert!(
2051 !schemes[0].verify_attestation::<_, Sha256Digest>(
2052 &mut rng,
2053 Subject::Notarize {
2054 proposal: &proposal,
2055 },
2056 &forged_attestation,
2057 &Sequential,
2058 ),
2059 "forged attestation should be rejected"
2060 );
2061 }
2062
2063 #[test]
2064 fn test_verify_attestation_rejects_malleability() {
2065 verify_attestation_rejects_malleability::<MinPk>();
2066 verify_attestation_rejects_malleability::<MinSig>();
2067 }
2068
2069 fn verify_attestations_rejects_malleability<V: Variant>() {
2070 let mut rng = test_rng();
2071 let (schemes, _) = setup_signers::<V>(4, 71);
2072 let proposal = sample_proposal(Epoch::new(0), View::new(29), 15);
2073
2074 let attestation1 = schemes[0]
2075 .sign(Subject::Notarize {
2076 proposal: &proposal,
2077 })
2078 .unwrap();
2079 let attestation2 = schemes[1]
2080 .sign(Subject::Notarize {
2081 proposal: &proposal,
2082 })
2083 .unwrap();
2084
2085 let verification = schemes[0].verify_attestations(
2086 &mut rng,
2087 Subject::Notarize {
2088 proposal: &proposal,
2089 },
2090 vec![attestation1.clone(), attestation2.clone()],
2091 &Sequential,
2092 );
2093 assert!(verification.invalid.is_empty());
2094 assert_eq!(verification.verified.len(), 2);
2095
2096 let random_scalar = Scalar::random(&mut rng);
2097 let delta = V::Signature::generator() * &random_scalar;
2098 let att1_sig = attestation1.signature.get().unwrap();
2099 let att2_sig = attestation2.signature.get().unwrap();
2100 let forged_attestation1: Attestation<Scheme<V>> = Attestation {
2101 signer: attestation1.signer,
2102 signature: Signature {
2103 vote_signature: att1_sig.vote_signature - &delta,
2104 seed_signature: att1_sig.seed_signature,
2105 }
2106 .into(),
2107 };
2108 let forged_attestation2: Attestation<Scheme<V>> = Attestation {
2109 signer: attestation2.signer,
2110 signature: Signature {
2111 vote_signature: att2_sig.vote_signature + &delta,
2112 seed_signature: att2_sig.seed_signature,
2113 }
2114 .into(),
2115 };
2116
2117 let forged1_sig = forged_attestation1.signature.get().unwrap();
2118 let forged2_sig = forged_attestation2.signature.get().unwrap();
2119 let forged_vote_sum = forged1_sig.vote_signature + &forged2_sig.vote_signature;
2120 let valid_vote_sum = att1_sig.vote_signature + &att2_sig.vote_signature;
2121 assert_eq!(
2122 forged_vote_sum, valid_vote_sum,
2123 "vote signature sums should be equal"
2124 );
2125
2126 let verification = schemes[0].verify_attestations(
2127 &mut rng,
2128 Subject::Notarize {
2129 proposal: &proposal,
2130 },
2131 vec![forged_attestation1, forged_attestation2],
2132 &Sequential,
2133 );
2134 assert!(
2135 !verification.invalid.is_empty(),
2136 "forged attestations should be detected"
2137 );
2138 }
2139
2140 #[test]
2141 fn test_verify_attestations_rejects_malleability() {
2142 verify_attestations_rejects_malleability::<MinPk>();
2143 verify_attestations_rejects_malleability::<MinSig>();
2144 }
2145
2146 fn verify_certificate_rejects_malleability<V: Variant>() {
2147 let mut rng = test_rng();
2148 let (schemes, verifier) = setup_signers::<V>(4, 73);
2149 let quorum = N3f1::quorum(schemes.len()) as usize;
2150 let proposal = sample_proposal(Epoch::new(0), View::new(31), 16);
2151
2152 let votes: Vec<_> = schemes
2153 .iter()
2154 .take(quorum)
2155 .map(|scheme| {
2156 scheme
2157 .sign(Subject::Notarize {
2158 proposal: &proposal,
2159 })
2160 .unwrap()
2161 })
2162 .collect();
2163
2164 let certificate = schemes[0]
2165 .assemble(non_empty![@votes], &Sequential)
2166 .expect("assemble certificate");
2167
2168 assert!(verifier.verify_certificate::<_, Sha256Digest>(
2169 &mut rng,
2170 Subject::Notarize {
2171 proposal: &proposal,
2172 },
2173 &certificate,
2174 &Sequential,
2175 ));
2176
2177 let cert = certificate.get().unwrap();
2178 let random_scalar = Scalar::random(&mut rng);
2179 let delta = V::Signature::generator() * &random_scalar;
2180 let forged_certificate: Certificate<V> = Signature {
2181 vote_signature: cert.vote_signature - &delta,
2182 seed_signature: cert.seed_signature + &delta,
2183 }
2184 .into();
2185
2186 let forged_cert = forged_certificate.get().unwrap();
2187 let forged_sum = forged_cert.vote_signature + &forged_cert.seed_signature;
2188 let valid_sum = cert.vote_signature + &cert.seed_signature;
2189 assert_eq!(forged_sum, valid_sum, "signature sums should be equal");
2190
2191 assert!(
2192 !verifier.verify_certificate::<_, Sha256Digest>(
2193 &mut rng,
2194 Subject::Notarize {
2195 proposal: &proposal,
2196 },
2197 &forged_certificate,
2198 &Sequential,
2199 ),
2200 "forged certificate should be rejected"
2201 );
2202 }
2203
2204 #[test]
2205 fn test_verify_certificate_rejects_malleability() {
2206 verify_certificate_rejects_malleability::<MinPk>();
2207 verify_certificate_rejects_malleability::<MinSig>();
2208 }
2209
2210 fn verify_certificates_rejects_malleability<V: Variant>() {
2211 let mut rng = test_rng();
2212 let (schemes, verifier) = setup_signers::<V>(4, 79);
2213 let quorum = N3f1::quorum(schemes.len()) as usize;
2214 let proposal1 = sample_proposal(Epoch::new(0), View::new(33), 17);
2215 let proposal2 = sample_proposal(Epoch::new(0), View::new(34), 18);
2216
2217 let votes1: Vec<_> = schemes
2218 .iter()
2219 .take(quorum)
2220 .map(|scheme| {
2221 scheme
2222 .sign(Subject::Notarize {
2223 proposal: &proposal1,
2224 })
2225 .unwrap()
2226 })
2227 .collect();
2228 let votes2: Vec<_> = schemes
2229 .iter()
2230 .take(quorum)
2231 .map(|scheme| {
2232 scheme
2233 .sign(Subject::Notarize {
2234 proposal: &proposal2,
2235 })
2236 .unwrap()
2237 })
2238 .collect();
2239
2240 let certificate1 = schemes[0]
2241 .assemble(non_empty![@votes1], &Sequential)
2242 .expect("assemble certificate1");
2243 let certificate2 = schemes[0]
2244 .assemble(non_empty![@votes2], &Sequential)
2245 .expect("assemble certificate2");
2246
2247 assert!(verifier.verify_certificates::<_, Sha256Digest, _>(
2248 &mut rng,
2249 non_empty![
2250 (
2251 Subject::Notarize {
2252 proposal: &proposal1,
2253 },
2254 &certificate1
2255 ),
2256 (
2257 Subject::Notarize {
2258 proposal: &proposal2,
2259 },
2260 &certificate2
2261 ),
2262 ],
2263 &Sequential,
2264 ));
2265
2266 let cert1 = certificate1.get().unwrap();
2267 let cert2 = certificate2.get().unwrap();
2268 let random_scalar = Scalar::random(&mut rng);
2269 let delta = V::Signature::generator() * &random_scalar;
2270 let forged_certificate1: Certificate<V> = Signature {
2271 vote_signature: cert1.vote_signature - &delta,
2272 seed_signature: cert1.seed_signature,
2273 }
2274 .into();
2275 let forged_certificate2: Certificate<V> = Signature {
2276 vote_signature: cert2.vote_signature + &delta,
2277 seed_signature: cert2.seed_signature,
2278 }
2279 .into();
2280
2281 let forged1 = forged_certificate1.get().unwrap();
2282 let forged2 = forged_certificate2.get().unwrap();
2283 let forged_vote_sum = forged1.vote_signature + &forged2.vote_signature;
2284 let valid_vote_sum = cert1.vote_signature + &cert2.vote_signature;
2285 assert_eq!(
2286 forged_vote_sum, valid_vote_sum,
2287 "vote signature sums should be equal"
2288 );
2289
2290 assert!(
2291 !verifier.verify_certificates::<_, Sha256Digest, _>(
2292 &mut rng,
2293 non_empty![
2294 (
2295 Subject::Notarize {
2296 proposal: &proposal1,
2297 },
2298 &forged_certificate1
2299 ),
2300 (
2301 Subject::Notarize {
2302 proposal: &proposal2,
2303 },
2304 &forged_certificate2
2305 ),
2306 ],
2307 &Sequential,
2308 ),
2309 "forged certificates should be rejected"
2310 );
2311 }
2312
2313 #[test]
2314 fn test_verify_certificates_rejects_malleability() {
2315 verify_certificates_rejects_malleability::<MinPk>();
2316 verify_certificates_rejects_malleability::<MinSig>();
2317 }
2318
2319 fn assemble_notarization_certificate<V: Variant>(
2320 schemes: &[Scheme<V>],
2321 proposal: &Proposal<Sha256Digest>,
2322 ) -> Certificate<V> {
2323 let quorum = N3f1::quorum(schemes.len()) as usize;
2324 let votes: Vec<_> = schemes
2325 .iter()
2326 .take(quorum)
2327 .map(|scheme| scheme.sign(Subject::Notarize { proposal }).unwrap())
2328 .collect();
2329
2330 schemes[0]
2331 .assemble(non_empty![@votes], &Sequential)
2332 .expect("assemble notarization certificate")
2333 }
2334
2335 fn assemble_finalization_certificate<V: Variant>(
2336 schemes: &[Scheme<V>],
2337 proposal: &Proposal<Sha256Digest>,
2338 ) -> Certificate<V> {
2339 let quorum = N3f1::quorum(schemes.len()) as usize;
2340 let votes: Vec<_> = schemes
2341 .iter()
2342 .skip(schemes.len() - quorum)
2343 .map(|scheme| scheme.sign(Subject::Finalize { proposal }).unwrap())
2344 .collect();
2345
2346 schemes[0]
2347 .assemble(non_empty![@votes], &Sequential)
2348 .expect("assemble finalization certificate")
2349 }
2350
2351 fn verify_certificates_accepts_shared_round_seed<V: Variant>() {
2352 let mut rng = test_rng();
2353 let (schemes, verifier) = setup_signers::<V>(4, 81);
2354 let proposal = sample_proposal(Epoch::new(1), View::new(35), 19);
2355 let notarization_certificate = assemble_notarization_certificate(&schemes, &proposal);
2356 let finalization_certificate = assemble_finalization_certificate(&schemes, &proposal);
2357
2358 assert!(verifier.verify_certificates::<_, Sha256Digest, _>(
2359 &mut rng,
2360 non_empty![
2361 (
2362 Subject::Notarize {
2363 proposal: &proposal,
2364 },
2365 ¬arization_certificate,
2366 ),
2367 (
2368 Subject::Finalize {
2369 proposal: &proposal,
2370 },
2371 &finalization_certificate,
2372 ),
2373 ],
2374 &Sequential,
2375 ));
2376 }
2377
2378 #[test]
2379 fn test_verify_certificates_accepts_shared_round_seed() {
2380 verify_certificates_accepts_shared_round_seed::<MinPk>();
2381 verify_certificates_accepts_shared_round_seed::<MinSig>();
2382 }
2383
2384 fn verify_certificates_rejects_cross_epoch_seed_replay<V: Variant>() {
2385 let mut rng = test_rng();
2386 let (schemes, verifier) = setup_signers::<V>(4, 83);
2387 let view = View::new(35);
2388 let proposal1 = sample_proposal(Epoch::new(1), view, 19);
2389 let proposal2 = sample_proposal(Epoch::new(2), view, 20);
2390 let certificate1 = assemble_notarization_certificate(&schemes, &proposal1);
2391 let certificate2 = assemble_notarization_certificate(&schemes, &proposal2);
2392
2393 assert!(verifier.verify_certificates::<_, Sha256Digest, _>(
2394 &mut rng,
2395 non_empty![
2396 (
2397 Subject::Notarize {
2398 proposal: &proposal1,
2399 },
2400 &certificate1,
2401 ),
2402 (
2403 Subject::Notarize {
2404 proposal: &proposal2,
2405 },
2406 &certificate2,
2407 ),
2408 ],
2409 &Sequential,
2410 ));
2411
2412 let cert1 = certificate1.get().unwrap();
2413 let cert2 = certificate2.get().unwrap();
2414 let forged_certificate2: Certificate<V> = Signature {
2415 vote_signature: cert2.vote_signature,
2416 seed_signature: cert1.seed_signature,
2417 }
2418 .into();
2419
2420 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
2421 &mut rng,
2422 Subject::Notarize {
2423 proposal: &proposal2,
2424 },
2425 &forged_certificate2,
2426 &Sequential,
2427 ));
2428
2429 let batch = [
2430 (
2431 Subject::Notarize {
2432 proposal: &proposal1,
2433 },
2434 &certificate1,
2435 ),
2436 (
2437 Subject::Notarize {
2438 proposal: &proposal2,
2439 },
2440 &forged_certificate2,
2441 ),
2442 ];
2443
2444 assert!(!verifier.verify_certificates::<_, Sha256Digest, _>(
2445 &mut rng,
2446 non_empty![@batch.iter().copied()],
2447 &Sequential,
2448 ));
2449 assert_eq!(
2450 verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &batch, &Sequential,),
2451 vec![true, false],
2452 );
2453 }
2454
2455 #[test]
2456 fn test_verify_certificates_rejects_cross_epoch_seed_replay() {
2457 verify_certificates_rejects_cross_epoch_seed_replay::<MinPk>();
2458 verify_certificates_rejects_cross_epoch_seed_replay::<MinSig>();
2459 }
2460
2461 #[cfg(feature = "arbitrary")]
2462 mod conformance {
2463 use super::*;
2464 use commonware_codec::conformance::CodecConformance;
2465
2466 commonware_conformance::conformance_tests! {
2467 CodecConformance<Signature<MinSig>>,
2468 CodecConformance<Certificate<MinSig>>,
2469 CodecConformance<Seed<MinSig>>,
2470 }
2471 }
2472}