1#[cfg(feature = "mocks")]
7pub mod mocks;
8
9use crate::{
10 bls12381::primitives::{
11 group::Private,
12 ops::{
13 aggregate_signatures, aggregate_verify_multiple_public_keys, compute_public,
14 sign_message, verify_message,
15 },
16 variant::Variant,
17 },
18 certificate::{Attestation, Scheme, Signers, Subject, Verification},
19 Digest, PublicKey,
20};
21#[cfg(not(feature = "std"))]
22use alloc::{collections::BTreeSet, vec::Vec};
23use bytes::{Buf, BufMut};
24use commonware_codec::{EncodeSize, Error, Read, ReadExt, Write};
25use commonware_utils::ordered::{BiMap, Quorum, Set};
26use rand::{CryptoRng, Rng};
27#[cfg(feature = "std")]
28use std::collections::BTreeSet;
29
30#[derive(Clone, Debug)]
36pub struct Generic<P: PublicKey, V: Variant> {
37 pub participants: BiMap<P, V::Public>,
39 pub signer: Option<(u32, Private)>,
41}
42
43impl<P: PublicKey, V: Variant> Generic<P, V> {
44 pub fn signer(participants: BiMap<P, V::Public>, private_key: Private) -> Option<Self> {
53 let public_key = compute_public::<V>(&private_key);
54 let signer = participants
55 .values()
56 .iter()
57 .position(|p| p == &public_key)
58 .map(|index| (index as u32, private_key))?;
59
60 Some(Self {
61 participants,
62 signer: Some(signer),
63 })
64 }
65
66 pub const fn verifier(participants: BiMap<P, V::Public>) -> Self {
72 Self {
73 participants,
74 signer: None,
75 }
76 }
77
78 pub const fn participants(&self) -> &Set<P> {
80 self.participants.keys()
81 }
82
83 pub fn me(&self) -> Option<u32> {
85 self.signer.as_ref().map(|(index, _)| *index)
86 }
87
88 pub fn sign<S, D>(&self, namespace: &[u8], subject: S::Subject<'_, D>) -> Option<Attestation<S>>
90 where
91 S: Scheme<Signature = V::Signature>,
92 D: Digest,
93 {
94 let (index, private_key) = self.signer.as_ref()?;
95
96 let (namespace, message) = subject.namespace_and_message(namespace);
97 let signature = sign_message::<V>(private_key, Some(namespace.as_ref()), message.as_ref());
98
99 Some(Attestation {
100 signer: *index,
101 signature,
102 })
103 }
104
105 pub fn verify_attestation<S, D>(
107 &self,
108 namespace: &[u8],
109 subject: S::Subject<'_, D>,
110 attestation: &Attestation<S>,
111 ) -> bool
112 where
113 S: Scheme<Signature = V::Signature>,
114 D: Digest,
115 {
116 let Some(public_key) = self.participants.value(attestation.signer as usize) else {
117 return false;
118 };
119
120 let (namespace, message) = subject.namespace_and_message(namespace);
121 verify_message::<V>(
122 public_key,
123 Some(namespace.as_ref()),
124 message.as_ref(),
125 &attestation.signature,
126 )
127 .is_ok()
128 }
129
130 pub fn verify_attestations<S, R, D, I>(
132 &self,
133 _rng: &mut R,
134 namespace: &[u8],
135 subject: S::Subject<'_, D>,
136 attestations: I,
137 ) -> Verification<S>
138 where
139 S: Scheme<Signature = V::Signature>,
140 R: Rng + CryptoRng,
141 D: Digest,
142 I: IntoIterator<Item = Attestation<S>>,
143 {
144 let mut invalid = BTreeSet::new();
145 let mut candidates = Vec::new();
146 let mut publics = Vec::new();
147 let mut sigs = Vec::new();
148 for attestation in attestations.into_iter() {
149 let Some(public_key) = self.participants.value(attestation.signer as usize) else {
150 invalid.insert(attestation.signer);
151 continue;
152 };
153
154 publics.push(*public_key);
155 sigs.push(attestation.signature);
156 candidates.push(attestation);
157 }
158
159 if candidates.is_empty() {
161 return Verification::new(candidates, invalid.into_iter().collect());
162 }
163
164 let (namespace, message) = subject.namespace_and_message(namespace);
166 if aggregate_verify_multiple_public_keys::<V, _>(
167 publics.iter(),
168 Some(namespace.as_ref()),
169 message.as_ref(),
170 &aggregate_signatures::<V, _>(sigs.iter()),
171 )
172 .is_err()
173 {
174 for (attestation, public_key) in candidates.iter().zip(publics.iter()) {
175 if verify_message::<V>(
176 public_key,
177 Some(namespace.as_ref()),
178 message.as_ref(),
179 &attestation.signature,
180 )
181 .is_err()
182 {
183 invalid.insert(attestation.signer);
184 }
185 }
186 }
187
188 let verified = candidates
190 .into_iter()
191 .filter(|attestation| !invalid.contains(&attestation.signer))
192 .collect();
193
194 Verification::new(verified, invalid.into_iter().collect())
195 }
196
197 pub fn assemble<S, I>(&self, attestations: I) -> Option<Certificate<V>>
199 where
200 S: Scheme<Signature = V::Signature>,
201 I: IntoIterator<Item = Attestation<S>>,
202 {
203 let mut entries = Vec::new();
205 for Attestation { signer, signature } in attestations {
206 if signer as usize >= self.participants.len() {
207 return None;
208 }
209
210 entries.push((signer, signature));
211 }
212 if entries.len() < self.participants.quorum() as usize {
213 return None;
214 }
215
216 let (signers, signatures): (Vec<_>, Vec<_>) = entries.into_iter().unzip();
218 let signers = Signers::from(self.participants.len(), signers);
219 let signature = aggregate_signatures::<V, _>(signatures.iter());
220
221 Some(Certificate { signers, signature })
222 }
223
224 pub fn verify_certificate<S, R, D>(
226 &self,
227 _rng: &mut R,
228 namespace: &[u8],
229 subject: S::Subject<'_, D>,
230 certificate: &Certificate<V>,
231 ) -> bool
232 where
233 S: Scheme,
234 R: Rng + CryptoRng,
235 D: Digest,
236 {
237 if certificate.signers.len() != self.participants.len() {
239 return false;
240 }
241
242 if certificate.signers.count() < self.participants.quorum() as usize {
244 return false;
245 }
246
247 let mut publics = Vec::with_capacity(certificate.signers.count());
249 for signer in certificate.signers.iter() {
250 let Some(public_key) = self.participants.value(signer as usize) else {
251 return false;
252 };
253
254 publics.push(*public_key);
255 }
256
257 let (namespace, message) = subject.namespace_and_message(namespace);
259 aggregate_verify_multiple_public_keys::<V, _>(
260 publics.iter(),
261 Some(namespace.as_ref()),
262 message.as_ref(),
263 &certificate.signature,
264 )
265 .is_ok()
266 }
267
268 pub fn verify_certificates<'a, S, R, D, I>(
270 &self,
271 rng: &mut R,
272 namespace: &[u8],
273 certificates: I,
274 ) -> bool
275 where
276 S: Scheme,
277 R: Rng + CryptoRng,
278 D: Digest,
279 I: Iterator<Item = (S::Subject<'a, D>, &'a Certificate<V>)>,
280 {
281 for (subject, certificate) in certificates {
282 if !self.verify_certificate::<S, R, D>(rng, namespace, subject, certificate) {
283 return false;
284 }
285 }
286 true
287 }
288
289 pub const fn is_attributable(&self) -> bool {
290 true
291 }
292
293 pub const fn certificate_codec_config(&self) -> <Certificate<V> as Read>::Cfg {
294 self.participants.len()
295 }
296
297 pub const fn certificate_codec_config_unbounded() -> <Certificate<V> as Read>::Cfg {
298 u32::MAX as usize
299 }
300}
301
302#[derive(Clone, Debug, PartialEq, Eq, Hash)]
305pub struct Certificate<V: Variant> {
306 pub signers: Signers,
308 pub signature: V::Signature,
310}
311
312impl<V: Variant> Write for Certificate<V> {
313 fn write(&self, writer: &mut impl BufMut) {
314 self.signers.write(writer);
315 self.signature.write(writer);
316 }
317}
318
319impl<V: Variant> EncodeSize for Certificate<V> {
320 fn encode_size(&self) -> usize {
321 self.signers.encode_size() + self.signature.encode_size()
322 }
323}
324
325impl<V: Variant> Read for Certificate<V> {
326 type Cfg = usize;
327
328 fn read_cfg(reader: &mut impl Buf, participants: &usize) -> Result<Self, Error> {
329 let signers = Signers::read_cfg(reader, participants)?;
330 if signers.count() == 0 {
331 return Err(Error::Invalid(
332 "cryptography::bls12381::certificate::multisig::Certificate",
333 "Certificate contains no signers",
334 ));
335 }
336
337 let signature = V::Signature::read(reader)?;
338
339 Ok(Self { signers, signature })
340 }
341}
342
343#[cfg(feature = "arbitrary")]
344impl<V: Variant> arbitrary::Arbitrary<'_> for Certificate<V>
345where
346 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
347{
348 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
349 let signers = Signers::arbitrary(u)?;
350 let signature = V::Signature::arbitrary(u)?;
351 Ok(Self { signers, signature })
352 }
353}
354
355mod macros {
356 #[macro_export]
367 macro_rules! impl_certificate_bls12381_multisig {
368 ($subject:ty) => {
369 #[cfg(feature = "mocks")]
374 #[allow(dead_code)]
375 pub fn fixture<V, R>(
376 rng: &mut R,
377 n: u32,
378 ) -> $crate::certificate::mocks::Fixture<Scheme<$crate::ed25519::PublicKey, V>>
379 where
380 V: $crate::bls12381::primitives::variant::Variant,
381 R: rand::RngCore + rand::CryptoRng,
382 {
383 $crate::bls12381::certificate::multisig::mocks::fixture::<_, V, _>(
384 rng,
385 n,
386 Scheme::signer,
387 Scheme::verifier,
388 )
389 }
390
391 #[derive(Clone, Debug)]
393 pub struct Scheme<
394 P: $crate::PublicKey,
395 V: $crate::bls12381::primitives::variant::Variant,
396 > {
397 generic: $crate::bls12381::certificate::multisig::Generic<P, V>,
398 }
399
400 impl<
401 P: $crate::PublicKey,
402 V: $crate::bls12381::primitives::variant::Variant,
403 > Scheme<P, V> {
404 pub fn signer(
406 participants: commonware_utils::ordered::BiMap<P, V::Public>,
407 private_key: $crate::bls12381::primitives::group::Private,
408 ) -> Option<Self> {
409 Some(Self {
410 generic: $crate::bls12381::certificate::multisig::Generic::signer(
411 participants,
412 private_key,
413 )?,
414 })
415 }
416
417 pub const fn verifier(
419 participants: commonware_utils::ordered::BiMap<P, V::Public>,
420 ) -> Self {
421 Self {
422 generic: $crate::bls12381::certificate::multisig::Generic::verifier(
423 participants,
424 ),
425 }
426 }
427 }
428
429 impl<
430 P: $crate::PublicKey,
431 V: $crate::bls12381::primitives::variant::Variant + Send + Sync,
432 > $crate::certificate::Scheme for Scheme<P, V> {
433 type Subject<'a, D: $crate::Digest> = $subject;
434 type PublicKey = P;
435 type Signature = V::Signature;
436 type Certificate = $crate::bls12381::certificate::multisig::Certificate<V>;
437
438 fn me(&self) -> Option<u32> {
439 self.generic.me()
440 }
441
442 fn participants(&self) -> &commonware_utils::ordered::Set<Self::PublicKey> {
443 self.generic.participants()
444 }
445
446 fn sign<D: $crate::Digest>(
447 &self,
448 namespace: &[u8],
449 subject: Self::Subject<'_, D>,
450 ) -> Option<$crate::certificate::Attestation<Self>> {
451 self.generic.sign::<_, D>(namespace, subject)
452 }
453
454 fn verify_attestation<D: $crate::Digest>(
455 &self,
456 namespace: &[u8],
457 subject: Self::Subject<'_, D>,
458 attestation: &$crate::certificate::Attestation<Self>,
459 ) -> bool {
460 self.generic.verify_attestation::<_, D>(namespace, subject, attestation)
461 }
462
463 fn verify_attestations<R, D, I>(
464 &self,
465 rng: &mut R,
466 namespace: &[u8],
467 subject: Self::Subject<'_, D>,
468 attestations: I,
469 ) -> $crate::certificate::Verification<Self>
470 where
471 R: rand::Rng + rand::CryptoRng,
472 D: $crate::Digest,
473 I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
474 {
475 self.generic.verify_attestations::<_, _, D, _>(rng, namespace, subject, attestations)
476 }
477
478 fn assemble<I>(&self, attestations: I) -> Option<Self::Certificate>
479 where
480 I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
481 {
482 self.generic.assemble(attestations)
483 }
484
485 fn verify_certificate<
486 R: rand::Rng + rand::CryptoRng,
487 D: $crate::Digest,
488 >(
489 &self,
490 rng: &mut R,
491 namespace: &[u8],
492 subject: Self::Subject<'_, D>,
493 certificate: &Self::Certificate,
494 ) -> bool {
495 self.generic.verify_certificate::<Self, _, D>(rng, namespace, subject, certificate)
496 }
497
498 fn verify_certificates<'a, R, D, I>(
499 &self,
500 rng: &mut R,
501 namespace: &[u8],
502 certificates: I,
503 ) -> bool
504 where
505 R: rand::Rng + rand::CryptoRng,
506 D: $crate::Digest,
507 I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
508 {
509 self.generic.verify_certificates::<Self, _, D, _>(rng, namespace, certificates)
510 }
511
512 fn is_attributable(&self) -> bool {
513 self.generic.is_attributable()
514 }
515
516 fn certificate_codec_config(
517 &self,
518 ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
519 self.generic.certificate_codec_config()
520 }
521
522 fn certificate_codec_config_unbounded() -> <Self::Certificate as commonware_codec::Read>::Cfg {
523 $crate::bls12381::certificate::multisig::Generic::<P, V>::certificate_codec_config_unbounded()
524 }
525 }
526 };
527 }
528}
529
530#[cfg(test)]
531mod tests {
532 use super::*;
533 use crate::{
534 bls12381::primitives::{
535 group::Private,
536 ops::compute_public,
537 variant::{MinPk, MinSig, Variant},
538 },
539 certificate::Scheme as _,
540 ed25519::{self, PrivateKey as Ed25519PrivateKey},
541 impl_certificate_bls12381_multisig,
542 sha256::Digest as Sha256Digest,
543 Signer as _,
544 };
545 use bytes::Bytes;
546 use commonware_codec::{Decode, Encode};
547 use commonware_math::algebra::{Additive, Random};
548 use commonware_utils::{ordered::BiMap, quorum, TryCollect};
549 use rand::{rngs::StdRng, thread_rng, SeedableRng};
550
551 const NAMESPACE: &[u8] = b"test-bls12381-multisig";
552 const MESSAGE: &[u8] = b"test message";
553
554 #[derive(Clone, Debug)]
556 pub struct TestSubject<'a> {
557 pub message: &'a [u8],
558 }
559
560 impl<'a> Subject for TestSubject<'a> {
561 fn namespace_and_message(&self, namespace: &[u8]) -> (Bytes, Bytes) {
562 (namespace.to_vec().into(), self.message.to_vec().into())
563 }
564 }
565
566 impl_certificate_bls12381_multisig!(TestSubject<'a>);
568
569 fn setup_signers<V: Variant>(
570 n: u32,
571 seed: u64,
572 ) -> (
573 Vec<Scheme<ed25519::PublicKey, V>>,
574 Scheme<ed25519::PublicKey, V>,
575 ) {
576 let mut rng = StdRng::seed_from_u64(seed);
577
578 let identity_keys: Vec<_> = (0..n)
580 .map(|_| Ed25519PrivateKey::random(&mut rng))
581 .collect();
582 let consensus_keys: Vec<Private> = (0..n).map(|_| Private::random(&mut rng)).collect();
583
584 let participants: BiMap<ed25519::PublicKey, V::Public> = identity_keys
586 .iter()
587 .zip(consensus_keys.iter())
588 .map(|(id_sk, cons_sk)| (id_sk.public_key(), compute_public::<V>(cons_sk)))
589 .try_collect()
590 .unwrap();
591
592 let signers = consensus_keys
593 .into_iter()
594 .map(|sk| Scheme::signer(participants.clone(), sk).unwrap())
595 .collect();
596
597 let verifier = Scheme::verifier(participants);
598
599 (signers, verifier)
600 }
601
602 fn test_sign_vote_roundtrip<V: Variant + Send + Sync>() {
603 let (schemes, _) = setup_signers::<V>(4, 42);
604 let scheme = &schemes[0];
605
606 let attestation = scheme
607 .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
608 .unwrap();
609 assert!(scheme.verify_attestation::<Sha256Digest>(
610 NAMESPACE,
611 TestSubject { message: MESSAGE },
612 &attestation
613 ));
614 }
615
616 #[test]
617 fn test_sign_vote_roundtrip_variants() {
618 test_sign_vote_roundtrip::<MinPk>();
619 test_sign_vote_roundtrip::<MinSig>();
620 }
621
622 fn test_verifier_cannot_sign<V: Variant + Send + Sync>() {
623 let (_, verifier) = setup_signers::<V>(4, 43);
624 assert!(verifier
625 .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
626 .is_none());
627 }
628
629 #[test]
630 fn test_verifier_cannot_sign_variants() {
631 test_verifier_cannot_sign::<MinPk>();
632 test_verifier_cannot_sign::<MinSig>();
633 }
634
635 fn test_verify_attestations_filters_invalid<V: Variant + Send + Sync>() {
636 let (schemes, _) = setup_signers::<V>(5, 44);
637 let quorum = quorum(schemes.len() as u32) as usize;
638
639 let attestations: Vec<_> = schemes
640 .iter()
641 .take(quorum)
642 .map(|s| {
643 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
644 .unwrap()
645 })
646 .collect();
647
648 let mut rng = StdRng::seed_from_u64(45);
649 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
650 &mut rng,
651 NAMESPACE,
652 TestSubject { message: MESSAGE },
653 attestations.clone(),
654 );
655 assert!(result.invalid.is_empty());
656 assert_eq!(result.verified.len(), quorum);
657
658 let mut attestations_corrupted = attestations.clone();
660 attestations_corrupted[0].signer = 999;
661 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
662 &mut rng,
663 NAMESPACE,
664 TestSubject { message: MESSAGE },
665 attestations_corrupted,
666 );
667 assert_eq!(result.invalid, vec![999]);
668 assert_eq!(result.verified.len(), quorum - 1);
669
670 let mut attestations_corrupted = attestations;
672 attestations_corrupted[0].signature = attestations_corrupted[1].signature;
673 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
674 &mut rng,
675 NAMESPACE,
676 TestSubject { message: MESSAGE },
677 attestations_corrupted,
678 );
679 assert_eq!(result.invalid.len(), 1);
680 assert_eq!(result.verified.len(), quorum - 1);
681 }
682
683 #[test]
684 fn test_verify_attestations_filters_invalid_variants() {
685 test_verify_attestations_filters_invalid::<MinPk>();
686 test_verify_attestations_filters_invalid::<MinSig>();
687 }
688
689 fn test_assemble_certificate<V: Variant + Send + Sync>() {
690 let (schemes, _) = setup_signers::<V>(4, 46);
691 let quorum = quorum(schemes.len() as u32) as usize;
692
693 let attestations: Vec<_> = schemes
694 .iter()
695 .take(quorum)
696 .map(|s| {
697 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
698 .unwrap()
699 })
700 .collect();
701
702 let certificate = schemes[0].assemble(attestations).unwrap();
703 assert_eq!(certificate.signers.count(), quorum);
704 }
705
706 #[test]
707 fn test_assemble_certificate_variants() {
708 test_assemble_certificate::<MinPk>();
709 test_assemble_certificate::<MinSig>();
710 }
711
712 fn test_assemble_certificate_sorts_signers<V: Variant + Send + Sync>() {
713 let (schemes, _) = setup_signers::<V>(4, 47);
714
715 let attestations = vec![
717 schemes[2]
718 .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
719 .unwrap(),
720 schemes[0]
721 .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
722 .unwrap(),
723 schemes[1]
724 .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
725 .unwrap(),
726 ];
727
728 let certificate = schemes[0].assemble(attestations).unwrap();
729 assert_eq!(
730 certificate.signers.iter().collect::<Vec<_>>(),
731 vec![0, 1, 2]
732 );
733 }
734
735 #[test]
736 fn test_assemble_certificate_sorts_signers_variants() {
737 test_assemble_certificate_sorts_signers::<MinPk>();
738 test_assemble_certificate_sorts_signers::<MinSig>();
739 }
740
741 fn test_verify_certificate<V: Variant + Send + Sync>() {
742 let (schemes, verifier) = setup_signers::<V>(4, 48);
743 let quorum = quorum(schemes.len() as u32) as usize;
744
745 let attestations: Vec<_> = schemes
746 .iter()
747 .take(quorum)
748 .map(|s| {
749 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
750 .unwrap()
751 })
752 .collect();
753
754 let certificate = schemes[0].assemble(attestations).unwrap();
755
756 let mut rng = StdRng::seed_from_u64(49);
757 assert!(verifier.verify_certificate::<_, Sha256Digest>(
758 &mut rng,
759 NAMESPACE,
760 TestSubject { message: MESSAGE },
761 &certificate
762 ));
763 }
764
765 #[test]
766 fn test_verify_certificate_variants() {
767 test_verify_certificate::<MinPk>();
768 test_verify_certificate::<MinSig>();
769 }
770
771 fn test_verify_certificate_detects_corruption<V: Variant + Send + Sync>() {
772 let (schemes, verifier) = setup_signers::<V>(4, 50);
773 let quorum = quorum(schemes.len() as u32) as usize;
774
775 let attestations: Vec<_> = schemes
776 .iter()
777 .take(quorum)
778 .map(|s| {
779 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
780 .unwrap()
781 })
782 .collect();
783
784 let certificate = schemes[0].assemble(attestations).unwrap();
785
786 assert!(verifier.verify_certificate::<_, Sha256Digest>(
788 &mut thread_rng(),
789 NAMESPACE,
790 TestSubject { message: MESSAGE },
791 &certificate
792 ));
793
794 let mut corrupted = certificate;
796 corrupted.signature = V::Signature::zero();
797 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
798 &mut thread_rng(),
799 NAMESPACE,
800 TestSubject { message: MESSAGE },
801 &corrupted
802 ));
803 }
804
805 #[test]
806 fn test_verify_certificate_detects_corruption_variants() {
807 test_verify_certificate_detects_corruption::<MinPk>();
808 test_verify_certificate_detects_corruption::<MinSig>();
809 }
810
811 fn test_certificate_codec_roundtrip<V: Variant + Send + Sync>() {
812 let (schemes, _) = setup_signers::<V>(4, 51);
813 let quorum = quorum(schemes.len() as u32) as usize;
814
815 let attestations: Vec<_> = schemes
816 .iter()
817 .take(quorum)
818 .map(|s| {
819 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
820 .unwrap()
821 })
822 .collect();
823
824 let certificate = schemes[0].assemble(attestations).unwrap();
825 let encoded = certificate.encode();
826 let decoded =
827 Certificate::<V>::decode_cfg(encoded, &schemes.len()).expect("decode certificate");
828 assert_eq!(decoded, certificate);
829 }
830
831 #[test]
832 fn test_certificate_codec_roundtrip_variants() {
833 test_certificate_codec_roundtrip::<MinPk>();
834 test_certificate_codec_roundtrip::<MinSig>();
835 }
836
837 fn test_certificate_rejects_sub_quorum<V: Variant + Send + Sync>() {
838 let (schemes, _) = setup_signers::<V>(4, 52);
839 let sub_quorum = 2; let attestations: Vec<_> = schemes
842 .iter()
843 .take(sub_quorum)
844 .map(|s| {
845 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
846 .unwrap()
847 })
848 .collect();
849
850 assert!(schemes[0].assemble(attestations).is_none());
851 }
852
853 #[test]
854 fn test_certificate_rejects_sub_quorum_variants() {
855 test_certificate_rejects_sub_quorum::<MinPk>();
856 test_certificate_rejects_sub_quorum::<MinSig>();
857 }
858
859 fn test_certificate_rejects_invalid_signer<V: Variant + Send + Sync>() {
860 let (schemes, _) = setup_signers::<V>(4, 53);
861 let quorum = quorum(schemes.len() as u32) as usize;
862
863 let mut attestations: Vec<_> = schemes
864 .iter()
865 .take(quorum)
866 .map(|s| {
867 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
868 .unwrap()
869 })
870 .collect();
871
872 attestations[0].signer = 999;
874
875 assert!(schemes[0].assemble(attestations).is_none());
876 }
877
878 #[test]
879 fn test_certificate_rejects_invalid_signer_variants() {
880 test_certificate_rejects_invalid_signer::<MinPk>();
881 test_certificate_rejects_invalid_signer::<MinSig>();
882 }
883
884 fn test_verify_certificate_rejects_sub_quorum<V: Variant + Send + Sync>() {
885 let (schemes, verifier) = setup_signers::<V>(4, 54);
886 let participants_len = schemes.len();
887
888 let attestations: Vec<_> = schemes
889 .iter()
890 .take(3)
891 .map(|s| {
892 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
893 .unwrap()
894 })
895 .collect();
896
897 let mut certificate = schemes[0].assemble(attestations).unwrap();
898
899 let mut signers: Vec<u32> = certificate.signers.iter().collect();
901 signers.pop();
902 certificate.signers = Signers::from(participants_len, signers);
903
904 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
905 &mut thread_rng(),
906 NAMESPACE,
907 TestSubject { message: MESSAGE },
908 &certificate
909 ));
910 }
911
912 #[test]
913 fn test_verify_certificate_rejects_sub_quorum_variants() {
914 test_verify_certificate_rejects_sub_quorum::<MinPk>();
915 test_verify_certificate_rejects_sub_quorum::<MinSig>();
916 }
917
918 fn test_verify_certificate_rejects_signers_size_mismatch<V: Variant + Send + Sync>() {
919 let (schemes, verifier) = setup_signers::<V>(4, 55);
920 let participants_len = schemes.len();
921
922 let attestations: Vec<_> = schemes
923 .iter()
924 .take(3)
925 .map(|s| {
926 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
927 .unwrap()
928 })
929 .collect();
930
931 let mut certificate = schemes[0].assemble(attestations).unwrap();
932
933 let signers: Vec<u32> = certificate.signers.iter().collect();
935 certificate.signers = Signers::from(participants_len + 1, signers);
936
937 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
938 &mut thread_rng(),
939 NAMESPACE,
940 TestSubject { message: MESSAGE },
941 &certificate
942 ));
943 }
944
945 #[test]
946 fn test_verify_certificate_rejects_signers_size_mismatch_variants() {
947 test_verify_certificate_rejects_signers_size_mismatch::<MinPk>();
948 test_verify_certificate_rejects_signers_size_mismatch::<MinSig>();
949 }
950
951 fn test_verify_certificates_batch<V: Variant + Send + Sync>() {
952 let (schemes, verifier) = setup_signers::<V>(4, 56);
953 let quorum = quorum(schemes.len() as u32) as usize;
954
955 let messages = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()];
956 let mut certificates = Vec::new();
957
958 for msg in &messages {
959 let attestations: Vec<_> = schemes
960 .iter()
961 .take(quorum)
962 .map(|s| {
963 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: msg })
964 .unwrap()
965 })
966 .collect();
967 certificates.push(schemes[0].assemble(attestations).unwrap());
968 }
969
970 let certs_iter = messages
971 .iter()
972 .zip(&certificates)
973 .map(|(msg, cert)| (TestSubject { message: msg }, cert));
974
975 let mut rng = StdRng::seed_from_u64(57);
976 assert!(verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter));
977 }
978
979 #[test]
980 fn test_verify_certificates_batch_variants() {
981 test_verify_certificates_batch::<MinPk>();
982 test_verify_certificates_batch::<MinSig>();
983 }
984
985 fn test_verify_certificates_batch_detects_failure<V: Variant + Send + Sync>() {
986 let (schemes, verifier) = setup_signers::<V>(4, 58);
987 let quorum = quorum(schemes.len() as u32) as usize;
988
989 let messages = [b"msg1".as_slice(), b"msg2".as_slice()];
990 let mut certificates = Vec::new();
991
992 for msg in &messages {
993 let attestations: Vec<_> = schemes
994 .iter()
995 .take(quorum)
996 .map(|s| {
997 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: msg })
998 .unwrap()
999 })
1000 .collect();
1001 certificates.push(schemes[0].assemble(attestations).unwrap());
1002 }
1003
1004 certificates[1].signature = V::Signature::zero();
1006
1007 let certs_iter = messages
1008 .iter()
1009 .zip(&certificates)
1010 .map(|(msg, cert)| (TestSubject { message: msg }, cert));
1011
1012 let mut rng = StdRng::seed_from_u64(59);
1013 assert!(
1014 !verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter)
1015 );
1016 }
1017
1018 #[test]
1019 fn test_verify_certificates_batch_detects_failure_variants() {
1020 test_verify_certificates_batch_detects_failure::<MinPk>();
1021 test_verify_certificates_batch_detects_failure::<MinSig>();
1022 }
1023
1024 fn test_scheme_clone_and_verifier<V: Variant + Send + Sync>() {
1025 let (schemes, verifier) = setup_signers::<V>(4, 60);
1026
1027 let signer = schemes[0].clone();
1029 assert!(
1030 signer
1031 .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
1032 .is_some(),
1033 "cloned signer should retain signing capability"
1034 );
1035
1036 assert!(
1038 verifier
1039 .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
1040 .is_none(),
1041 "verifier must not sign votes"
1042 );
1043 }
1044
1045 #[test]
1046 fn test_scheme_clone_and_verifier_variants() {
1047 test_scheme_clone_and_verifier::<MinPk>();
1048 test_scheme_clone_and_verifier::<MinSig>();
1049 }
1050
1051 fn test_certificate_decode_validation<V: Variant + Send + Sync>() {
1052 let (schemes, _) = setup_signers::<V>(4, 61);
1053 let participants_len = schemes.len();
1054
1055 let attestations: Vec<_> = schemes
1056 .iter()
1057 .take(3)
1058 .map(|s| {
1059 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
1060 .unwrap()
1061 })
1062 .collect();
1063
1064 let certificate = schemes[0].assemble(attestations).unwrap();
1065
1066 let encoded = certificate.encode();
1068 let decoded =
1069 Certificate::<V>::decode_cfg(encoded, &participants_len).expect("decode certificate");
1070 assert_eq!(decoded, certificate);
1071
1072 let empty = Certificate::<V> {
1074 signers: Signers::from(participants_len, std::iter::empty::<u32>()),
1075 signature: certificate.signature,
1076 };
1077 assert!(Certificate::<V>::decode_cfg(empty.encode(), &participants_len).is_err());
1078
1079 let mut signers = certificate.signers.iter().collect::<Vec<_>>();
1081 signers.push(participants_len as u32);
1082 let extended = Certificate::<V> {
1083 signers: Signers::from(participants_len + 1, signers),
1084 signature: certificate.signature,
1085 };
1086 assert!(Certificate::<V>::decode_cfg(extended.encode(), &participants_len).is_err());
1087 }
1088
1089 #[test]
1090 fn test_certificate_decode_validation_variants() {
1091 test_certificate_decode_validation::<MinPk>();
1092 test_certificate_decode_validation::<MinSig>();
1093 }
1094
1095 fn test_verify_certificate_rejects_unknown_signer<V: Variant + Send + Sync>() {
1096 let (schemes, verifier) = setup_signers::<V>(4, 62);
1097 let participants_len = schemes.len();
1098
1099 let attestations: Vec<_> = schemes
1100 .iter()
1101 .take(3)
1102 .map(|s| {
1103 s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
1104 .unwrap()
1105 })
1106 .collect();
1107
1108 let mut certificate = schemes[0].assemble(attestations).unwrap();
1109
1110 let mut signers: Vec<u32> = certificate.signers.iter().collect();
1112 signers.push(participants_len as u32);
1113 certificate.signers = Signers::from(participants_len + 1, signers);
1114
1115 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1116 &mut thread_rng(),
1117 NAMESPACE,
1118 TestSubject { message: MESSAGE },
1119 &certificate,
1120 ));
1121 }
1122
1123 #[test]
1124 fn test_verify_certificate_rejects_unknown_signer_variants() {
1125 test_verify_certificate_rejects_unknown_signer::<MinPk>();
1126 test_verify_certificate_rejects_unknown_signer::<MinSig>();
1127 }
1128
1129 #[cfg(feature = "arbitrary")]
1130 mod conformance {
1131 use super::*;
1132 use crate::bls12381::primitives::variant::MinSig;
1133 use commonware_codec::conformance::CodecConformance;
1134
1135 commonware_conformance::conformance_tests! {
1136 CodecConformance<Certificate<MinSig>>,
1137 }
1138 }
1139}