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