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