1#[cfg(feature = "mocks")]
7pub mod mocks;
8
9use super::{PrivateKey, PublicKey, Signature as Ed25519Signature};
10use crate::{
11 BatchEntry, BatchVerifier as _, Signer as _, Verifier as _,
12 certificate::{AssemblyError, Attestation, Namespace, Scheme, Signers, Subject, Verification},
13};
14#[cfg(not(feature = "std"))]
15use alloc::{collections::BTreeSet, vec::Vec};
16use bytes::{BufMut, Bytes};
17use commonware_codec::{
18 Buf, EncodeSize, Error, FixedSize, Read, ReadRangeExt, Write, types::lazy::Lazy,
19};
20use commonware_parallel::Strategy;
21use commonware_utils::{
22 Participant, Widen,
23 iter::NonEmpty,
24 ordered::{Quorum, Set},
25};
26use rand_core::CryptoRng;
27#[cfg(feature = "std")]
28use std::collections::BTreeSet;
29
30#[derive(Clone, Debug)]
36pub struct Generic<N: Namespace> {
37 pub participants: Set<PublicKey>,
39 pub signer: Option<(Participant, PrivateKey)>,
41 pub namespace: N,
43}
44
45impl<N: Namespace> Generic<N> {
46 pub fn signer(
48 namespace: &[u8],
49 participants: Set<PublicKey>,
50 private_key: PrivateKey,
51 ) -> Option<Self> {
52 let signer = participants
53 .index(&private_key.public_key())
54 .map(|index| (index, private_key))?;
55
56 Some(Self {
57 participants,
58 signer: Some(signer),
59 namespace: N::derive(namespace),
60 })
61 }
62
63 pub fn verifier(namespace: &[u8], participants: Set<PublicKey>) -> Self {
65 Self {
66 participants,
67 signer: None,
68 namespace: N::derive(namespace),
69 }
70 }
71
72 pub fn signer_index(&self) -> Option<Participant> {
74 self.signer.as_ref().map(|(index, _)| *index)
75 }
76
77 pub fn sign<S>(&self, subject: impl Subject<Namespace = N>) -> Option<Attestation<S>>
79 where
80 S: Scheme<Signature = Ed25519Signature>,
81 {
82 let (index, private_key) = self.signer.as_ref()?;
83
84 let signature = private_key.sign(subject.namespace(&self.namespace), &subject.message());
85
86 Some(Attestation {
87 signer: *index,
88 signature: signature.into(),
89 })
90 }
91
92 pub fn verify_attestation<S>(
94 &self,
95 subject: impl Subject<Namespace = N>,
96 attestation: &Attestation<S>,
97 ) -> bool
98 where
99 S: Scheme<Signature = Ed25519Signature>,
100 {
101 let Some(public_key) = self.participants.key(attestation.signer) else {
102 return false;
103 };
104 let Some(signature) = attestation.signature.get() else {
105 return false;
106 };
107
108 public_key.verify(
109 subject.namespace(&self.namespace),
110 &subject.message(),
111 signature,
112 )
113 }
114
115 pub fn verify_attestations<S, R, I>(
117 &self,
118 rng: &mut R,
119 subject: impl Subject<Namespace = N>,
120 attestations: I,
121 strategy: &impl Strategy,
122 ) -> Verification<S>
123 where
124 S: Scheme<Signature = Ed25519Signature>,
125 R: CryptoRng,
126 I: IntoIterator<Item = Attestation<S>>,
127 {
128 let namespace = subject.namespace(&self.namespace);
129 let message = subject.message();
130
131 let attestations = attestations.into_iter();
132 let mut invalid = BTreeSet::new();
133 let mut candidates = Vec::with_capacity(attestations.size_hint().0);
134
135 for attestation in attestations {
136 let Some(public_key) = self.participants.key(attestation.signer) else {
137 invalid.insert(attestation.signer);
138 continue;
139 };
140 if attestation.signature.get().is_none() {
141 invalid.insert(attestation.signer);
142 continue;
143 }
144 candidates.push((attestation, public_key));
145 }
146
147 if !candidates.is_empty()
148 && !PublicKey::verify_batch(
149 rng,
150 &candidates,
151 |_, (attestation, public_key)| BatchEntry {
152 namespace,
153 message: &message,
154 public_key,
155 signature: attestation.signature.get().expect("validated signature"),
156 },
157 strategy,
158 )
159 {
160 for (attestation, public_key) in &candidates {
162 let signature = attestation.signature.get().expect("validated signature");
163 if !public_key.verify(namespace, &message, signature) {
164 invalid.insert(attestation.signer);
165 }
166 }
167 }
168
169 let verified = candidates
170 .into_iter()
171 .filter(|(attestation, _)| !invalid.contains(&attestation.signer))
172 .map(|(attestation, _)| attestation)
173 .collect();
174
175 Verification::new(verified, invalid.into_iter().collect())
176 }
177
178 pub fn assemble<S, I>(&self, attestations: NonEmpty<I>) -> Result<Certificate, AssemblyError>
180 where
181 S: Scheme<Signature = Ed25519Signature>,
182 I: Iterator<Item = Attestation<S>>,
183 {
184 let mut entries = Vec::new();
186 for Attestation { signer, signature } in attestations {
187 self.participants
188 .key(signer)
189 .ok_or(AssemblyError::UnknownSigner(signer))?;
190 let signature = signature
191 .get()
192 .cloned()
193 .ok_or(AssemblyError::MalformedSignature(signer))?;
194 entries.push((signer, signature));
195 }
196
197 entries.sort_by_key(|(signer, _)| *signer);
199 let (signer, signatures): (Vec<Participant>, Vec<_>) = entries.into_iter().unzip();
200 let signers = Signers::try_from((&self.participants, signer))?
201 .require(self.participants.quorum::<S::Faults>())?;
202 let signatures = signatures.into_iter().map(Lazy::from).collect();
203
204 Ok(Certificate {
205 signers,
206 signatures,
207 })
208 }
209
210 fn stage_certificate<'a, S: Scheme>(
215 &'a self,
216 subject: impl Subject<Namespace = N>,
217 certificate: &'a Certificate,
218 mut stage: impl FnMut(&'a PublicKey, &'a Ed25519Signature),
219 ) -> Option<(&'a [u8], Bytes)> {
220 if certificate.signers.len() != self.participants.len()
221 || certificate.signers.count() != certificate.signatures.len()
222 || certificate.signers.count() < Widen::widen(self.participants.quorum::<S::Faults>())
223 {
224 return None;
225 }
226
227 for (signer, signature) in certificate.signers.iter().zip(&certificate.signatures) {
228 stage(self.participants.key(signer)?, signature.get()?);
229 }
230
231 Some((subject.namespace(&self.namespace), subject.message()))
232 }
233
234 pub fn verify_certificate<S, R>(
236 &self,
237 rng: &mut R,
238 subject: impl Subject<Namespace = N>,
239 certificate: &Certificate,
240 strategy: &impl Strategy,
241 ) -> bool
242 where
243 S: Scheme,
244 R: CryptoRng,
245 {
246 let mut entries = Vec::with_capacity(certificate.signatures.len());
247 let Some((namespace, message)) =
248 self.stage_certificate::<S>(subject, certificate, |public_key, signature| {
249 entries.push((public_key, signature));
250 })
251 else {
252 return false;
253 };
254
255 PublicKey::verify_batch(
256 rng,
257 &entries,
258 |_, (public_key, signature)| BatchEntry {
259 namespace,
260 message: &message,
261 public_key,
262 signature,
263 },
264 strategy,
265 )
266 }
267
268 pub fn verify_certificates<'a, S, R, T, I>(
270 &self,
271 rng: &mut R,
272 certificates: NonEmpty<I>,
273 strategy: &impl Strategy,
274 ) -> bool
275 where
276 S: Scheme,
277 T: Subject<Namespace = N>,
278 R: CryptoRng,
279 I: Iterator<Item = (T, &'a Certificate)>,
280 {
281 let per_certificate = self.participants.len();
283 let certificates = certificates.into_iter();
284 let mut messages = Vec::with_capacity(certificates.size_hint().0);
285 let mut entries =
286 Vec::with_capacity(certificates.size_hint().0.saturating_mul(per_certificate));
287 for (subject, certificate) in certificates {
288 let index = messages.len();
289 let Some(message) =
290 self.stage_certificate::<S>(subject, certificate, |public_key, signature| {
291 entries.push((index, public_key, signature));
292 })
293 else {
294 return false;
295 };
296 messages.push(message);
297 }
298
299 PublicKey::verify_batch(
300 rng,
301 &entries,
302 |_, (index, public_key, signature)| BatchEntry {
303 namespace: messages[*index].0,
304 message: &messages[*index].1,
305 public_key,
306 signature,
307 },
308 strategy,
309 )
310 }
311
312 pub const fn is_attributable() -> bool {
313 true
314 }
315
316 pub const fn is_batchable() -> bool {
317 true
318 }
319
320 pub fn max_certificate_size(participants: u32) -> usize {
323 crate::certificate::max_individual_certificate_size(participants, Ed25519Signature::SIZE)
324 }
325
326 pub const fn certificate_codec_config(&self) -> <Certificate as commonware_codec::Read>::Cfg {
327 self.participants.len()
328 }
329
330 pub const fn certificate_codec_config_unbounded() -> <Certificate as commonware_codec::Read>::Cfg
331 {
332 u32::MAX as usize
333 }
334}
335
336#[derive(Clone, Debug, PartialEq, Eq, Hash)]
337pub struct Certificate {
338 pub signers: Signers,
340 pub signatures: Vec<Lazy<Ed25519Signature>>,
342}
343
344#[cfg(feature = "arbitrary")]
345impl arbitrary::Arbitrary<'_> for Certificate {
346 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
347 let signers = Signers::arbitrary(u)?;
348 let signatures = (0..signers.count())
349 .map(|_| u.arbitrary::<Ed25519Signature>().map(Lazy::from))
350 .collect::<arbitrary::Result<Vec<_>>>()?;
351 Ok(Self {
352 signers,
353 signatures,
354 })
355 }
356}
357
358impl Write for Certificate {
359 fn write(&self, writer: &mut impl BufMut) {
360 self.signers.write(writer);
361 self.signatures.write(writer);
362 }
363}
364
365impl EncodeSize for Certificate {
366 fn encode_size(&self) -> usize {
367 self.signers.encode_size() + self.signatures.encode_size()
368 }
369}
370
371impl Read for Certificate {
372 type Cfg = usize;
373
374 fn read_cfg(reader: &mut impl Buf, participants: &usize) -> Result<Self, Error> {
375 let signers = Signers::read_cfg(reader, participants)?;
376 if signers.count() == 0 {
377 return Err(Error::Invalid(
378 "cryptography::ed25519::certificate::Certificate",
379 "Certificate contains no signers",
380 ));
381 }
382
383 let signatures = Vec::<Lazy<Ed25519Signature>>::read_range(reader, ..=*participants)?;
384 if signers.count() != signatures.len() {
385 return Err(Error::Invalid(
386 "cryptography::ed25519::certificate::Certificate",
387 "Signers and signatures counts differ",
388 ));
389 }
390
391 Ok(Self {
392 signers,
393 signatures,
394 })
395 }
396}
397
398#[macro_export]
427macro_rules! impl_certificate_ed25519 {
428 ($subject:ty, $namespace:ty, $faults:ty) => {
429 #[cfg(feature = "mocks")]
434 #[allow(dead_code)]
435 pub fn fixture<R>(
436 rng: &mut R,
437 namespace: &[u8],
438 n: u32,
439 ) -> $crate::certificate::mocks::Fixture<Scheme>
440 where
441 R: rand_core::CryptoRng,
442 {
443 $crate::ed25519::certificate::mocks::fixture(
444 rng,
445 namespace,
446 n,
447 Scheme::signer,
448 Scheme::verifier,
449 )
450 }
451
452 #[derive(Clone, Debug)]
454 pub struct Scheme {
455 generic: $crate::ed25519::certificate::Generic<$namespace>,
457 member: Option<commonware_utils::Participant>,
459 }
460
461 impl Scheme {
462 pub fn signer(
472 namespace: &[u8],
473 participants: commonware_utils::ordered::Set<$crate::ed25519::PublicKey>,
474 private_key: $crate::ed25519::PrivateKey,
475 ) -> Option<Self> {
476 Some(Self {
477 generic: $crate::ed25519::certificate::Generic::signer(
478 namespace,
479 participants,
480 private_key,
481 )?,
482 member: None,
483 })
484 }
485
486 pub fn verifier(
493 namespace: &[u8],
494 participants: commonware_utils::ordered::Set<$crate::ed25519::PublicKey>,
495 ) -> Self {
496 Self {
497 generic: $crate::ed25519::certificate::Generic::verifier(
498 namespace,
499 participants,
500 ),
501 member: None,
502 }
503 }
504
505 pub fn member(
515 namespace: &[u8],
516 participants: commonware_utils::ordered::Set<$crate::ed25519::PublicKey>,
517 public_key: &$crate::ed25519::PublicKey,
518 ) -> Option<Self> {
519 let member = commonware_utils::ordered::Quorum::index(&participants, public_key)?;
520 Some(Self {
521 generic: $crate::ed25519::certificate::Generic::verifier(
522 namespace,
523 participants,
524 ),
525 member: Some(member),
526 })
527 }
528 }
529
530 impl $crate::certificate::Verifier for Scheme {
531 type Subject<'a, D: $crate::Digest> = $subject;
532 type Faults = $faults;
533 type PublicKey = $crate::ed25519::PublicKey;
534 type Certificate = $crate::ed25519::certificate::Certificate;
535
536 fn verify_certificate<R, D>(
537 &self,
538 rng: &mut R,
539 subject: Self::Subject<'_, D>,
540 certificate: &Self::Certificate,
541 strategy: &impl commonware_parallel::Strategy,
542 ) -> bool
543 where
544 R: rand_core::CryptoRng,
545 D: $crate::Digest,
546 {
547 self.generic
548 .verify_certificate::<Self, _>(rng, subject, certificate, strategy)
549 }
550
551 fn verify_certificates<'a, R, D, I>(
552 &self,
553 rng: &mut R,
554 certificates: commonware_utils::iter::NonEmpty<I>,
555 strategy: &impl commonware_parallel::Strategy,
556 ) -> bool
557 where
558 R: rand_core::CryptoRng,
559 D: $crate::Digest,
560 I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
561 {
562 self.generic
563 .verify_certificates::<Self, _, _, _>(rng, certificates, strategy)
564 }
565
566 fn is_batchable() -> bool {
567 $crate::ed25519::certificate::Generic::<$namespace>::is_batchable()
568 }
569
570 fn max_certificate_size(participants: u32) -> usize {
571 $crate::ed25519::certificate::Generic::<$namespace>::max_certificate_size(participants)
572 }
573
574 fn certificate_codec_config(
575 &self,
576 ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
577 self.generic.certificate_codec_config()
578 }
579
580 fn certificate_codec_config_unbounded(
581 ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
582 $crate::ed25519::certificate::Generic::<$namespace>::certificate_codec_config_unbounded()
583 }
584 }
585
586 impl $crate::certificate::Scheme for Scheme {
587 type Signature = $crate::ed25519::Signature;
588
589 fn me(&self) -> Option<$crate::certificate::Membership> {
590 self.generic
591 .signer_index()
592 .map($crate::certificate::Membership::Signer)
593 .or(self.member.map($crate::certificate::Membership::Member))
594 }
595
596 fn participants(&self) -> &commonware_utils::ordered::Set<Self::PublicKey> {
597 &self.generic.participants
598 }
599
600 fn sign<D: $crate::Digest>(
601 &self,
602 subject: Self::Subject<'_, D>,
603 ) -> Option<$crate::certificate::Attestation<Self>> {
604 self.generic.sign(subject)
605 }
606
607 fn verify_attestation<R, D>(
608 &self,
609 _rng: &mut R,
610 subject: Self::Subject<'_, D>,
611 attestation: &$crate::certificate::Attestation<Self>,
612 _strategy: &impl commonware_parallel::Strategy,
613 ) -> bool
614 where
615 R: rand_core::CryptoRng,
616 D: $crate::Digest,
617 {
618 self.generic
619 .verify_attestation(subject, attestation)
620 }
621
622 fn verify_attestations<R, D, I>(
623 &self,
624 rng: &mut R,
625 subject: Self::Subject<'_, D>,
626 attestations: I,
627 strategy: &impl commonware_parallel::Strategy,
628 ) -> $crate::certificate::Verification<Self>
629 where
630 R: rand_core::CryptoRng,
631 D: $crate::Digest,
632 I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
633 {
634 self.generic
635 .verify_attestations(rng, subject, attestations, strategy)
636 }
637
638 fn optimistic_assemble<'a, R, D, I, J>(
639 &self,
640 rng: &mut R,
641 subject: Self::Subject<'_, D>,
642 pending: I,
643 verified: J,
644 strategy: &impl commonware_parallel::Strategy,
645 ) -> Result<Self::Certificate, $crate::certificate::Verification<Self>>
646 where
647 R: rand_core::CryptoRng,
648 D: $crate::Digest,
649 I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
650 I::IntoIter: ExactSizeIterator + Send,
651 J: IntoIterator<Item = &'a $crate::certificate::Attestation<Self>>,
652 J::IntoIter: Send,
653 {
654 $crate::certificate::verify_then_assemble::<Self, _, D, _, _>(
655 self, rng, subject, pending, verified, strategy,
656 )
657 }
658
659 fn assemble<I>(
660 &self,
661 attestations: commonware_utils::iter::NonEmpty<I>,
662 _strategy: &impl commonware_parallel::Strategy,
663 ) -> Result<Self::Certificate, $crate::certificate::AssemblyError>
664 where
665 I: Iterator<Item = $crate::certificate::Attestation<Self>> + Send,
666 {
667 self.generic.assemble::<Self, _>(attestations)
668 }
669
670 fn is_attributable() -> bool {
671 $crate::ed25519::certificate::Generic::<$namespace>::is_attributable()
672 }
673 }
674 };
675}
676
677#[cfg(test)]
678mod tests {
679 use super::*;
680 use crate::{
681 certificate::{Membership, Scheme as _, Verifier as _},
682 sha256::Digest as Sha256Digest,
683 };
684 use bytes::Bytes;
685 use commonware_codec::{Decode, Encode, varint::MAX_U32_VARINT_SIZE};
686 use commonware_math::algebra::Random;
687 use commonware_parallel::{Rayon, Sequential};
688 use commonware_utils::{
689 Faults, N3f1, NZUsize, Participant, TestRng, TryCollect, non_empty, ordered::Set, test_rng,
690 };
691 use rand_core::Rng;
692
693 const NAMESPACE: &[u8] = b"test-ed25519";
694 const MESSAGE: &[u8] = b"test message";
695
696 #[derive(Clone, Debug)]
698 pub struct TestSubject {
699 pub message: Bytes,
700 }
701
702 impl Subject for TestSubject {
703 type Namespace = Vec<u8>;
704
705 fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] {
706 derived.as_ref()
707 }
708
709 fn message(&self) -> Bytes {
710 self.message.clone()
711 }
712 }
713
714 impl_certificate_ed25519!(TestSubject, Vec<u8>, N3f1);
716
717 fn setup_signers(rng: &mut impl CryptoRng, n: u32) -> (Vec<Scheme>, Scheme) {
718 let private_keys: Vec<_> = (0..n).map(|_| PrivateKey::random(&mut *rng)).collect();
719 let participants: Set<PublicKey> = private_keys
720 .iter()
721 .map(|sk| sk.public_key())
722 .try_collect()
723 .unwrap();
724
725 let signers = private_keys
726 .into_iter()
727 .map(|sk| Scheme::signer(NAMESPACE, participants.clone(), sk).unwrap())
728 .collect();
729
730 let verifier = Scheme::verifier(NAMESPACE, participants);
731
732 (signers, verifier)
733 }
734
735 #[test]
736 fn test_is_attributable() {
737 assert!(Generic::<Vec<u8>>::is_attributable());
738 assert!(Scheme::is_attributable());
739 }
740
741 #[test]
742 fn test_is_batchable() {
743 assert!(Generic::<Vec<u8>>::is_batchable());
744 assert!(Scheme::is_batchable());
745 }
746
747 #[test]
748 fn test_optimistic_assemble_reuses_verified_attestations() {
749 let mut rng = test_rng();
750 let (schemes, verifier) = setup_signers(&mut rng, 5);
751 let quorum = N3f1::quorum(schemes.len()) as usize;
752 let subject = TestSubject {
753 message: Bytes::from_static(MESSAGE),
754 };
755 let attestations: Vec<_> = schemes
756 .iter()
757 .take(quorum)
758 .map(|scheme| scheme.sign::<Sha256Digest>(subject.clone()).unwrap())
759 .collect();
760
761 let strategy = Rayon::new(NZUsize!(1))
764 .unwrap()
765 .with_parallelism(NZUsize!(quorum));
766 for pending_len in [quorum, 1, 0] {
767 let (prior, pending) = attestations.split_at(quorum - pending_len);
768 let verified = verifier.verify_attestations::<_, Sha256Digest, _>(
769 &mut rng,
770 subject.clone(),
771 prior.iter().cloned(),
772 &Sequential,
773 );
774 assert!(verified.invalid.is_empty());
775
776 let mut expected_rng = TestRng::new(0);
779 let expected = verifier.verify_attestations::<_, Sha256Digest, _>(
780 &mut expected_rng,
781 subject.clone(),
782 pending.iter().cloned(),
783 &strategy,
784 );
785 assert!(expected.invalid.is_empty());
786 let mut actual_rng = TestRng::new(0);
787 let certificate = verifier
788 .optimistic_assemble::<_, Sha256Digest, _, _>(
789 &mut actual_rng,
790 subject.clone(),
791 pending.iter().cloned(),
792 &verified.verified,
793 &strategy,
794 )
795 .ok()
796 .expect("verified quorum must certify");
797 assert_eq!(
798 actual_rng.next_u64(),
799 expected_rng.next_u64(),
800 "retained votes must not enter the verification batch"
801 );
802 assert!(verifier.verify_certificate::<_, Sha256Digest>(
803 &mut rng,
804 subject.clone(),
805 &certificate,
806 &Sequential,
807 ));
808 }
809 }
810
811 #[test]
812 fn test_sign_vote_roundtrip() {
813 let mut rng = test_rng();
814 let (schemes, _) = setup_signers(&mut rng, 4);
815 let scheme = &schemes[0];
816
817 let attestation = scheme
818 .sign::<Sha256Digest>(TestSubject {
819 message: Bytes::from_static(MESSAGE),
820 })
821 .unwrap();
822 assert!(scheme.verify_attestation::<_, Sha256Digest>(
823 &mut rng,
824 TestSubject {
825 message: Bytes::from_static(MESSAGE),
826 },
827 &attestation,
828 &Sequential,
829 ));
830 }
831
832 #[test]
833 fn test_verifier_cannot_sign() {
834 let mut rng = test_rng();
835 let (_, verifier) = setup_signers(&mut rng, 4);
836 assert!(
837 verifier
838 .sign::<Sha256Digest>(TestSubject {
839 message: Bytes::from_static(MESSAGE)
840 })
841 .is_none()
842 );
843 }
844
845 #[test]
848 fn test_member_reports_membership_without_signing() {
849 let mut rng = test_rng();
850 let (schemes, verifier) = setup_signers(&mut rng, 4);
851 let participants = verifier.participants().clone();
852 let subject = TestSubject {
853 message: Bytes::from_static(MESSAGE),
854 };
855
856 let outsider = PrivateKey::random(&mut rng).public_key();
858 assert!(Scheme::member(NAMESPACE, participants.clone(), &outsider).is_none());
859
860 let index = Participant::new(2);
863 let key = participants.key(index).unwrap().clone();
864 let member = Scheme::member(NAMESPACE, participants, &key).unwrap();
865 assert_eq!(member.me(), Some(Membership::Member(index)));
866 assert_eq!(member.me().unwrap().signer(), None);
867 assert!(member.sign::<Sha256Digest>(subject.clone()).is_none());
868 assert_eq!(verifier.me(), None);
869 for scheme in &schemes {
870 let Some(Membership::Signer(signer)) = scheme.me() else {
871 panic!("signer must report signing membership");
872 };
873 let attestation = scheme.sign::<Sha256Digest>(subject.clone()).unwrap();
874 assert_eq!(attestation.signer, signer);
875 }
876
877 let attestations: Vec<_> = schemes
879 .iter()
880 .take(Widen::widen(N3f1::quorum(schemes.len())))
881 .map(|s| s.sign::<Sha256Digest>(subject.clone()).unwrap())
882 .collect();
883 let certificate = schemes[0]
884 .assemble(non_empty![@attestations], &Sequential)
885 .unwrap();
886 assert!(member.verify_certificate::<_, Sha256Digest>(
887 &mut rng,
888 subject,
889 &certificate,
890 &Sequential,
891 ));
892 }
893
894 #[test]
895 fn test_verify_attestations_filters_invalid() {
896 let mut rng = test_rng();
897 let (schemes, _) = setup_signers(&mut rng, 5);
898 let quorum = N3f1::quorum(schemes.len()) as usize;
899
900 let attestations: Vec<_> = schemes
901 .iter()
902 .take(quorum)
903 .map(|s| {
904 s.sign::<Sha256Digest>(TestSubject {
905 message: Bytes::from_static(MESSAGE),
906 })
907 .unwrap()
908 })
909 .collect();
910
911 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
912 &mut rng,
913 TestSubject {
914 message: Bytes::from_static(MESSAGE),
915 },
916 attestations.clone(),
917 &Sequential,
918 );
919 assert!(result.invalid.is_empty());
920 assert_eq!(result.verified.len(), quorum);
921
922 let mut attestations_corrupted = attestations.clone();
924 attestations_corrupted[0].signer = Participant::new(999);
925 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
926 &mut rng,
927 TestSubject {
928 message: Bytes::from_static(MESSAGE),
929 },
930 attestations_corrupted,
931 &Sequential,
932 );
933 assert_eq!(result.invalid, vec![Participant::new(999)]);
934 assert_eq!(result.verified.len(), quorum - 1);
935
936 let mut attestations_corrupted = attestations;
938 attestations_corrupted[0].signature = attestations_corrupted[1].signature.clone();
939 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
940 &mut rng,
941 TestSubject {
942 message: Bytes::from_static(MESSAGE),
943 },
944 attestations_corrupted,
945 &Sequential,
946 );
947 assert_eq!(result.invalid.len(), 1);
948 assert_eq!(result.verified.len(), quorum - 1);
949
950 let subject = TestSubject {
952 message: Bytes::from_static(MESSAGE),
953 };
954 let mut malformed = schemes[0].sign::<Sha256Digest>(subject.clone()).unwrap();
955 let mut truncated = Bytes::from_static(&[0u8; 3]);
956 malformed.signature = Lazy::deferred(&mut truncated, ());
957 let mut unknown = schemes[1].sign::<Sha256Digest>(subject.clone()).unwrap();
958 unknown.signer = Participant::new(999);
959
960 for attestations in [Vec::new(), vec![malformed, unknown]] {
962 let expected_invalid = attestations.len();
963 let mut actual_rng = TestRng::new(0);
964 let mut expected_rng = TestRng::new(0);
965 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
966 &mut actual_rng,
967 subject.clone(),
968 attestations,
969 &Sequential,
970 );
971 assert!(result.verified.is_empty());
972 assert_eq!(result.invalid.len(), expected_invalid);
973 assert_eq!(actual_rng.next_u64(), expected_rng.next_u64());
974 }
975 }
976
977 #[test]
978 fn test_assemble_certificate() {
979 let mut rng = test_rng();
980 let (schemes, _) = setup_signers(&mut rng, 4);
981 let quorum = N3f1::quorum(schemes.len()) as usize;
982
983 let attestations: Vec<_> = schemes
984 .iter()
985 .take(quorum)
986 .map(|s| {
987 s.sign::<Sha256Digest>(TestSubject {
988 message: Bytes::from_static(MESSAGE),
989 })
990 .unwrap()
991 })
992 .collect();
993
994 let certificate = schemes[0]
995 .assemble(non_empty![@attestations], &Sequential)
996 .unwrap();
997
998 assert_eq!(certificate.signers.count(), quorum);
1000 assert_eq!(certificate.signatures.len(), quorum);
1001 }
1002
1003 #[test]
1004 fn test_assemble_certificate_sorts_signers() {
1005 let mut rng = test_rng();
1006 let (schemes, _) = setup_signers(&mut rng, 4);
1007
1008 let mut indexed: Vec<_> = (0..3)
1010 .map(|i| (schemes[i].me().unwrap().index(), i))
1011 .collect();
1012 indexed.sort_by_key(|(idx, _)| *idx);
1013
1014 let attestations = vec![
1016 schemes[indexed[2].1]
1017 .sign::<Sha256Digest>(TestSubject {
1018 message: Bytes::from_static(MESSAGE),
1019 })
1020 .unwrap(),
1021 schemes[indexed[1].1]
1022 .sign::<Sha256Digest>(TestSubject {
1023 message: Bytes::from_static(MESSAGE),
1024 })
1025 .unwrap(),
1026 schemes[indexed[0].1]
1027 .sign::<Sha256Digest>(TestSubject {
1028 message: Bytes::from_static(MESSAGE),
1029 })
1030 .unwrap(),
1031 ];
1032
1033 let certificate = schemes[0]
1034 .assemble(non_empty![@attestations], &Sequential)
1035 .unwrap();
1036
1037 let expected: Vec<_> = indexed.iter().map(|(idx, _)| *idx).collect();
1039 assert_eq!(certificate.signers.iter().collect::<Vec<_>>(), expected);
1040 }
1041
1042 #[test]
1043 fn test_verify_certificate() {
1044 let mut rng = test_rng();
1045 let (schemes, verifier) = setup_signers(&mut rng, 4);
1046 let quorum = N3f1::quorum(schemes.len()) as usize;
1047
1048 let attestations: Vec<_> = schemes
1049 .iter()
1050 .take(quorum)
1051 .map(|s| {
1052 s.sign::<Sha256Digest>(TestSubject {
1053 message: Bytes::from_static(MESSAGE),
1054 })
1055 .unwrap()
1056 })
1057 .collect();
1058
1059 let certificate = schemes[0]
1060 .assemble(non_empty![@attestations], &Sequential)
1061 .unwrap();
1062
1063 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1064 &mut rng,
1065 TestSubject {
1066 message: Bytes::from_static(MESSAGE)
1067 },
1068 &certificate,
1069 &Sequential,
1070 ));
1071 }
1072
1073 #[test]
1074 fn test_verify_certificate_detects_corruption() {
1075 let mut rng = test_rng();
1076 let (schemes, verifier) = setup_signers(&mut rng, 4);
1077 let quorum = N3f1::quorum(schemes.len()) as usize;
1078
1079 let attestations: Vec<_> = schemes
1080 .iter()
1081 .take(quorum)
1082 .map(|s| {
1083 s.sign::<Sha256Digest>(TestSubject {
1084 message: Bytes::from_static(MESSAGE),
1085 })
1086 .unwrap()
1087 })
1088 .collect();
1089
1090 let certificate = schemes[0]
1091 .assemble(non_empty![@attestations], &Sequential)
1092 .unwrap();
1093
1094 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1096 &mut rng,
1097 TestSubject {
1098 message: Bytes::from_static(MESSAGE),
1099 },
1100 &certificate,
1101 &Sequential,
1102 ));
1103
1104 let mut corrupted = certificate;
1106 corrupted.signatures[0] = corrupted.signatures[1].clone();
1107 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1108 &mut rng,
1109 TestSubject {
1110 message: Bytes::from_static(MESSAGE),
1111 },
1112 &corrupted,
1113 &Sequential,
1114 ));
1115 }
1116
1117 #[test]
1118 fn test_certificate_codec_roundtrip() {
1119 let mut rng = test_rng();
1120 let (schemes, _) = setup_signers(&mut rng, 4);
1121 let quorum = N3f1::quorum(schemes.len()) as usize;
1122
1123 let attestations: Vec<_> = schemes
1124 .iter()
1125 .take(quorum)
1126 .map(|s| {
1127 s.sign::<Sha256Digest>(TestSubject {
1128 message: Bytes::from_static(MESSAGE),
1129 })
1130 .unwrap()
1131 })
1132 .collect();
1133
1134 let certificate = schemes[0]
1135 .assemble(non_empty![@attestations], &Sequential)
1136 .unwrap();
1137 let encoded = certificate.encode();
1138 assert!(
1139 encoded.len() <= Scheme::max_certificate_size(u32::try_from(schemes.len()).unwrap())
1140 );
1141 let decoded = Certificate::decode_cfg(encoded, &schemes.len()).expect("decode certificate");
1142 assert_eq!(decoded, certificate);
1143 }
1144
1145 #[test]
1146 fn test_max_certificate_size() {
1147 let mut rng = test_rng();
1149 let (schemes, _) = setup_signers(&mut rng, 128);
1150 let attestations = schemes.iter().map(|s| {
1151 s.sign::<Sha256Digest>(TestSubject {
1152 message: Bytes::from_static(MESSAGE),
1153 })
1154 .unwrap()
1155 });
1156 let certificate = schemes[0]
1157 .assemble(non_empty![@attestations], &Sequential)
1158 .unwrap();
1159 let prefix_slack = MAX_U32_VARINT_SIZE - schemes.len().encode_size();
1160 assert_eq!(
1161 certificate.encode().len() + prefix_slack,
1162 Scheme::max_certificate_size(u32::try_from(schemes.len()).unwrap())
1163 );
1164 }
1165
1166 #[test]
1167 fn test_certificate_rejects_sub_quorum() {
1168 let mut rng = test_rng();
1169 let (schemes, _) = setup_signers(&mut rng, 4);
1170 let expected = N3f1::quorum(schemes.len());
1171 let found = expected - 1;
1172 let found_count = usize::try_from(found).expect("quorum exceeds usize::MAX");
1173
1174 let attestations: Vec<_> = schemes
1175 .iter()
1176 .take(found_count)
1177 .map(|s| {
1178 s.sign::<Sha256Digest>(TestSubject {
1179 message: Bytes::from_static(MESSAGE),
1180 })
1181 .unwrap()
1182 })
1183 .collect();
1184
1185 assert_eq!(
1186 schemes[0].assemble(non_empty![@attestations], &Sequential),
1187 Err(AssemblyError::InsufficientAttestations(expected, found))
1188 );
1189 }
1190
1191 #[test]
1192 fn test_certificate_rejects_invalid_signer() {
1193 let mut rng = test_rng();
1194 let (schemes, _) = setup_signers(&mut rng, 4);
1195 let quorum =
1196 usize::try_from(N3f1::quorum(schemes.len())).expect("quorum exceeds usize::MAX");
1197
1198 let mut attestations: Vec<_> = schemes
1199 .iter()
1200 .take(quorum)
1201 .map(|s| {
1202 s.sign::<Sha256Digest>(TestSubject {
1203 message: Bytes::from_static(MESSAGE),
1204 })
1205 .unwrap()
1206 })
1207 .collect();
1208
1209 let unknown = Participant::new(999);
1211 attestations[0].signer = unknown;
1212
1213 assert_eq!(
1214 schemes[0].assemble(non_empty![@attestations], &Sequential),
1215 Err(AssemblyError::UnknownSigner(unknown))
1216 );
1217 }
1218
1219 #[test]
1220 fn test_certificate_rejects_malformed_signature() {
1221 let mut rng = test_rng();
1223 let (schemes, verifier) = setup_signers(&mut rng, 4);
1224 let quorum =
1225 usize::try_from(N3f1::quorum(schemes.len())).expect("quorum exceeds usize::MAX");
1226
1227 let mut attestations: Vec<_> = schemes
1228 .iter()
1229 .take(quorum)
1230 .map(|s| {
1231 s.sign::<Sha256Digest>(TestSubject {
1232 message: Bytes::from_static(MESSAGE),
1233 })
1234 .unwrap()
1235 })
1236 .collect();
1237
1238 let valid = schemes[0]
1239 .assemble(non_empty![@attestations.clone()], &Sequential)
1240 .unwrap();
1241
1242 let signer = attestations[0].signer;
1244 let mut truncated = Bytes::from_static(&[0u8; 3]);
1245 attestations[0].signature = Lazy::deferred(&mut truncated, ());
1246
1247 assert_eq!(
1248 schemes[0].assemble(non_empty![@attestations], &Sequential),
1249 Err(AssemblyError::MalformedSignature(signer))
1250 );
1251
1252 let subject = TestSubject {
1254 message: Bytes::from_static(MESSAGE),
1255 };
1256 let mut invalid = valid.clone();
1257 let mut truncated = Bytes::from_static(&[0u8; 3]);
1258 *invalid.signatures.last_mut().unwrap() = Lazy::deferred(&mut truncated, ());
1259 let mut actual_rng = TestRng::new(0);
1260 let mut expected_rng = TestRng::new(0);
1261 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1262 &mut actual_rng,
1263 subject.clone(),
1264 &invalid,
1265 &Sequential,
1266 ));
1267 assert_eq!(actual_rng.next_u64(), expected_rng.next_u64());
1268
1269 let mut actual_rng = TestRng::new(0);
1271 let mut expected_rng = TestRng::new(0);
1272 assert!(!verifier.verify_certificates::<_, Sha256Digest, _>(
1273 &mut actual_rng,
1274 non_empty![(subject.clone(), &valid), (subject, &invalid)],
1275 &Sequential,
1276 ));
1277 assert_eq!(actual_rng.next_u64(), expected_rng.next_u64());
1278 }
1279
1280 #[test]
1281 fn test_verify_certificate_rejects_sub_quorum() {
1282 let mut rng = test_rng();
1283 let (schemes, verifier) = setup_signers(&mut rng, 4);
1284 let participants_len = schemes.len();
1285
1286 let attestations: Vec<_> = schemes
1287 .iter()
1288 .take(3)
1289 .map(|s| {
1290 s.sign::<Sha256Digest>(TestSubject {
1291 message: Bytes::from_static(MESSAGE),
1292 })
1293 .unwrap()
1294 })
1295 .collect();
1296
1297 let mut certificate = schemes[0]
1298 .assemble(non_empty![@attestations], &Sequential)
1299 .unwrap();
1300
1301 let mut signers: Vec<Participant> = certificate.signers.iter().collect();
1303 signers.pop();
1304 certificate.signers = Signers::new(participants_len.try_into().unwrap(), signers).unwrap();
1305 certificate.signatures.pop();
1306
1307 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1308 &mut rng,
1309 TestSubject {
1310 message: Bytes::from_static(MESSAGE),
1311 },
1312 &certificate,
1313 &Sequential,
1314 ));
1315 }
1316
1317 #[test]
1318 fn test_verify_certificate_rejects_mismatched_signature_count() {
1319 let mut rng = test_rng();
1320 let (schemes, verifier) = setup_signers(&mut rng, 4);
1321
1322 let attestations: Vec<_> = schemes
1323 .iter()
1324 .take(3)
1325 .map(|s| {
1326 s.sign::<Sha256Digest>(TestSubject {
1327 message: Bytes::from_static(MESSAGE),
1328 })
1329 .unwrap()
1330 })
1331 .collect();
1332
1333 let mut certificate = schemes[0]
1334 .assemble(non_empty![@attestations], &Sequential)
1335 .unwrap();
1336
1337 certificate.signatures.pop();
1339
1340 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1341 &mut rng,
1342 TestSubject {
1343 message: Bytes::from_static(MESSAGE),
1344 },
1345 &certificate,
1346 &Sequential,
1347 ));
1348 }
1349
1350 #[test]
1351 fn test_verify_certificates_batch() {
1352 let mut rng = test_rng();
1353 let (schemes, verifier) = setup_signers(&mut rng, 4);
1354 let quorum = N3f1::quorum(schemes.len()) as usize;
1355
1356 let messages: Vec<Bytes> = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()]
1357 .into_iter()
1358 .map(Bytes::from_static)
1359 .collect();
1360 let mut certificates = Vec::new();
1361
1362 for msg in &messages {
1363 let attestations: Vec<_> = schemes
1364 .iter()
1365 .take(quorum)
1366 .map(|s| {
1367 s.sign::<Sha256Digest>(TestSubject {
1368 message: msg.clone(),
1369 })
1370 .unwrap()
1371 })
1372 .collect();
1373 certificates.push(
1374 schemes[0]
1375 .assemble(non_empty![@attestations], &Sequential)
1376 .unwrap(),
1377 );
1378 }
1379
1380 let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
1383 (
1384 TestSubject {
1385 message: msg.clone(),
1386 },
1387 cert,
1388 )
1389 });
1390
1391 let mut actual_rng = TestRng::new(0);
1392 let mut expected_rng = TestRng::new(0);
1393 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1394 &mut expected_rng,
1395 TestSubject {
1396 message: messages[0].clone(),
1397 },
1398 &certificates[0],
1399 &Sequential,
1400 ));
1401 assert!(verifier.verify_certificates::<_, Sha256Digest, _>(
1402 &mut actual_rng,
1403 non_empty![@certs_iter],
1404 &Sequential
1405 ));
1406 assert_eq!(actual_rng.next_u64(), expected_rng.next_u64());
1407 }
1408
1409 #[test]
1410 fn test_verify_certificates_batch_detects_failure() {
1411 let mut rng = test_rng();
1412 let (schemes, verifier) = setup_signers(&mut rng, 4);
1413 let quorum = N3f1::quorum(schemes.len()) as usize;
1414
1415 let messages: Vec<Bytes> = [b"msg1".as_slice(), b"msg2".as_slice()]
1416 .into_iter()
1417 .map(Bytes::from_static)
1418 .collect();
1419 let mut certificates = Vec::new();
1420
1421 for msg in &messages {
1422 let attestations: Vec<_> = schemes
1423 .iter()
1424 .take(quorum)
1425 .map(|s| {
1426 s.sign::<Sha256Digest>(TestSubject {
1427 message: msg.clone(),
1428 })
1429 .unwrap()
1430 })
1431 .collect();
1432 certificates.push(
1433 schemes[0]
1434 .assemble(non_empty![@attestations], &Sequential)
1435 .unwrap(),
1436 );
1437 }
1438
1439 certificates[1].signatures[0] = certificates[1].signatures[1].clone();
1441
1442 let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
1443 (
1444 TestSubject {
1445 message: msg.clone(),
1446 },
1447 cert,
1448 )
1449 });
1450
1451 assert!(!verifier.verify_certificates::<_, Sha256Digest, _>(
1452 &mut rng,
1453 non_empty![@certs_iter],
1454 &Sequential
1455 ));
1456 }
1457
1458 #[test]
1459 fn test_assemble_certificate_rejects_duplicate_signers() {
1460 let mut rng = test_rng();
1461 let (schemes, _) = setup_signers(&mut rng, 4);
1462
1463 let mut attestations: Vec<_> = schemes
1464 .iter()
1465 .take(3)
1466 .map(|s| {
1467 s.sign::<Sha256Digest>(TestSubject {
1468 message: Bytes::from_static(MESSAGE),
1469 })
1470 .unwrap()
1471 })
1472 .collect();
1473
1474 let duplicate = attestations.last().unwrap().clone();
1476 let signer = duplicate.signer;
1477 attestations.push(duplicate);
1478
1479 assert_eq!(
1480 schemes[0].assemble(non_empty![@attestations], &Sequential),
1481 Err(AssemblyError::DuplicateSigner(signer))
1482 );
1483 }
1484
1485 #[test]
1486 fn test_scheme_clone_and_verifier() {
1487 let mut rng = test_rng();
1488 let (schemes, _) = setup_signers(&mut rng, 4);
1489 let participants = schemes[0].participants().clone();
1490
1491 let signer = schemes[0].clone();
1493 assert!(
1494 signer
1495 .sign::<Sha256Digest>(TestSubject {
1496 message: Bytes::from_static(MESSAGE),
1497 })
1498 .is_some(),
1499 "signer should produce votes"
1500 );
1501
1502 let verifier = Scheme::verifier(NAMESPACE, participants);
1504 assert!(
1505 verifier
1506 .sign::<Sha256Digest>(TestSubject {
1507 message: Bytes::from_static(MESSAGE),
1508 })
1509 .is_none(),
1510 "verifier should not produce votes"
1511 );
1512 }
1513
1514 #[test]
1515 fn test_certificate_decode_validation() {
1516 let mut rng = test_rng();
1517 let (schemes, _) = setup_signers(&mut rng, 4);
1518 let participants_len = schemes.len();
1519 let participant_count =
1520 u32::try_from(participants_len).expect("participant count exceeds u32::MAX");
1521
1522 let attestations: Vec<_> = schemes
1523 .iter()
1524 .take(3)
1525 .map(|s| {
1526 s.sign::<Sha256Digest>(TestSubject {
1527 message: Bytes::from_static(MESSAGE),
1528 })
1529 .unwrap()
1530 })
1531 .collect();
1532
1533 let certificate = schemes[0]
1534 .assemble(non_empty![@attestations], &Sequential)
1535 .unwrap();
1536
1537 let encoded = certificate.encode();
1539 let decoded =
1540 Certificate::decode_cfg(encoded, &participants_len).expect("decode certificate");
1541 assert_eq!(decoded, certificate);
1542
1543 let empty = Certificate {
1545 signers: Signers::new(participant_count, std::iter::empty::<Participant>()).unwrap(),
1546 signatures: Vec::new(),
1547 };
1548 assert!(Certificate::decode_cfg(empty.encode(), &participants_len).is_err());
1549
1550 let mismatched = Certificate {
1552 signers: Signers::new(participant_count, [0u32, 1].map(Participant::new)).unwrap(),
1553 signatures: vec![certificate.signatures[0].clone()],
1554 };
1555 assert!(Certificate::decode_cfg(mismatched.encode(), &participants_len).is_err());
1556
1557 let mut signers = certificate.signers.iter().collect::<Vec<_>>();
1559 signers.push(Participant::from_usize(participants_len));
1560 let mut sigs = certificate.signatures.clone();
1561 sigs.push(certificate.signatures[0].clone());
1562 let extended = Certificate {
1563 signers: Signers::new(participant_count + 1, signers).unwrap(),
1564 signatures: sigs,
1565 };
1566 assert!(Certificate::decode_cfg(extended.encode(), &participants_len).is_err());
1567 }
1568
1569 #[test]
1570 fn test_verify_certificate_rejects_unknown_signer() {
1571 let mut rng = test_rng();
1572 let (schemes, verifier) = setup_signers(&mut rng, 4);
1573 let participants_len = schemes.len();
1574
1575 let attestations: Vec<_> = schemes
1576 .iter()
1577 .take(3)
1578 .map(|s| {
1579 s.sign::<Sha256Digest>(TestSubject {
1580 message: Bytes::from_static(MESSAGE),
1581 })
1582 .unwrap()
1583 })
1584 .collect();
1585
1586 let mut certificate = schemes[0]
1587 .assemble(non_empty![@attestations], &Sequential)
1588 .unwrap();
1589
1590 let mut signers: Vec<Participant> = certificate.signers.iter().collect();
1592 signers.push(Participant::from_usize(participants_len));
1593 certificate.signers =
1594 Signers::new((participants_len + 1).try_into().unwrap(), signers).unwrap();
1595 certificate
1596 .signatures
1597 .push(certificate.signatures[0].clone());
1598
1599 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1600 &mut rng,
1601 TestSubject {
1602 message: Bytes::from_static(MESSAGE),
1603 },
1604 &certificate,
1605 &Sequential,
1606 ));
1607 }
1608
1609 #[test]
1610 fn test_verify_certificate_rejects_invalid_certificate_signers_size() {
1611 let mut rng = test_rng();
1612 let (schemes, verifier) = setup_signers(&mut rng, 4);
1613 let participants_len = schemes.len();
1614
1615 let attestations: Vec<_> = schemes
1616 .iter()
1617 .take(3)
1618 .map(|s| {
1619 s.sign::<Sha256Digest>(TestSubject {
1620 message: Bytes::from_static(MESSAGE),
1621 })
1622 .unwrap()
1623 })
1624 .collect();
1625
1626 let mut certificate = schemes[0]
1627 .assemble(non_empty![@attestations], &Sequential)
1628 .unwrap();
1629
1630 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1632 &mut rng,
1633 TestSubject {
1634 message: Bytes::from_static(MESSAGE),
1635 },
1636 &certificate,
1637 &Sequential,
1638 ));
1639
1640 let signers: Vec<Participant> = certificate.signers.iter().collect();
1642 certificate.signers =
1643 Signers::new((participants_len + 1).try_into().unwrap(), signers).unwrap();
1644
1645 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1647 &mut rng,
1648 TestSubject {
1649 message: Bytes::from_static(MESSAGE),
1650 },
1651 &certificate,
1652 &Sequential,
1653 ));
1654 }
1655
1656 #[test]
1657 fn test_verify_certificate_rejects_signers_size_mismatch() {
1658 let mut rng = test_rng();
1659 let (schemes, verifier) = setup_signers(&mut rng, 4);
1660 let participants_len = schemes.len();
1661
1662 let attestations: Vec<_> = schemes
1663 .iter()
1664 .take(3)
1665 .map(|s| {
1666 s.sign::<Sha256Digest>(TestSubject {
1667 message: Bytes::from_static(MESSAGE),
1668 })
1669 .unwrap()
1670 })
1671 .collect();
1672
1673 let mut certificate = schemes[0]
1674 .assemble(non_empty![@attestations], &Sequential)
1675 .unwrap();
1676
1677 let signers: Vec<Participant> = certificate.signers.iter().collect();
1679 certificate.signers =
1680 Signers::new((participants_len + 1).try_into().unwrap(), signers).unwrap();
1681 certificate
1682 .signatures
1683 .push(certificate.signatures[0].clone());
1684
1685 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1686 &mut rng,
1687 TestSubject {
1688 message: Bytes::from_static(MESSAGE),
1689 },
1690 &certificate,
1691 &Sequential,
1692 ));
1693 }
1694
1695 #[cfg(feature = "arbitrary")]
1696 mod conformance {
1697 use super::*;
1698 use commonware_codec::conformance::CodecConformance;
1699
1700 commonware_conformance::conformance_tests! {
1701 CodecConformance<Certificate>,
1702 }
1703 }
1704}