1#[cfg(feature = "mocks")]
7pub mod mocks;
8
9use super::{Batch, PrivateKey, PublicKey, Signature as Ed25519Signature};
10use crate::{
11 BatchVerifier, Digest, 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::{Buf, BufMut};
17use commonware_codec::{EncodeSize, Error, Read, ReadRangeExt, Write, types::lazy::Lazy};
18use commonware_parallel::Strategy;
19use commonware_utils::{
20 Participant,
21 iter::NonEmpty,
22 ordered::{Quorum, Set},
23};
24use rand_core::CryptoRng;
25#[cfg(feature = "std")]
26use std::collections::BTreeSet;
27
28#[derive(Clone, Debug)]
34pub struct Generic<N: Namespace> {
35 pub participants: Set<PublicKey>,
37 pub signer: Option<(Participant, PrivateKey)>,
39 pub namespace: N,
41}
42
43impl<N: Namespace> Generic<N> {
44 pub fn signer(
46 namespace: &[u8],
47 participants: Set<PublicKey>,
48 private_key: PrivateKey,
49 ) -> Option<Self> {
50 let signer = participants
51 .index(&private_key.public_key())
52 .map(|index| (index, private_key))?;
53
54 Some(Self {
55 participants,
56 signer: Some(signer),
57 namespace: N::derive(namespace),
58 })
59 }
60
61 pub fn verifier(namespace: &[u8], participants: Set<PublicKey>) -> Self {
63 Self {
64 participants,
65 signer: None,
66 namespace: N::derive(namespace),
67 }
68 }
69
70 pub fn me(&self) -> Option<Participant> {
72 self.signer.as_ref().map(|(index, _)| *index)
73 }
74
75 pub fn sign<'a, S, D>(&self, subject: S::Subject<'a, D>) -> Option<Attestation<S>>
77 where
78 S: Scheme<Signature = Ed25519Signature>,
79 S::Subject<'a, D>: Subject<Namespace = N>,
80 D: Digest,
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<'a, S, D>(
94 &self,
95 subject: S::Subject<'a, D>,
96 attestation: &Attestation<S>,
97 ) -> bool
98 where
99 S: Scheme<Signature = Ed25519Signature>,
100 S::Subject<'a, D>: Subject<Namespace = N>,
101 D: Digest,
102 {
103 let Some(public_key) = self.participants.key(attestation.signer) else {
104 return false;
105 };
106 let Some(signature) = attestation.signature.get() else {
107 return false;
108 };
109
110 public_key.verify(
111 subject.namespace(&self.namespace),
112 &subject.message(),
113 signature,
114 )
115 }
116
117 pub fn verify_attestations<'a, S, R, D, I>(
119 &self,
120 rng: &mut R,
121 subject: S::Subject<'a, D>,
122 attestations: I,
123 strategy: &impl Strategy,
124 ) -> Verification<S>
125 where
126 S: Scheme<Signature = Ed25519Signature>,
127 S::Subject<'a, D>: Subject<Namespace = N>,
128 R: CryptoRng,
129 D: Digest,
130 I: IntoIterator<Item = Attestation<S>>,
131 {
132 let namespace = subject.namespace(&self.namespace);
133 let message = subject.message();
134
135 let attestations = attestations.into_iter();
136 let mut invalid = BTreeSet::new();
137 let mut candidates = Vec::with_capacity(attestations.size_hint().0);
138 let mut batch = Batch::new(attestations.size_hint().0);
139
140 for attestation in attestations {
141 let Some(public_key) = self.participants.key(attestation.signer) else {
142 invalid.insert(attestation.signer);
143 continue;
144 };
145 let Some(signature) = attestation.signature.get() else {
146 invalid.insert(attestation.signer);
147 continue;
148 };
149
150 batch.add(namespace, &message, public_key, signature);
151 candidates.push((attestation, public_key));
152 }
153
154 if !candidates.is_empty() && !batch.verify(rng, strategy) {
155 for (attestation, public_key) in &candidates {
157 let Some(signature) = attestation.signature.get() else {
158 invalid.insert(attestation.signer);
159 continue;
160 };
161 if !public_key.verify(namespace, &message, signature) {
162 invalid.insert(attestation.signer);
163 }
164 }
165 }
166
167 let verified = candidates
168 .into_iter()
169 .filter_map(|(attestation, _)| {
170 if invalid.contains(&attestation.signer) {
171 None
172 } else {
173 Some(attestation)
174 }
175 })
176 .collect();
177
178 Verification::new(verified, invalid.into_iter().collect())
179 }
180
181 pub fn assemble<S, I>(&self, attestations: NonEmpty<I>) -> Result<Certificate, AssemblyError>
183 where
184 S: Scheme<Signature = Ed25519Signature>,
185 I: Iterator<Item = Attestation<S>>,
186 {
187 let mut entries = Vec::new();
189 for Attestation { signer, signature } in attestations {
190 self.participants
191 .key(signer)
192 .ok_or(AssemblyError::UnknownSigner(signer))?;
193 let signature = signature
194 .get()
195 .cloned()
196 .ok_or(AssemblyError::MalformedSignature(signer))?;
197 entries.push((signer, signature));
198 }
199
200 entries.sort_by_key(|(signer, _)| *signer);
202 let (signer, signatures): (Vec<Participant>, Vec<_>) = entries.into_iter().unzip();
203 let signers = Signers::try_from((&self.participants, signer))?
204 .require(self.participants.quorum::<S::Faults>())?;
205 let signatures = signatures.into_iter().map(Lazy::from).collect();
206
207 Ok(Certificate {
208 signers,
209 signatures,
210 })
211 }
212
213 fn batch_verify_certificate<'a, S, D>(
217 &self,
218 batch: &mut Batch,
219 subject: S::Subject<'a, D>,
220 certificate: &Certificate,
221 ) -> bool
222 where
223 S: Scheme,
224 S::Subject<'a, D>: Subject<Namespace = N>,
225 D: Digest,
226 {
227 if certificate.signers.len() != self.participants.len() {
229 return false;
230 }
231
232 if certificate.signers.count() != certificate.signatures.len() {
234 return false;
235 }
236
237 if certificate.signers.count() < self.participants.quorum::<S::Faults>() as usize {
239 return false;
240 }
241
242 let namespace = subject.namespace(&self.namespace);
244 let message = subject.message();
245 for (signer, signature) in certificate.signers.iter().zip(&certificate.signatures) {
246 let Some(public_key) = self.participants.key(signer) else {
247 return false;
248 };
249 let Some(signature) = signature.get() else {
250 return false;
251 };
252
253 batch.add(namespace, &message, public_key, signature);
254 }
255
256 true
257 }
258
259 pub fn verify_certificate<'a, S, R, D>(
261 &self,
262 rng: &mut R,
263 subject: S::Subject<'a, D>,
264 certificate: &Certificate,
265 strategy: &impl Strategy,
266 ) -> bool
267 where
268 S: Scheme,
269 S::Subject<'a, D>: Subject<Namespace = N>,
270 R: CryptoRng,
271 D: Digest,
272 {
273 let mut batch = Batch::new(certificate.signatures.len());
274 if !self.batch_verify_certificate::<S, D>(&mut batch, subject, certificate) {
275 return false;
276 }
277
278 batch.verify(rng, strategy)
279 }
280
281 pub fn verify_certificates<'a, S, R, D, I>(
283 &self,
284 rng: &mut R,
285 certificates: NonEmpty<I>,
286 strategy: &impl Strategy,
287 ) -> bool
288 where
289 S: Scheme,
290 S::Subject<'a, D>: Subject<Namespace = N>,
291 R: CryptoRng,
292 D: Digest,
293 I: Iterator<Item = (S::Subject<'a, D>, &'a Certificate)>,
294 {
295 let per_certificate = self.participants.len();
297 let certificates = certificates.into_iter();
298 let mut batch = Batch::new(certificates.size_hint().0.saturating_mul(per_certificate));
299 for (subject, certificate) in certificates {
300 if !self.batch_verify_certificate::<S, D>(&mut batch, subject, certificate) {
301 return false;
302 }
303 }
304
305 batch.verify(rng, strategy)
306 }
307
308 pub const fn is_attributable() -> bool {
309 true
310 }
311
312 pub const fn is_batchable() -> bool {
313 true
314 }
315
316 pub const fn certificate_codec_config(&self) -> <Certificate as commonware_codec::Read>::Cfg {
317 self.participants.len()
318 }
319
320 pub const fn certificate_codec_config_unbounded() -> <Certificate as commonware_codec::Read>::Cfg
321 {
322 u32::MAX as usize
323 }
324}
325
326#[derive(Clone, Debug, PartialEq, Eq, Hash)]
327pub struct Certificate {
328 pub signers: Signers,
330 pub signatures: Vec<Lazy<Ed25519Signature>>,
332}
333
334#[cfg(feature = "arbitrary")]
335impl arbitrary::Arbitrary<'_> for Certificate {
336 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
337 let signers = Signers::arbitrary(u)?;
338 let signatures = (0..signers.count())
339 .map(|_| u.arbitrary::<Ed25519Signature>().map(Lazy::from))
340 .collect::<arbitrary::Result<Vec<_>>>()?;
341 Ok(Self {
342 signers,
343 signatures,
344 })
345 }
346}
347
348impl Write for Certificate {
349 fn write(&self, writer: &mut impl BufMut) {
350 self.signers.write(writer);
351 self.signatures.write(writer);
352 }
353}
354
355impl EncodeSize for Certificate {
356 fn encode_size(&self) -> usize {
357 self.signers.encode_size() + self.signatures.encode_size()
358 }
359}
360
361impl Read for Certificate {
362 type Cfg = usize;
363
364 fn read_cfg(reader: &mut impl Buf, participants: &usize) -> Result<Self, Error> {
365 let signers = Signers::read_cfg(reader, participants)?;
366 if signers.count() == 0 {
367 return Err(Error::Invalid(
368 "cryptography::ed25519::certificate::Certificate",
369 "Certificate contains no signers",
370 ));
371 }
372
373 let signatures = Vec::<Lazy<Ed25519Signature>>::read_range(reader, ..=*participants)?;
374 if signers.count() != signatures.len() {
375 return Err(Error::Invalid(
376 "cryptography::ed25519::certificate::Certificate",
377 "Signers and signatures counts differ",
378 ));
379 }
380
381 Ok(Self {
382 signers,
383 signatures,
384 })
385 }
386}
387
388#[macro_export]
417macro_rules! impl_certificate_ed25519 {
418 ($subject:ty, $namespace:ty, $faults:ty) => {
419 #[cfg(feature = "mocks")]
424 #[allow(dead_code)]
425 pub fn fixture<R>(
426 rng: &mut R,
427 namespace: &[u8],
428 n: u32,
429 ) -> $crate::certificate::mocks::Fixture<Scheme>
430 where
431 R: rand_core::CryptoRng,
432 {
433 $crate::ed25519::certificate::mocks::fixture(
434 rng,
435 namespace,
436 n,
437 Scheme::signer,
438 Scheme::verifier,
439 )
440 }
441
442 #[derive(Clone, Debug)]
444 pub struct Scheme {
445 generic: $crate::ed25519::certificate::Generic<$namespace>,
446 }
447
448 impl Scheme {
449 pub fn signer(
459 namespace: &[u8],
460 participants: commonware_utils::ordered::Set<$crate::ed25519::PublicKey>,
461 private_key: $crate::ed25519::PrivateKey,
462 ) -> Option<Self> {
463 Some(Self {
464 generic: $crate::ed25519::certificate::Generic::signer(
465 namespace,
466 participants,
467 private_key,
468 )?,
469 })
470 }
471
472 pub fn verifier(
476 namespace: &[u8],
477 participants: commonware_utils::ordered::Set<$crate::ed25519::PublicKey>,
478 ) -> Self {
479 Self {
480 generic: $crate::ed25519::certificate::Generic::verifier(
481 namespace,
482 participants,
483 ),
484 }
485 }
486 }
487
488 impl $crate::certificate::Verifier for Scheme {
489 type Subject<'a, D: $crate::Digest> = $subject;
490 type Faults = $faults;
491 type PublicKey = $crate::ed25519::PublicKey;
492 type Certificate = $crate::ed25519::certificate::Certificate;
493
494 fn verify_certificate<R, D>(
495 &self,
496 rng: &mut R,
497 subject: Self::Subject<'_, D>,
498 certificate: &Self::Certificate,
499 strategy: &impl commonware_parallel::Strategy,
500 ) -> bool
501 where
502 R: rand_core::CryptoRng,
503 D: $crate::Digest,
504 {
505 self.generic
506 .verify_certificate::<Self, _, D>(rng, subject, certificate, strategy)
507 }
508
509 fn verify_certificates<'a, R, D, I>(
510 &self,
511 rng: &mut R,
512 certificates: commonware_utils::iter::NonEmpty<I>,
513 strategy: &impl commonware_parallel::Strategy,
514 ) -> bool
515 where
516 R: rand_core::CryptoRng,
517 D: $crate::Digest,
518 I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
519 {
520 self.generic
521 .verify_certificates::<Self, _, D, _>(rng, certificates, strategy)
522 }
523
524 fn is_batchable() -> bool {
525 $crate::ed25519::certificate::Generic::<$namespace>::is_batchable()
526 }
527
528 fn certificate_codec_config(
529 &self,
530 ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
531 self.generic.certificate_codec_config()
532 }
533
534 fn certificate_codec_config_unbounded(
535 ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
536 $crate::ed25519::certificate::Generic::<$namespace>::certificate_codec_config_unbounded()
537 }
538 }
539
540 impl $crate::certificate::Scheme for Scheme {
541 type Signature = $crate::ed25519::Signature;
542
543 fn me(&self) -> Option<commonware_utils::Participant> {
544 self.generic.me()
545 }
546
547 fn participants(&self) -> &commonware_utils::ordered::Set<Self::PublicKey> {
548 &self.generic.participants
549 }
550
551 fn sign<D: $crate::Digest>(
552 &self,
553 subject: Self::Subject<'_, D>,
554 ) -> Option<$crate::certificate::Attestation<Self>> {
555 self.generic.sign::<_, D>(subject)
556 }
557
558 fn verify_attestation<R, D>(
559 &self,
560 _rng: &mut R,
561 subject: Self::Subject<'_, D>,
562 attestation: &$crate::certificate::Attestation<Self>,
563 _strategy: &impl commonware_parallel::Strategy,
564 ) -> bool
565 where
566 R: rand_core::CryptoRng,
567 D: $crate::Digest,
568 {
569 self.generic
570 .verify_attestation::<_, D>(subject, attestation)
571 }
572
573 fn verify_attestations<R, D, I>(
574 &self,
575 rng: &mut R,
576 subject: Self::Subject<'_, D>,
577 attestations: I,
578 strategy: &impl commonware_parallel::Strategy,
579 ) -> $crate::certificate::Verification<Self>
580 where
581 R: rand_core::CryptoRng,
582 D: $crate::Digest,
583 I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
584 {
585 self.generic
586 .verify_attestations::<_, _, D, _>(rng, subject, attestations, strategy)
587 }
588
589 fn assemble<I>(
590 &self,
591 attestations: commonware_utils::iter::NonEmpty<I>,
592 _strategy: &impl commonware_parallel::Strategy,
593 ) -> Result<Self::Certificate, $crate::certificate::AssemblyError>
594 where
595 I: Iterator<Item = $crate::certificate::Attestation<Self>> + Send,
596 {
597 self.generic.assemble::<Self, _>(attestations)
598 }
599
600 fn is_attributable() -> bool {
601 $crate::ed25519::certificate::Generic::<$namespace>::is_attributable()
602 }
603 }
604 };
605}
606
607#[cfg(test)]
608mod tests {
609 use super::*;
610 use crate::{
611 certificate::{Scheme as _, Verifier as _},
612 sha256::Digest as Sha256Digest,
613 };
614 use bytes::Bytes;
615 use commonware_codec::{Decode, Encode};
616 use commonware_math::algebra::Random;
617 use commonware_parallel::Sequential;
618 use commonware_utils::{
619 Faults, N3f1, Participant, TryCollect, non_empty, ordered::Set, test_rng,
620 };
621
622 const NAMESPACE: &[u8] = b"test-ed25519";
623 const MESSAGE: &[u8] = b"test message";
624
625 #[derive(Clone, Debug)]
627 pub struct TestSubject {
628 pub message: Bytes,
629 }
630
631 impl Subject for TestSubject {
632 type Namespace = Vec<u8>;
633
634 fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] {
635 derived.as_ref()
636 }
637
638 fn message(&self) -> Bytes {
639 self.message.clone()
640 }
641 }
642
643 impl_certificate_ed25519!(TestSubject, Vec<u8>, N3f1);
645
646 fn setup_signers(rng: &mut impl CryptoRng, n: u32) -> (Vec<Scheme>, Scheme) {
647 let private_keys: Vec<_> = (0..n).map(|_| PrivateKey::random(&mut *rng)).collect();
648 let participants: Set<PublicKey> = private_keys
649 .iter()
650 .map(|sk| sk.public_key())
651 .try_collect()
652 .unwrap();
653
654 let signers = private_keys
655 .into_iter()
656 .map(|sk| Scheme::signer(NAMESPACE, participants.clone(), sk).unwrap())
657 .collect();
658
659 let verifier = Scheme::verifier(NAMESPACE, participants);
660
661 (signers, verifier)
662 }
663
664 #[test]
665 fn test_is_attributable() {
666 assert!(Generic::<Vec<u8>>::is_attributable());
667 assert!(Scheme::is_attributable());
668 }
669
670 #[test]
671 fn test_is_batchable() {
672 assert!(Generic::<Vec<u8>>::is_batchable());
673 assert!(Scheme::is_batchable());
674 }
675
676 #[test]
677 fn test_sign_vote_roundtrip() {
678 let mut rng = test_rng();
679 let (schemes, _) = setup_signers(&mut rng, 4);
680 let scheme = &schemes[0];
681
682 let attestation = scheme
683 .sign::<Sha256Digest>(TestSubject {
684 message: Bytes::from_static(MESSAGE),
685 })
686 .unwrap();
687 assert!(scheme.verify_attestation::<_, Sha256Digest>(
688 &mut rng,
689 TestSubject {
690 message: Bytes::from_static(MESSAGE),
691 },
692 &attestation,
693 &Sequential,
694 ));
695 }
696
697 #[test]
698 fn test_verifier_cannot_sign() {
699 let mut rng = test_rng();
700 let (_, verifier) = setup_signers(&mut rng, 4);
701 assert!(
702 verifier
703 .sign::<Sha256Digest>(TestSubject {
704 message: Bytes::from_static(MESSAGE)
705 })
706 .is_none()
707 );
708 }
709
710 #[test]
711 fn test_verify_attestations_filters_invalid() {
712 let mut rng = test_rng();
713 let (schemes, _) = setup_signers(&mut rng, 5);
714 let quorum = N3f1::quorum(schemes.len()) as usize;
715
716 let attestations: Vec<_> = schemes
717 .iter()
718 .take(quorum)
719 .map(|s| {
720 s.sign::<Sha256Digest>(TestSubject {
721 message: Bytes::from_static(MESSAGE),
722 })
723 .unwrap()
724 })
725 .collect();
726
727 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
728 &mut rng,
729 TestSubject {
730 message: Bytes::from_static(MESSAGE),
731 },
732 attestations.clone(),
733 &Sequential,
734 );
735 assert!(result.invalid.is_empty());
736 assert_eq!(result.verified.len(), quorum);
737
738 let mut attestations_corrupted = attestations.clone();
740 attestations_corrupted[0].signer = Participant::new(999);
741 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
742 &mut rng,
743 TestSubject {
744 message: Bytes::from_static(MESSAGE),
745 },
746 attestations_corrupted,
747 &Sequential,
748 );
749 assert_eq!(result.invalid, vec![Participant::new(999)]);
750 assert_eq!(result.verified.len(), quorum - 1);
751
752 let mut attestations_corrupted = attestations;
754 attestations_corrupted[0].signature = attestations_corrupted[1].signature.clone();
755 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
756 &mut rng,
757 TestSubject {
758 message: Bytes::from_static(MESSAGE),
759 },
760 attestations_corrupted,
761 &Sequential,
762 );
763 assert_eq!(result.invalid.len(), 1);
765 assert_eq!(result.verified.len(), quorum - 1);
766 }
767
768 #[test]
769 fn test_assemble_certificate() {
770 let mut rng = test_rng();
771 let (schemes, _) = setup_signers(&mut rng, 4);
772 let quorum = N3f1::quorum(schemes.len()) as usize;
773
774 let attestations: Vec<_> = schemes
775 .iter()
776 .take(quorum)
777 .map(|s| {
778 s.sign::<Sha256Digest>(TestSubject {
779 message: Bytes::from_static(MESSAGE),
780 })
781 .unwrap()
782 })
783 .collect();
784
785 let certificate = schemes[0]
786 .assemble(non_empty![@attestations], &Sequential)
787 .unwrap();
788
789 assert_eq!(certificate.signers.count(), quorum);
791 assert_eq!(certificate.signatures.len(), quorum);
792 }
793
794 #[test]
795 fn test_assemble_certificate_sorts_signers() {
796 let mut rng = test_rng();
797 let (schemes, _) = setup_signers(&mut rng, 4);
798
799 let mut indexed: Vec<_> = (0..3).map(|i| (schemes[i].me().unwrap(), i)).collect();
801 indexed.sort_by_key(|(idx, _)| *idx);
802
803 let attestations = vec![
805 schemes[indexed[2].1]
806 .sign::<Sha256Digest>(TestSubject {
807 message: Bytes::from_static(MESSAGE),
808 })
809 .unwrap(),
810 schemes[indexed[1].1]
811 .sign::<Sha256Digest>(TestSubject {
812 message: Bytes::from_static(MESSAGE),
813 })
814 .unwrap(),
815 schemes[indexed[0].1]
816 .sign::<Sha256Digest>(TestSubject {
817 message: Bytes::from_static(MESSAGE),
818 })
819 .unwrap(),
820 ];
821
822 let certificate = schemes[0]
823 .assemble(non_empty![@attestations], &Sequential)
824 .unwrap();
825
826 let expected: Vec<_> = indexed.iter().map(|(idx, _)| *idx).collect();
828 assert_eq!(certificate.signers.iter().collect::<Vec<_>>(), expected);
829 }
830
831 #[test]
832 fn test_verify_certificate() {
833 let mut rng = test_rng();
834 let (schemes, verifier) = setup_signers(&mut rng, 4);
835 let quorum = N3f1::quorum(schemes.len()) as usize;
836
837 let attestations: Vec<_> = schemes
838 .iter()
839 .take(quorum)
840 .map(|s| {
841 s.sign::<Sha256Digest>(TestSubject {
842 message: Bytes::from_static(MESSAGE),
843 })
844 .unwrap()
845 })
846 .collect();
847
848 let certificate = schemes[0]
849 .assemble(non_empty![@attestations], &Sequential)
850 .unwrap();
851
852 assert!(verifier.verify_certificate::<_, Sha256Digest>(
853 &mut rng,
854 TestSubject {
855 message: Bytes::from_static(MESSAGE)
856 },
857 &certificate,
858 &Sequential,
859 ));
860 }
861
862 #[test]
863 fn test_verify_certificate_detects_corruption() {
864 let mut rng = test_rng();
865 let (schemes, verifier) = setup_signers(&mut rng, 4);
866 let quorum = N3f1::quorum(schemes.len()) as usize;
867
868 let attestations: Vec<_> = schemes
869 .iter()
870 .take(quorum)
871 .map(|s| {
872 s.sign::<Sha256Digest>(TestSubject {
873 message: Bytes::from_static(MESSAGE),
874 })
875 .unwrap()
876 })
877 .collect();
878
879 let certificate = schemes[0]
880 .assemble(non_empty![@attestations], &Sequential)
881 .unwrap();
882
883 assert!(verifier.verify_certificate::<_, Sha256Digest>(
885 &mut rng,
886 TestSubject {
887 message: Bytes::from_static(MESSAGE),
888 },
889 &certificate,
890 &Sequential,
891 ));
892
893 let mut corrupted = certificate;
895 corrupted.signatures[0] = corrupted.signatures[1].clone();
896 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
897 &mut rng,
898 TestSubject {
899 message: Bytes::from_static(MESSAGE),
900 },
901 &corrupted,
902 &Sequential,
903 ));
904 }
905
906 #[test]
907 fn test_certificate_codec_roundtrip() {
908 let mut rng = test_rng();
909 let (schemes, _) = setup_signers(&mut rng, 4);
910 let quorum = N3f1::quorum(schemes.len()) as usize;
911
912 let attestations: Vec<_> = schemes
913 .iter()
914 .take(quorum)
915 .map(|s| {
916 s.sign::<Sha256Digest>(TestSubject {
917 message: Bytes::from_static(MESSAGE),
918 })
919 .unwrap()
920 })
921 .collect();
922
923 let certificate = schemes[0]
924 .assemble(non_empty![@attestations], &Sequential)
925 .unwrap();
926 let encoded = certificate.encode();
927 let decoded = Certificate::decode_cfg(encoded, &schemes.len()).expect("decode certificate");
928 assert_eq!(decoded, certificate);
929 }
930
931 #[test]
932 fn test_certificate_rejects_sub_quorum() {
933 let mut rng = test_rng();
934 let (schemes, _) = setup_signers(&mut rng, 4);
935 let expected = N3f1::quorum(schemes.len());
936 let found = expected - 1;
937 let found_count = usize::try_from(found).expect("quorum exceeds usize::MAX");
938
939 let attestations: Vec<_> = schemes
940 .iter()
941 .take(found_count)
942 .map(|s| {
943 s.sign::<Sha256Digest>(TestSubject {
944 message: Bytes::from_static(MESSAGE),
945 })
946 .unwrap()
947 })
948 .collect();
949
950 assert_eq!(
951 schemes[0].assemble(non_empty![@attestations], &Sequential),
952 Err(AssemblyError::InsufficientAttestations(expected, found))
953 );
954 }
955
956 #[test]
957 fn test_certificate_rejects_invalid_signer() {
958 let mut rng = test_rng();
959 let (schemes, _) = setup_signers(&mut rng, 4);
960 let quorum =
961 usize::try_from(N3f1::quorum(schemes.len())).expect("quorum exceeds usize::MAX");
962
963 let mut attestations: Vec<_> = schemes
964 .iter()
965 .take(quorum)
966 .map(|s| {
967 s.sign::<Sha256Digest>(TestSubject {
968 message: Bytes::from_static(MESSAGE),
969 })
970 .unwrap()
971 })
972 .collect();
973
974 let unknown = Participant::new(999);
976 attestations[0].signer = unknown;
977
978 assert_eq!(
979 schemes[0].assemble(non_empty![@attestations], &Sequential),
980 Err(AssemblyError::UnknownSigner(unknown))
981 );
982 }
983
984 #[test]
985 fn test_certificate_rejects_malformed_signature() {
986 let mut rng = test_rng();
987 let (schemes, _) = setup_signers(&mut rng, 4);
988 let quorum =
989 usize::try_from(N3f1::quorum(schemes.len())).expect("quorum exceeds usize::MAX");
990
991 let mut attestations: Vec<_> = schemes
992 .iter()
993 .take(quorum)
994 .map(|s| {
995 s.sign::<Sha256Digest>(TestSubject {
996 message: Bytes::from_static(MESSAGE),
997 })
998 .unwrap()
999 })
1000 .collect();
1001
1002 let signer = attestations[0].signer;
1003 let mut truncated = &[0u8; 3][..];
1004 attestations[0].signature = Lazy::deferred(&mut truncated, ());
1005
1006 assert_eq!(
1007 schemes[0].assemble(non_empty![@attestations], &Sequential),
1008 Err(AssemblyError::MalformedSignature(signer))
1009 );
1010 }
1011
1012 #[test]
1013 fn test_verify_certificate_rejects_sub_quorum() {
1014 let mut rng = test_rng();
1015 let (schemes, verifier) = setup_signers(&mut rng, 4);
1016 let participants_len = schemes.len();
1017
1018 let attestations: Vec<_> = schemes
1019 .iter()
1020 .take(3)
1021 .map(|s| {
1022 s.sign::<Sha256Digest>(TestSubject {
1023 message: Bytes::from_static(MESSAGE),
1024 })
1025 .unwrap()
1026 })
1027 .collect();
1028
1029 let mut certificate = schemes[0]
1030 .assemble(non_empty![@attestations], &Sequential)
1031 .unwrap();
1032
1033 let mut signers: Vec<Participant> = certificate.signers.iter().collect();
1035 signers.pop();
1036 certificate.signers = Signers::new(participants_len.try_into().unwrap(), signers).unwrap();
1037 certificate.signatures.pop();
1038
1039 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1040 &mut rng,
1041 TestSubject {
1042 message: Bytes::from_static(MESSAGE),
1043 },
1044 &certificate,
1045 &Sequential,
1046 ));
1047 }
1048
1049 #[test]
1050 fn test_verify_certificate_rejects_mismatched_signature_count() {
1051 let mut rng = test_rng();
1052 let (schemes, verifier) = setup_signers(&mut rng, 4);
1053
1054 let attestations: Vec<_> = schemes
1055 .iter()
1056 .take(3)
1057 .map(|s| {
1058 s.sign::<Sha256Digest>(TestSubject {
1059 message: Bytes::from_static(MESSAGE),
1060 })
1061 .unwrap()
1062 })
1063 .collect();
1064
1065 let mut certificate = schemes[0]
1066 .assemble(non_empty![@attestations], &Sequential)
1067 .unwrap();
1068
1069 certificate.signatures.pop();
1071
1072 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1073 &mut rng,
1074 TestSubject {
1075 message: Bytes::from_static(MESSAGE),
1076 },
1077 &certificate,
1078 &Sequential,
1079 ));
1080 }
1081
1082 #[test]
1083 fn test_verify_certificates_batch() {
1084 let mut rng = test_rng();
1085 let (schemes, verifier) = setup_signers(&mut rng, 4);
1086 let quorum = N3f1::quorum(schemes.len()) as usize;
1087
1088 let messages: Vec<Bytes> = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()]
1089 .into_iter()
1090 .map(Bytes::copy_from_slice)
1091 .collect();
1092 let mut certificates = Vec::new();
1093
1094 for msg in &messages {
1095 let attestations: Vec<_> = schemes
1096 .iter()
1097 .take(quorum)
1098 .map(|s| {
1099 s.sign::<Sha256Digest>(TestSubject {
1100 message: msg.clone(),
1101 })
1102 .unwrap()
1103 })
1104 .collect();
1105 certificates.push(
1106 schemes[0]
1107 .assemble(non_empty![@attestations], &Sequential)
1108 .unwrap(),
1109 );
1110 }
1111
1112 let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
1113 (
1114 TestSubject {
1115 message: msg.clone(),
1116 },
1117 cert,
1118 )
1119 });
1120
1121 assert!(verifier.verify_certificates::<_, Sha256Digest, _>(
1122 &mut rng,
1123 non_empty![@certs_iter],
1124 &Sequential
1125 ));
1126 }
1127
1128 #[test]
1129 fn test_verify_certificates_batch_detects_failure() {
1130 let mut rng = test_rng();
1131 let (schemes, verifier) = setup_signers(&mut rng, 4);
1132 let quorum = N3f1::quorum(schemes.len()) as usize;
1133
1134 let messages: Vec<Bytes> = [b"msg1".as_slice(), b"msg2".as_slice()]
1135 .into_iter()
1136 .map(Bytes::copy_from_slice)
1137 .collect();
1138 let mut certificates = Vec::new();
1139
1140 for msg in &messages {
1141 let attestations: Vec<_> = schemes
1142 .iter()
1143 .take(quorum)
1144 .map(|s| {
1145 s.sign::<Sha256Digest>(TestSubject {
1146 message: msg.clone(),
1147 })
1148 .unwrap()
1149 })
1150 .collect();
1151 certificates.push(
1152 schemes[0]
1153 .assemble(non_empty![@attestations], &Sequential)
1154 .unwrap(),
1155 );
1156 }
1157
1158 certificates[1].signatures[0] = certificates[1].signatures[1].clone();
1160
1161 let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
1162 (
1163 TestSubject {
1164 message: msg.clone(),
1165 },
1166 cert,
1167 )
1168 });
1169
1170 assert!(!verifier.verify_certificates::<_, Sha256Digest, _>(
1171 &mut rng,
1172 non_empty![@certs_iter],
1173 &Sequential
1174 ));
1175 }
1176
1177 #[test]
1178 fn test_assemble_certificate_rejects_duplicate_signers() {
1179 let mut rng = test_rng();
1180 let (schemes, _) = setup_signers(&mut rng, 4);
1181
1182 let mut attestations: Vec<_> = schemes
1183 .iter()
1184 .take(3)
1185 .map(|s| {
1186 s.sign::<Sha256Digest>(TestSubject {
1187 message: Bytes::from_static(MESSAGE),
1188 })
1189 .unwrap()
1190 })
1191 .collect();
1192
1193 let duplicate = attestations.last().unwrap().clone();
1195 let signer = duplicate.signer;
1196 attestations.push(duplicate);
1197
1198 assert_eq!(
1199 schemes[0].assemble(non_empty![@attestations], &Sequential),
1200 Err(AssemblyError::DuplicateSigner(signer))
1201 );
1202 }
1203
1204 #[test]
1205 fn test_scheme_clone_and_verifier() {
1206 let mut rng = test_rng();
1207 let (schemes, _) = setup_signers(&mut rng, 4);
1208 let participants = schemes[0].participants().clone();
1209
1210 let signer = schemes[0].clone();
1212 assert!(
1213 signer
1214 .sign::<Sha256Digest>(TestSubject {
1215 message: Bytes::from_static(MESSAGE),
1216 })
1217 .is_some(),
1218 "signer should produce votes"
1219 );
1220
1221 let verifier = Scheme::verifier(NAMESPACE, participants);
1223 assert!(
1224 verifier
1225 .sign::<Sha256Digest>(TestSubject {
1226 message: Bytes::from_static(MESSAGE),
1227 })
1228 .is_none(),
1229 "verifier should not produce votes"
1230 );
1231 }
1232
1233 #[test]
1234 fn test_certificate_decode_validation() {
1235 let mut rng = test_rng();
1236 let (schemes, _) = setup_signers(&mut rng, 4);
1237 let participants_len = schemes.len();
1238 let participant_count =
1239 u32::try_from(participants_len).expect("participant count exceeds u32::MAX");
1240
1241 let attestations: Vec<_> = schemes
1242 .iter()
1243 .take(3)
1244 .map(|s| {
1245 s.sign::<Sha256Digest>(TestSubject {
1246 message: Bytes::from_static(MESSAGE),
1247 })
1248 .unwrap()
1249 })
1250 .collect();
1251
1252 let certificate = schemes[0]
1253 .assemble(non_empty![@attestations], &Sequential)
1254 .unwrap();
1255
1256 let encoded = certificate.encode();
1258 let decoded =
1259 Certificate::decode_cfg(encoded, &participants_len).expect("decode certificate");
1260 assert_eq!(decoded, certificate);
1261
1262 let empty = Certificate {
1264 signers: Signers::new(participant_count, std::iter::empty::<Participant>()).unwrap(),
1265 signatures: Vec::new(),
1266 };
1267 assert!(Certificate::decode_cfg(empty.encode(), &participants_len).is_err());
1268
1269 let mismatched = Certificate {
1271 signers: Signers::new(participant_count, [0u32, 1].map(Participant::new)).unwrap(),
1272 signatures: vec![certificate.signatures[0].clone()],
1273 };
1274 assert!(Certificate::decode_cfg(mismatched.encode(), &participants_len).is_err());
1275
1276 let mut signers = certificate.signers.iter().collect::<Vec<_>>();
1278 signers.push(Participant::from_usize(participants_len));
1279 let mut sigs = certificate.signatures.clone();
1280 sigs.push(certificate.signatures[0].clone());
1281 let extended = Certificate {
1282 signers: Signers::new(participant_count + 1, signers).unwrap(),
1283 signatures: sigs,
1284 };
1285 assert!(Certificate::decode_cfg(extended.encode(), &participants_len).is_err());
1286 }
1287
1288 #[test]
1289 fn test_verify_certificate_rejects_unknown_signer() {
1290 let mut rng = test_rng();
1291 let (schemes, verifier) = setup_signers(&mut rng, 4);
1292 let participants_len = schemes.len();
1293
1294 let attestations: Vec<_> = schemes
1295 .iter()
1296 .take(3)
1297 .map(|s| {
1298 s.sign::<Sha256Digest>(TestSubject {
1299 message: Bytes::from_static(MESSAGE),
1300 })
1301 .unwrap()
1302 })
1303 .collect();
1304
1305 let mut certificate = schemes[0]
1306 .assemble(non_empty![@attestations], &Sequential)
1307 .unwrap();
1308
1309 let mut signers: Vec<Participant> = certificate.signers.iter().collect();
1311 signers.push(Participant::from_usize(participants_len));
1312 certificate.signers =
1313 Signers::new((participants_len + 1).try_into().unwrap(), signers).unwrap();
1314 certificate
1315 .signatures
1316 .push(certificate.signatures[0].clone());
1317
1318 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1319 &mut rng,
1320 TestSubject {
1321 message: Bytes::from_static(MESSAGE),
1322 },
1323 &certificate,
1324 &Sequential,
1325 ));
1326 }
1327
1328 #[test]
1329 fn test_verify_certificate_rejects_invalid_certificate_signers_size() {
1330 let mut rng = test_rng();
1331 let (schemes, verifier) = setup_signers(&mut rng, 4);
1332 let participants_len = schemes.len();
1333
1334 let attestations: Vec<_> = schemes
1335 .iter()
1336 .take(3)
1337 .map(|s| {
1338 s.sign::<Sha256Digest>(TestSubject {
1339 message: Bytes::from_static(MESSAGE),
1340 })
1341 .unwrap()
1342 })
1343 .collect();
1344
1345 let mut certificate = schemes[0]
1346 .assemble(non_empty![@attestations], &Sequential)
1347 .unwrap();
1348
1349 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1351 &mut rng,
1352 TestSubject {
1353 message: Bytes::from_static(MESSAGE),
1354 },
1355 &certificate,
1356 &Sequential,
1357 ));
1358
1359 let signers: Vec<Participant> = certificate.signers.iter().collect();
1361 certificate.signers =
1362 Signers::new((participants_len + 1).try_into().unwrap(), signers).unwrap();
1363
1364 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1366 &mut rng,
1367 TestSubject {
1368 message: Bytes::from_static(MESSAGE),
1369 },
1370 &certificate,
1371 &Sequential,
1372 ));
1373 }
1374
1375 #[test]
1376 fn test_verify_certificate_rejects_signers_size_mismatch() {
1377 let mut rng = test_rng();
1378 let (schemes, verifier) = setup_signers(&mut rng, 4);
1379 let participants_len = schemes.len();
1380
1381 let attestations: Vec<_> = schemes
1382 .iter()
1383 .take(3)
1384 .map(|s| {
1385 s.sign::<Sha256Digest>(TestSubject {
1386 message: Bytes::from_static(MESSAGE),
1387 })
1388 .unwrap()
1389 })
1390 .collect();
1391
1392 let mut certificate = schemes[0]
1393 .assemble(non_empty![@attestations], &Sequential)
1394 .unwrap();
1395
1396 let signers: Vec<Participant> = certificate.signers.iter().collect();
1398 certificate.signers =
1399 Signers::new((participants_len + 1).try_into().unwrap(), signers).unwrap();
1400 certificate
1401 .signatures
1402 .push(certificate.signatures[0].clone());
1403
1404 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1405 &mut rng,
1406 TestSubject {
1407 message: Bytes::from_static(MESSAGE),
1408 },
1409 &certificate,
1410 &Sequential,
1411 ));
1412 }
1413
1414 #[cfg(feature = "arbitrary")]
1415 mod conformance {
1416 use super::*;
1417 use commonware_codec::conformance::CodecConformance;
1418
1419 commonware_conformance::conformance_tests! {
1420 CodecConformance<Certificate>,
1421 }
1422 }
1423}