1#[cfg(feature = "mocks")]
7pub mod mocks;
8
9use crate::{
10 certificate::{Attestation, Namespace, Scheme, Signers, Subject, Verification},
11 secp256r1::standard::{PrivateKey, PublicKey, Signature as Secp256r1Signature},
12 Digest, Signer as _, Verifier as _,
13};
14#[cfg(not(feature = "std"))]
15use alloc::{collections::BTreeSet, vec::Vec};
16use bytes::{Buf, BufMut};
17use commonware_codec::{EncodeSize, Error, Read, ReadRangeExt, Write};
18use commonware_utils::{
19 ordered::{BiMap, Quorum, Set},
20 Faults, Participant,
21};
22use rand::{CryptoRng, Rng};
23#[cfg(feature = "std")]
24use std::collections::BTreeSet;
25
26#[derive(Clone, Debug)]
32pub struct Generic<P: crate::PublicKey, N: Namespace> {
33 pub participants: BiMap<P, PublicKey>,
35 pub signer: Option<(Participant, PrivateKey)>,
37 pub namespace: N,
39}
40
41impl<P: crate::PublicKey, N: Namespace> Generic<P, N> {
42 pub fn signer(
51 namespace: &[u8],
52 participants: BiMap<P, PublicKey>,
53 private_key: PrivateKey,
54 ) -> Option<Self> {
55 let public_key = private_key.public_key();
56 let signer = participants
57 .values()
58 .iter()
59 .position(|p| p == &public_key)
60 .map(|index| (Participant::from_usize(index), private_key))?;
61
62 Some(Self {
63 participants,
64 signer: Some(signer),
65 namespace: N::derive(namespace),
66 })
67 }
68
69 pub fn verifier(namespace: &[u8], participants: BiMap<P, PublicKey>) -> Self {
75 Self {
76 participants,
77 signer: None,
78 namespace: N::derive(namespace),
79 }
80 }
81
82 pub const fn participants(&self) -> &Set<P> {
84 self.participants.keys()
85 }
86
87 pub fn me(&self) -> Option<Participant> {
89 self.signer.as_ref().map(|(index, _)| *index)
90 }
91
92 pub fn sign<'a, S, D>(&self, subject: S::Subject<'a, D>) -> Option<Attestation<S>>
94 where
95 S: Scheme<Signature = Secp256r1Signature>,
96 S::Subject<'a, D>: Subject<Namespace = N>,
97 D: Digest,
98 {
99 let (index, private_key) = self.signer.as_ref()?;
100
101 let signature = private_key.sign(subject.namespace(&self.namespace), &subject.message());
102
103 Some(Attestation {
104 signer: *index,
105 signature,
106 })
107 }
108
109 pub fn verify_attestation<'a, S, D>(
111 &self,
112 subject: S::Subject<'a, D>,
113 attestation: &Attestation<S>,
114 ) -> bool
115 where
116 S: Scheme<Signature = Secp256r1Signature>,
117 S::Subject<'a, D>: Subject<Namespace = N>,
118 D: Digest,
119 {
120 let Some(public_key) = self.participants.value(attestation.signer.into()) else {
121 return false;
122 };
123
124 public_key.verify(
125 subject.namespace(&self.namespace),
126 &subject.message(),
127 &attestation.signature,
128 )
129 }
130
131 pub fn verify_attestations<'a, S, R, D, I>(
133 &self,
134 _rng: &mut R,
135 subject: S::Subject<'a, D>,
136 attestations: I,
137 ) -> Verification<S>
138 where
139 S: Scheme<Signature = Secp256r1Signature>,
140 S::Subject<'a, D>: Subject<Namespace = N>,
141 R: Rng + CryptoRng,
142 D: Digest,
143 I: IntoIterator<Item = Attestation<S>>,
144 {
145 let namespace = subject.namespace(&self.namespace);
146 let message = subject.message();
147
148 let mut invalid = BTreeSet::new();
149 let mut verified = Vec::new();
150
151 for attestation in attestations.into_iter() {
152 let Some(public_key) = self.participants.value(attestation.signer.into()) else {
153 invalid.insert(attestation.signer);
154 continue;
155 };
156
157 if public_key.verify(namespace, &message, &attestation.signature) {
158 verified.push(attestation);
159 } else {
160 invalid.insert(attestation.signer);
161 }
162 }
163
164 Verification::new(verified, invalid.into_iter().collect())
165 }
166
167 pub fn assemble<S, I, M>(&self, attestations: I) -> Option<Certificate>
169 where
170 S: Scheme<Signature = Secp256r1Signature>,
171 I: IntoIterator<Item = Attestation<S>>,
172 M: Faults,
173 {
174 let mut entries = Vec::new();
176 for Attestation { signer, signature } in attestations {
177 if usize::from(signer) >= self.participants.len() {
178 return None;
179 }
180
181 entries.push((signer, signature));
182 }
183 if entries.len() < self.participants.quorum::<M>() as usize {
184 return None;
185 }
186
187 entries.sort_by_key(|(signer, _)| *signer);
189 let (signer, signatures): (Vec<Participant>, Vec<_>) = entries.into_iter().unzip();
190 let signers = Signers::from(self.participants.len(), signer);
191
192 Some(Certificate {
193 signers,
194 signatures,
195 })
196 }
197
198 pub fn verify_certificate<'a, S, R, D, M>(
200 &self,
201 _rng: &mut R,
202 subject: S::Subject<'a, D>,
203 certificate: &Certificate,
204 ) -> bool
205 where
206 S: Scheme,
207 S::Subject<'a, D>: Subject<Namespace = N>,
208 R: Rng + CryptoRng,
209 D: Digest,
210 M: Faults,
211 {
212 if certificate.signers.len() != self.participants.len() {
214 return false;
215 }
216
217 if certificate.signers.count() != certificate.signatures.len() {
219 return false;
220 }
221
222 if certificate.signers.count() < self.participants.quorum::<M>() as usize {
224 return false;
225 }
226
227 let namespace = subject.namespace(&self.namespace);
228 let message = subject.message();
229 for (signer, signature) in certificate.signers.iter().zip(&certificate.signatures) {
230 let Some(public_key) = self.participants.value(signer.into()) else {
231 return false;
232 };
233 if !public_key.verify(namespace, &message, signature) {
234 return false;
235 }
236 }
237
238 true
239 }
240
241 pub const fn is_attributable() -> bool {
242 true
243 }
244
245 pub const fn is_batchable() -> bool {
246 false
247 }
248
249 pub const fn certificate_codec_config(&self) -> <Certificate as commonware_codec::Read>::Cfg {
250 self.participants.len()
251 }
252
253 pub const fn certificate_codec_config_unbounded() -> <Certificate as commonware_codec::Read>::Cfg
254 {
255 u32::MAX as usize
256 }
257}
258
259#[derive(Clone, Debug, PartialEq, Eq, Hash)]
260pub struct Certificate {
261 pub signers: Signers,
263 pub signatures: Vec<Secp256r1Signature>,
265}
266
267#[cfg(feature = "arbitrary")]
268impl arbitrary::Arbitrary<'_> for Certificate {
269 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
270 let signers = Signers::arbitrary(u)?;
271 let signatures = (0..signers.count())
272 .map(|_| u.arbitrary::<Secp256r1Signature>())
273 .collect::<arbitrary::Result<Vec<_>>>()?;
274 Ok(Self {
275 signers,
276 signatures,
277 })
278 }
279}
280
281impl Write for Certificate {
282 fn write(&self, writer: &mut impl BufMut) {
283 self.signers.write(writer);
284 self.signatures.write(writer);
285 }
286}
287
288impl EncodeSize for Certificate {
289 fn encode_size(&self) -> usize {
290 self.signers.encode_size() + self.signatures.encode_size()
291 }
292}
293
294impl Read for Certificate {
295 type Cfg = usize;
296
297 fn read_cfg(reader: &mut impl Buf, participants: &usize) -> Result<Self, Error> {
298 let signers = Signers::read_cfg(reader, participants)?;
299 if signers.count() == 0 {
300 return Err(Error::Invalid(
301 "cryptography::secp256r1::certificate::Certificate",
302 "Certificate contains no signers",
303 ));
304 }
305
306 let signatures = Vec::<Secp256r1Signature>::read_range(reader, ..=*participants)?;
307 if signers.count() != signatures.len() {
308 return Err(Error::Invalid(
309 "cryptography::secp256r1::certificate::Certificate",
310 "Signers and signatures counts differ",
311 ));
312 }
313
314 Ok(Self {
315 signers,
316 signatures,
317 })
318 }
319}
320
321mod macros {
322 #[macro_export]
333 macro_rules! impl_certificate_secp256r1 {
334 ($subject:ty, $namespace:ty) => {
335 #[cfg(feature = "mocks")]
340 #[allow(dead_code)]
341 pub fn fixture<R>(
342 rng: &mut R,
343 namespace: &[u8],
344 n: u32,
345 ) -> $crate::certificate::mocks::Fixture<Scheme<$crate::ed25519::PublicKey>>
346 where
347 R: rand::RngCore + rand::CryptoRng,
348 {
349 $crate::secp256r1::certificate::mocks::fixture(
350 rng,
351 namespace,
352 n,
353 Scheme::signer,
354 Scheme::verifier,
355 )
356 }
357
358 #[derive(Clone, Debug)]
360 pub struct Scheme<P: $crate::PublicKey> {
361 generic: $crate::secp256r1::certificate::Generic<P, $namespace>,
362 }
363
364 impl<P: $crate::PublicKey> Scheme<P> {
365 pub fn signer(
367 namespace: &[u8],
368 participants: commonware_utils::ordered::BiMap<P, $crate::secp256r1::standard::PublicKey>,
369 private_key: $crate::secp256r1::standard::PrivateKey,
370 ) -> Option<Self> {
371 Some(Self {
372 generic: $crate::secp256r1::certificate::Generic::signer(
373 namespace,
374 participants,
375 private_key,
376 )?,
377 })
378 }
379
380 pub fn verifier(
382 namespace: &[u8],
383 participants: commonware_utils::ordered::BiMap<P, $crate::secp256r1::standard::PublicKey>,
384 ) -> Self {
385 Self {
386 generic: $crate::secp256r1::certificate::Generic::verifier(
387 namespace,
388 participants,
389 ),
390 }
391 }
392 }
393
394 impl<P: $crate::PublicKey> $crate::certificate::Scheme for Scheme<P> {
395 type Subject<'a, D: $crate::Digest> = $subject;
396 type PublicKey = P;
397 type Signature = $crate::secp256r1::standard::Signature;
398 type Certificate = $crate::secp256r1::certificate::Certificate;
399
400 fn me(&self) -> Option<commonware_utils::Participant> {
401 self.generic.me()
402 }
403
404 fn participants(&self) -> &commonware_utils::ordered::Set<Self::PublicKey> {
405 self.generic.participants()
406 }
407
408 fn sign<D: $crate::Digest>(
409 &self,
410 subject: Self::Subject<'_, D>,
411 ) -> Option<$crate::certificate::Attestation<Self>> {
412 self.generic.sign::<_, D>(subject)
413 }
414
415 fn verify_attestation<R, D>(
416 &self,
417 _rng: &mut R,
418 subject: Self::Subject<'_, D>,
419 attestation: &$crate::certificate::Attestation<Self>,
420 _strategy: &impl commonware_parallel::Strategy,
421 ) -> bool
422 where
423 R: rand_core::CryptoRngCore,
424 D: $crate::Digest,
425 {
426 self.generic
427 .verify_attestation::<_, D>(subject, attestation)
428 }
429
430 fn verify_attestations<R, D, I>(
431 &self,
432 rng: &mut R,
433 subject: Self::Subject<'_, D>,
434 attestations: I,
435 _strategy: &impl commonware_parallel::Strategy,
436 ) -> $crate::certificate::Verification<Self>
437 where
438 R: rand_core::CryptoRngCore,
439 D: $crate::Digest,
440 I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
441 {
442 self.generic.verify_attestations::<_, _, D, _>(
443 rng,
444 subject,
445 attestations,
446 )
447 }
448
449 fn assemble<I, M>(
450 &self,
451 attestations: I,
452 _strategy: &impl commonware_parallel::Strategy,
453 ) -> Option<Self::Certificate>
454 where
455 I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
456 M: commonware_utils::Faults,
457 {
458 self.generic.assemble::<Self, _, M>(attestations)
459 }
460
461 fn verify_certificate<R, D, M>(
462 &self,
463 rng: &mut R,
464 subject: Self::Subject<'_, D>,
465 certificate: &Self::Certificate,
466 _strategy: &impl commonware_parallel::Strategy,
467 ) -> bool
468 where
469 R: rand_core::CryptoRngCore,
470 D: $crate::Digest,
471 M: commonware_utils::Faults,
472 {
473 self.generic.verify_certificate::<Self, _, D, M>(
474 rng,
475 subject,
476 certificate,
477 )
478 }
479
480 fn verify_certificates<'a, R, D, I, M>(
481 &self,
482 rng: &mut R,
483 certificates: I,
484 _strategy: &impl commonware_parallel::Strategy,
485 ) -> bool
486 where
487 R: rand_core::CryptoRngCore,
488 D: $crate::Digest,
489 I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
490 M: commonware_utils::Faults,
491 {
492 for (subject, certificate) in certificates {
493 if !self.generic.verify_certificate::<Self, _, D, M>(rng, subject, certificate) {
494 return false;
495 }
496 }
497 true
498 }
499
500 fn is_attributable() -> bool {
501 $crate::secp256r1::certificate::Generic::<P, $namespace>::is_attributable()
502 }
503
504 fn is_batchable() -> bool {
505 $crate::secp256r1::certificate::Generic::<P, $namespace>::is_batchable()
506 }
507
508 fn certificate_codec_config(
509 &self,
510 ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
511 self.generic.certificate_codec_config()
512 }
513
514 fn certificate_codec_config_unbounded() -> <Self::Certificate as commonware_codec::Read>::Cfg {
515 $crate::secp256r1::certificate::Generic::<P, $namespace>::certificate_codec_config_unbounded()
516 }
517 }
518 };
519 }
520}
521
522#[cfg(test)]
523mod tests {
524 use super::*;
525 use crate::{
526 certificate::Scheme as _, impl_certificate_secp256r1, sha256::Digest as Sha256Digest,
527 };
528 use bytes::Bytes;
529 use commonware_codec::{Decode, Encode};
530 use commonware_math::algebra::Random;
531 use commonware_parallel::Sequential;
532 use commonware_utils::{ordered::BiMap, test_rng, Faults, N3f1, TryCollect};
533 use rand_core::CryptoRngCore;
534
535 const NAMESPACE: &[u8] = b"test-secp256r1";
536 const MESSAGE: &[u8] = b"test message";
537
538 #[derive(Clone, Debug)]
540 pub struct TestSubject {
541 pub message: Bytes,
542 }
543
544 impl Subject for TestSubject {
545 type Namespace = Vec<u8>;
546
547 fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] {
548 derived.as_ref()
549 }
550
551 fn message(&self) -> Bytes {
552 self.message.clone()
553 }
554 }
555
556 impl_certificate_secp256r1!(TestSubject, Vec<u8>);
558
559 fn setup_signers(
560 rng: &mut impl CryptoRngCore,
561 n: u32,
562 ) -> (Vec<Scheme<PublicKey>>, Scheme<PublicKey>) {
563 let private_keys: Vec<_> = (0..n).map(|_| PrivateKey::random(&mut *rng)).collect();
564
565 let participants: BiMap<PublicKey, PublicKey> = private_keys
567 .iter()
568 .map(|sk| {
569 let pk = sk.public_key();
570 (pk.clone(), pk)
571 })
572 .try_collect()
573 .unwrap();
574
575 let signers = private_keys
576 .into_iter()
577 .map(|sk| Scheme::signer(NAMESPACE, participants.clone(), sk).unwrap())
578 .collect();
579
580 let verifier = Scheme::verifier(NAMESPACE, participants);
581
582 (signers, verifier)
583 }
584
585 #[test]
586 fn test_is_attributable() {
587 assert!(Generic::<PublicKey, Vec<u8>>::is_attributable());
588 assert!(Scheme::<PublicKey>::is_attributable());
589 }
590
591 #[test]
592 fn test_is_not_batchable() {
593 assert!(!Generic::<PublicKey, Vec<u8>>::is_batchable());
594 assert!(!Scheme::<PublicKey>::is_batchable());
595 }
596
597 #[test]
598 fn test_sign_vote_roundtrip() {
599 let mut rng = test_rng();
600 let (schemes, _) = setup_signers(&mut rng, 4);
601 let scheme = &schemes[0];
602
603 let attestation = scheme
604 .sign::<Sha256Digest>(TestSubject {
605 message: Bytes::from_static(MESSAGE),
606 })
607 .unwrap();
608 assert!(scheme.verify_attestation::<_, Sha256Digest>(
609 &mut rng,
610 TestSubject {
611 message: Bytes::from_static(MESSAGE),
612 },
613 &attestation,
614 &Sequential,
615 ));
616 }
617
618 #[test]
619 fn test_verifier_cannot_sign() {
620 let mut rng = test_rng();
621 let (_, verifier) = setup_signers(&mut rng, 4);
622 assert!(verifier
623 .sign::<Sha256Digest>(TestSubject {
624 message: Bytes::from_static(MESSAGE),
625 })
626 .is_none());
627 }
628
629 #[test]
630 fn test_verify_attestations_filters_invalid() {
631 let mut rng = test_rng();
632 let (schemes, _) = setup_signers(&mut rng, 5);
633 let quorum = N3f1::quorum(schemes.len()) as usize;
634
635 let attestations: Vec<_> = schemes
636 .iter()
637 .take(quorum)
638 .map(|s| {
639 s.sign::<Sha256Digest>(TestSubject {
640 message: Bytes::from_static(MESSAGE),
641 })
642 .unwrap()
643 })
644 .collect();
645
646 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
647 &mut rng,
648 TestSubject {
649 message: Bytes::from_static(MESSAGE),
650 },
651 attestations.clone(),
652 &Sequential,
653 );
654 assert!(result.invalid.is_empty());
655 assert_eq!(result.verified.len(), quorum);
656
657 let mut attestations_corrupted = attestations.clone();
659 attestations_corrupted[0].signer = Participant::new(999);
660 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
661 &mut rng,
662 TestSubject {
663 message: Bytes::from_static(MESSAGE),
664 },
665 attestations_corrupted,
666 &Sequential,
667 );
668 assert_eq!(result.invalid, vec![Participant::new(999)]);
669 assert_eq!(result.verified.len(), quorum - 1);
670
671 let mut attestations_corrupted = attestations;
673 let first_signer = attestations_corrupted[0].signer;
674 attestations_corrupted[0].signature = attestations_corrupted[1].signature.clone();
675 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
676 &mut rng,
677 TestSubject {
678 message: Bytes::from_static(MESSAGE),
679 },
680 attestations_corrupted,
681 &Sequential,
682 );
683 assert_eq!(result.invalid, vec![first_signer]);
685 assert_eq!(result.verified.len(), quorum - 1);
686 }
687
688 #[test]
689 fn test_assemble_certificate() {
690 let mut rng = test_rng();
691 let (schemes, _) = setup_signers(&mut rng, 4);
692 let quorum = N3f1::quorum(schemes.len()) as usize;
693
694 let attestations: Vec<_> = schemes
695 .iter()
696 .take(quorum)
697 .map(|s| {
698 s.sign::<Sha256Digest>(TestSubject {
699 message: Bytes::from_static(MESSAGE),
700 })
701 .unwrap()
702 })
703 .collect();
704
705 let certificate = schemes[0]
706 .assemble::<_, N3f1>(attestations, &Sequential)
707 .unwrap();
708
709 assert_eq!(certificate.signers.count(), quorum);
711 assert_eq!(certificate.signatures.len(), quorum);
712 }
713
714 #[test]
715 fn test_assemble_certificate_sorts_signers() {
716 let mut rng = test_rng();
717 let (schemes, _) = setup_signers(&mut rng, 4);
718
719 let mut indexed: Vec<_> = (0..3).map(|i| (schemes[i].me().unwrap(), i)).collect();
721 indexed.sort_by_key(|(idx, _)| *idx);
722
723 let attestations = vec![
725 schemes[indexed[2].1]
726 .sign::<Sha256Digest>(TestSubject {
727 message: Bytes::from_static(MESSAGE),
728 })
729 .unwrap(),
730 schemes[indexed[1].1]
731 .sign::<Sha256Digest>(TestSubject {
732 message: Bytes::from_static(MESSAGE),
733 })
734 .unwrap(),
735 schemes[indexed[0].1]
736 .sign::<Sha256Digest>(TestSubject {
737 message: Bytes::from_static(MESSAGE),
738 })
739 .unwrap(),
740 ];
741
742 let certificate = schemes[0]
743 .assemble::<_, N3f1>(attestations, &Sequential)
744 .unwrap();
745
746 let expected: Vec<_> = indexed.iter().map(|(idx, _)| *idx).collect();
748 assert_eq!(certificate.signers.iter().collect::<Vec<_>>(), expected);
749 }
750
751 #[test]
752 fn test_verify_certificate() {
753 let mut rng = test_rng();
754 let (schemes, verifier) = setup_signers(&mut rng, 4);
755 let quorum = N3f1::quorum(schemes.len()) as usize;
756
757 let attestations: Vec<_> = schemes
758 .iter()
759 .take(quorum)
760 .map(|s| {
761 s.sign::<Sha256Digest>(TestSubject {
762 message: Bytes::from_static(MESSAGE),
763 })
764 .unwrap()
765 })
766 .collect();
767
768 let certificate = schemes[0]
769 .assemble::<_, N3f1>(attestations, &Sequential)
770 .unwrap();
771
772 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
773 &mut rng,
774 TestSubject {
775 message: Bytes::from_static(MESSAGE),
776 },
777 &certificate,
778 &Sequential,
779 ));
780 }
781
782 #[test]
783 fn test_verify_certificate_detects_corruption() {
784 let mut rng = test_rng();
785 let (schemes, verifier) = setup_signers(&mut rng, 4);
786 let quorum = N3f1::quorum(schemes.len()) as usize;
787
788 let attestations: Vec<_> = schemes
789 .iter()
790 .take(quorum)
791 .map(|s| {
792 s.sign::<Sha256Digest>(TestSubject {
793 message: Bytes::from_static(MESSAGE),
794 })
795 .unwrap()
796 })
797 .collect();
798
799 let certificate = schemes[0]
800 .assemble::<_, N3f1>(attestations, &Sequential)
801 .unwrap();
802
803 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
805 &mut rng,
806 TestSubject {
807 message: Bytes::from_static(MESSAGE),
808 },
809 &certificate,
810 &Sequential,
811 ));
812
813 let mut corrupted = certificate;
815 corrupted.signatures[0] = corrupted.signatures[1].clone();
816 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
817 &mut rng,
818 TestSubject {
819 message: Bytes::from_static(MESSAGE),
820 },
821 &corrupted,
822 &Sequential,
823 ));
824 }
825
826 #[test]
827 fn test_certificate_codec_roundtrip() {
828 let mut rng = test_rng();
829 let (schemes, _) = setup_signers(&mut rng, 4);
830 let quorum = N3f1::quorum(schemes.len()) as usize;
831
832 let attestations: Vec<_> = schemes
833 .iter()
834 .take(quorum)
835 .map(|s| {
836 s.sign::<Sha256Digest>(TestSubject {
837 message: Bytes::from_static(MESSAGE),
838 })
839 .unwrap()
840 })
841 .collect();
842
843 let certificate = schemes[0]
844 .assemble::<_, N3f1>(attestations, &Sequential)
845 .unwrap();
846 let encoded = certificate.encode();
847 let decoded = Certificate::decode_cfg(encoded, &schemes.len()).expect("decode certificate");
848 assert_eq!(decoded, certificate);
849 }
850
851 #[test]
852 fn test_certificate_rejects_sub_quorum() {
853 let mut rng = test_rng();
854 let (schemes, _) = setup_signers(&mut rng, 4);
855 let sub_quorum = 2; let attestations: Vec<_> = schemes
858 .iter()
859 .take(sub_quorum)
860 .map(|s| {
861 s.sign::<Sha256Digest>(TestSubject {
862 message: Bytes::from_static(MESSAGE),
863 })
864 .unwrap()
865 })
866 .collect();
867
868 assert!(schemes[0]
869 .assemble::<_, N3f1>(attestations, &Sequential)
870 .is_none());
871 }
872
873 #[test]
874 fn test_certificate_rejects_invalid_signer() {
875 let mut rng = test_rng();
876 let (schemes, _) = setup_signers(&mut rng, 4);
877 let quorum = N3f1::quorum(schemes.len()) as usize;
878
879 let mut attestations: Vec<_> = schemes
880 .iter()
881 .take(quorum)
882 .map(|s| {
883 s.sign::<Sha256Digest>(TestSubject {
884 message: Bytes::from_static(MESSAGE),
885 })
886 .unwrap()
887 })
888 .collect();
889
890 attestations[0].signer = Participant::new(999);
892
893 assert!(schemes[0]
894 .assemble::<_, N3f1>(attestations, &Sequential)
895 .is_none());
896 }
897
898 #[test]
899 fn test_verify_certificate_rejects_sub_quorum() {
900 let mut rng = test_rng();
901 let (schemes, verifier) = setup_signers(&mut rng, 4);
902 let participants_len = schemes.len();
903
904 let attestations: Vec<_> = schemes
905 .iter()
906 .take(3)
907 .map(|s| {
908 s.sign::<Sha256Digest>(TestSubject {
909 message: Bytes::from_static(MESSAGE),
910 })
911 .unwrap()
912 })
913 .collect();
914
915 let mut certificate = schemes[0]
916 .assemble::<_, N3f1>(attestations, &Sequential)
917 .unwrap();
918
919 let mut signers: Vec<Participant> = certificate.signers.iter().collect();
921 signers.pop();
922 certificate.signers = Signers::from(participants_len, signers);
923 certificate.signatures.pop();
924
925 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
926 &mut rng,
927 TestSubject {
928 message: Bytes::from_static(MESSAGE),
929 },
930 &certificate,
931 &Sequential,
932 ));
933 }
934
935 #[test]
936 fn test_verify_certificate_rejects_mismatched_signature_count() {
937 let mut rng = test_rng();
938 let (schemes, verifier) = setup_signers(&mut rng, 4);
939
940 let attestations: Vec<_> = schemes
941 .iter()
942 .take(3)
943 .map(|s| {
944 s.sign::<Sha256Digest>(TestSubject {
945 message: Bytes::from_static(MESSAGE),
946 })
947 .unwrap()
948 })
949 .collect();
950
951 let mut certificate = schemes[0]
952 .assemble::<_, N3f1>(attestations, &Sequential)
953 .unwrap();
954
955 certificate.signatures.pop();
957
958 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
959 &mut rng,
960 TestSubject {
961 message: Bytes::from_static(MESSAGE),
962 },
963 &certificate,
964 &Sequential,
965 ));
966 }
967
968 #[test]
969 fn test_verify_certificates_batch() {
970 let mut rng = test_rng();
971 let (schemes, verifier) = setup_signers(&mut rng, 4);
972 let quorum = N3f1::quorum(schemes.len()) as usize;
973
974 let messages: Vec<Bytes> = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()]
975 .into_iter()
976 .map(Bytes::copy_from_slice)
977 .collect();
978 let mut certificates = Vec::new();
979
980 for msg in &messages {
981 let attestations: Vec<_> = schemes
982 .iter()
983 .take(quorum)
984 .map(|s| {
985 s.sign::<Sha256Digest>(TestSubject {
986 message: msg.clone(),
987 })
988 .unwrap()
989 })
990 .collect();
991 certificates.push(
992 schemes[0]
993 .assemble::<_, N3f1>(attestations, &Sequential)
994 .unwrap(),
995 );
996 }
997
998 let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
999 (
1000 TestSubject {
1001 message: msg.clone(),
1002 },
1003 cert,
1004 )
1005 });
1006
1007 assert!(verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1008 &mut rng,
1009 certs_iter,
1010 &Sequential
1011 ));
1012 }
1013
1014 #[test]
1015 fn test_verify_certificates_batch_detects_failure() {
1016 let mut rng = test_rng();
1017 let (schemes, verifier) = setup_signers(&mut rng, 4);
1018 let quorum = N3f1::quorum(schemes.len()) as usize;
1019
1020 let messages: Vec<Bytes> = [b"msg1".as_slice(), b"msg2".as_slice()]
1021 .into_iter()
1022 .map(Bytes::copy_from_slice)
1023 .collect();
1024 let mut certificates = Vec::new();
1025
1026 for msg in &messages {
1027 let attestations: Vec<_> = schemes
1028 .iter()
1029 .take(quorum)
1030 .map(|s| {
1031 s.sign::<Sha256Digest>(TestSubject {
1032 message: msg.clone(),
1033 })
1034 .unwrap()
1035 })
1036 .collect();
1037 certificates.push(
1038 schemes[0]
1039 .assemble::<_, N3f1>(attestations, &Sequential)
1040 .unwrap(),
1041 );
1042 }
1043
1044 certificates[1].signatures[0] = certificates[1].signatures[1].clone();
1046
1047 let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
1048 (
1049 TestSubject {
1050 message: msg.clone(),
1051 },
1052 cert,
1053 )
1054 });
1055
1056 assert!(!verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1057 &mut rng,
1058 certs_iter,
1059 &Sequential
1060 ));
1061 }
1062
1063 #[test]
1064 #[should_panic(expected = "duplicate signer index")]
1065 fn test_assemble_certificate_rejects_duplicate_signers() {
1066 let mut rng = test_rng();
1067 let (schemes, _) = setup_signers(&mut rng, 4);
1068
1069 let mut attestations: Vec<_> = schemes
1070 .iter()
1071 .take(3)
1072 .map(|s| {
1073 s.sign::<Sha256Digest>(TestSubject {
1074 message: Bytes::from_static(MESSAGE),
1075 })
1076 .unwrap()
1077 })
1078 .collect();
1079
1080 attestations.push(attestations.last().unwrap().clone());
1082
1083 schemes[0].assemble::<_, N3f1>(attestations, &Sequential);
1085 }
1086
1087 #[test]
1088 fn test_scheme_clone_and_verifier() {
1089 let mut rng = test_rng();
1090 let (schemes, _) = setup_signers(&mut rng, 4);
1091 let participants = schemes[0].generic.participants.clone();
1092
1093 let signer = schemes[0].clone();
1095 assert!(
1096 signer
1097 .sign::<Sha256Digest>(TestSubject {
1098 message: Bytes::from_static(MESSAGE),
1099 })
1100 .is_some(),
1101 "signer should produce votes"
1102 );
1103
1104 let verifier = Scheme::verifier(NAMESPACE, participants);
1106 assert!(
1107 verifier
1108 .sign::<Sha256Digest>(TestSubject {
1109 message: Bytes::from_static(MESSAGE),
1110 })
1111 .is_none(),
1112 "verifier should not produce votes"
1113 );
1114 }
1115
1116 #[test]
1117 fn test_certificate_decode_validation() {
1118 let mut rng = test_rng();
1119 let (schemes, _) = setup_signers(&mut rng, 4);
1120 let participants_len = schemes.len();
1121
1122 let attestations: Vec<_> = schemes
1123 .iter()
1124 .take(3)
1125 .map(|s| {
1126 s.sign::<Sha256Digest>(TestSubject {
1127 message: Bytes::from_static(MESSAGE),
1128 })
1129 .unwrap()
1130 })
1131 .collect();
1132
1133 let certificate = schemes[0]
1134 .assemble::<_, N3f1>(attestations, &Sequential)
1135 .unwrap();
1136
1137 let encoded = certificate.encode();
1139 let decoded =
1140 Certificate::decode_cfg(encoded, &participants_len).expect("decode certificate");
1141 assert_eq!(decoded, certificate);
1142
1143 let empty = Certificate {
1145 signers: Signers::from(participants_len, std::iter::empty::<Participant>()),
1146 signatures: Vec::new(),
1147 };
1148 assert!(Certificate::decode_cfg(empty.encode(), &participants_len).is_err());
1149
1150 let mismatched = Certificate {
1152 signers: Signers::from(participants_len, [Participant::new(0), Participant::new(1)]),
1153 signatures: vec![certificate.signatures[0].clone()],
1154 };
1155 assert!(Certificate::decode_cfg(mismatched.encode(), &participants_len).is_err());
1156
1157 let mut signers = certificate.signers.iter().collect::<Vec<_>>();
1159 signers.push(Participant::from_usize(participants_len));
1160 let mut sigs = certificate.signatures.clone();
1161 sigs.push(certificate.signatures[0].clone());
1162 let extended = Certificate {
1163 signers: Signers::from(participants_len + 1, signers),
1164 signatures: sigs,
1165 };
1166 assert!(Certificate::decode_cfg(extended.encode(), &participants_len).is_err());
1167 }
1168
1169 #[test]
1170 fn test_verify_certificate_rejects_unknown_signer() {
1171 let mut rng = test_rng();
1172 let (schemes, verifier) = setup_signers(&mut rng, 4);
1173 let participants_len = schemes.len();
1174
1175 let attestations: Vec<_> = schemes
1176 .iter()
1177 .take(3)
1178 .map(|s| {
1179 s.sign::<Sha256Digest>(TestSubject {
1180 message: Bytes::from_static(MESSAGE),
1181 })
1182 .unwrap()
1183 })
1184 .collect();
1185
1186 let mut certificate = schemes[0]
1187 .assemble::<_, N3f1>(attestations, &Sequential)
1188 .unwrap();
1189
1190 let mut signers: Vec<Participant> = certificate.signers.iter().collect();
1192 signers.push(Participant::from_usize(participants_len));
1193 certificate.signers = Signers::from(participants_len + 1, signers);
1194 certificate
1195 .signatures
1196 .push(certificate.signatures[0].clone());
1197
1198 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1199 &mut rng,
1200 TestSubject {
1201 message: Bytes::from_static(MESSAGE),
1202 },
1203 &certificate,
1204 &Sequential,
1205 ));
1206 }
1207
1208 #[test]
1209 fn test_verify_certificate_rejects_invalid_certificate_signers_size() {
1210 let mut rng = test_rng();
1211 let (schemes, verifier) = setup_signers(&mut rng, 4);
1212 let participants_len = schemes.len();
1213
1214 let attestations: Vec<_> = schemes
1215 .iter()
1216 .take(3)
1217 .map(|s| {
1218 s.sign::<Sha256Digest>(TestSubject {
1219 message: Bytes::from_static(MESSAGE),
1220 })
1221 .unwrap()
1222 })
1223 .collect();
1224
1225 let mut certificate = schemes[0]
1226 .assemble::<_, N3f1>(attestations, &Sequential)
1227 .unwrap();
1228
1229 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1231 &mut rng,
1232 TestSubject {
1233 message: Bytes::from_static(MESSAGE),
1234 },
1235 &certificate,
1236 &Sequential,
1237 ));
1238
1239 let signers: Vec<Participant> = certificate.signers.iter().collect();
1241 certificate.signers = Signers::from(participants_len + 1, signers);
1242
1243 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1245 &mut rng,
1246 TestSubject {
1247 message: Bytes::from_static(MESSAGE),
1248 },
1249 &certificate,
1250 &Sequential,
1251 ));
1252 }
1253
1254 #[test]
1255 fn test_verify_certificate_rejects_signers_size_mismatch() {
1256 let mut rng = test_rng();
1257 let (schemes, verifier) = setup_signers(&mut rng, 4);
1258 let participants_len = schemes.len();
1259
1260 let attestations: Vec<_> = schemes
1261 .iter()
1262 .take(3)
1263 .map(|s| {
1264 s.sign::<Sha256Digest>(TestSubject {
1265 message: Bytes::from_static(MESSAGE),
1266 })
1267 .unwrap()
1268 })
1269 .collect();
1270
1271 let mut certificate = schemes[0]
1272 .assemble::<_, N3f1>(attestations, &Sequential)
1273 .unwrap();
1274
1275 let signers: Vec<Participant> = certificate.signers.iter().collect();
1277 certificate.signers = Signers::from(participants_len + 1, signers);
1278 certificate
1279 .signatures
1280 .push(certificate.signatures[0].clone());
1281
1282 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1283 &mut rng,
1284 TestSubject {
1285 message: Bytes::from_static(MESSAGE),
1286 },
1287 &certificate,
1288 &Sequential,
1289 ));
1290 }
1291
1292 #[cfg(feature = "arbitrary")]
1293 mod conformance {
1294 use super::*;
1295 use commonware_codec::conformance::CodecConformance;
1296
1297 commonware_conformance::conformance_tests! {
1298 CodecConformance<Certificate>,
1299 }
1300 }
1301}