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