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