1#[cfg(feature = "mocks")]
12pub mod mocks;
13
14use crate::{
15 bls12381::primitives::{
16 group::Share,
17 ops::{self, batch, threshold},
18 sharing::Sharing,
19 variant::{PartialSignature, Variant},
20 },
21 certificate::{Attestation, Namespace, Scheme, Subject, Verification},
22 Digest, PublicKey,
23};
24#[cfg(not(feature = "std"))]
25use alloc::{collections::BTreeSet, vec::Vec};
26use bytes::{Buf, BufMut};
27use commonware_codec::{types::lazy::Lazy, Error, FixedSize, Read, ReadExt, Write};
28use commonware_parallel::Strategy;
29use commonware_utils::{ordered::Set, Faults, Participant};
30use core::fmt::Debug;
31use rand_core::CryptoRng;
32#[cfg(feature = "std")]
33use std::collections::BTreeSet;
34
35#[derive(Clone, Debug)]
45pub enum Generic<P: PublicKey, V: Variant, N: Namespace> {
46 Signer {
47 participants: Set<P>,
49 polynomial: Sharing<V>,
51 share: Share,
53 namespace: N,
55 },
56 Verifier {
57 participants: Set<P>,
59 polynomial: Sharing<V>,
61 namespace: N,
63 },
64 CertificateVerifier {
65 identity: V::Public,
67 namespace: N,
69 },
70}
71
72impl<P: PublicKey, V: Variant, N: Namespace> Generic<P, V, N> {
73 pub fn signer(
86 namespace: &[u8],
87 participants: Set<P>,
88 polynomial: Sharing<V>,
89 share: Share,
90 ) -> Option<Self> {
91 assert_eq!(
92 polynomial.total().get() as usize,
93 participants.len(),
94 "polynomial total must equal participant len"
95 );
96 #[cfg(feature = "std")]
97 polynomial.precompute_partial_publics();
98 let partial_public = polynomial
99 .partial_public(share.index)
100 .expect("share index must match participant indices");
101 if partial_public == share.public::<V>() {
102 Some(Self::Signer {
103 participants,
104 polynomial,
105 share,
106 namespace: N::derive(namespace),
107 })
108 } else {
109 None
110 }
111 }
112
113 pub fn verifier(namespace: &[u8], participants: Set<P>, polynomial: Sharing<V>) -> Self {
123 assert_eq!(
124 polynomial.total().get() as usize,
125 participants.len(),
126 "polynomial total must equal participant len"
127 );
128 #[cfg(feature = "std")]
129 polynomial.precompute_partial_publics();
130
131 Self::Verifier {
132 participants,
133 polynomial,
134 namespace: N::derive(namespace),
135 }
136 }
137
138 pub fn certificate_verifier(namespace: &[u8], identity: V::Public) -> Self {
146 Self::CertificateVerifier {
147 identity,
148 namespace: N::derive(namespace),
149 }
150 }
151
152 pub fn participants(&self) -> &Set<P> {
154 match self {
155 Self::Signer { participants, .. } => participants,
156 Self::Verifier { participants, .. } => participants,
157 _ => panic!("can only be called for signer and verifier"),
158 }
159 }
160
161 pub fn identity(&self) -> &V::Public {
163 match self {
164 Self::Signer { polynomial, .. } => polynomial.public(),
165 Self::Verifier { polynomial, .. } => polynomial.public(),
166 Self::CertificateVerifier { identity, .. } => identity,
167 }
168 }
169
170 pub const fn share(&self) -> Option<&Share> {
172 match self {
173 Self::Signer { share, .. } => Some(share),
174 _ => None,
175 }
176 }
177
178 fn polynomial(&self) -> &Sharing<V> {
180 match self {
181 Self::Signer { polynomial, .. } => polynomial,
182 Self::Verifier { polynomial, .. } => polynomial,
183 _ => panic!("can only be called for signer and verifier"),
184 }
185 }
186
187 const fn namespace(&self) -> &N {
189 match self {
190 Self::Signer { namespace, .. } => namespace,
191 Self::Verifier { namespace, .. } => namespace,
192 Self::CertificateVerifier { namespace, .. } => namespace,
193 }
194 }
195
196 pub const fn me(&self) -> Option<Participant> {
198 match self {
199 Self::Signer { share, .. } => Some(share.index),
200 _ => None,
201 }
202 }
203
204 pub fn sign<'a, S, D>(&self, subject: S::Subject<'a, D>) -> Option<Attestation<S>>
206 where
207 S: Scheme<Signature = V::Signature>,
208 S::Subject<'a, D>: Subject<Namespace = N>,
209 D: Digest,
210 {
211 let share = self.share()?;
212
213 let signature = threshold::sign_message::<V>(
214 share,
215 subject.namespace(self.namespace()),
216 &subject.message(),
217 )
218 .value;
219
220 Some(Attestation {
221 signer: share.index,
222 signature: signature.into(),
223 })
224 }
225
226 pub fn verify_attestation<'a, S, D>(
228 &self,
229 subject: S::Subject<'a, D>,
230 attestation: &Attestation<S>,
231 ) -> bool
232 where
233 S: Scheme<Signature = V::Signature>,
234 S::Subject<'a, D>: Subject<Namespace = N>,
235 D: Digest,
236 {
237 let Ok(evaluated) = self.polynomial().partial_public(attestation.signer) else {
238 return false;
239 };
240 let Some(signature) = attestation.signature.get() else {
241 return false;
242 };
243
244 ops::verify_message::<V>(
245 &evaluated,
246 subject.namespace(self.namespace()),
247 &subject.message(),
248 signature,
249 )
250 .is_ok()
251 }
252
253 pub fn verify_attestations<'a, S, R, D, I, T>(
255 &self,
256 rng: &mut R,
257 subject: S::Subject<'a, D>,
258 attestations: I,
259 strategy: &T,
260 ) -> Verification<S>
261 where
262 S: Scheme<Signature = V::Signature>,
263 S::Subject<'a, D>: Subject<Namespace = N>,
264 R: CryptoRng,
265 D: Digest,
266 I: IntoIterator<Item = Attestation<S>>,
267 I::IntoIter: Send,
268 T: Strategy,
269 {
270 let (partials, failures) =
271 strategy.map_partition_collect_vec(attestations.into_iter(), |attestation| {
272 let index = attestation.signer;
273 let partial = attestation
274 .signature
275 .get()
276 .map(|&value| PartialSignature::<V> { index, value });
277 (index, partial)
278 });
279 let mut invalid: BTreeSet<_> = failures.into_iter().collect();
280 let polynomial = self.polynomial();
281 if let Err(errs) = threshold::batch_verify_same_message::<_, V, _>(
282 rng,
283 polynomial,
284 subject.namespace(self.namespace()),
285 &subject.message(),
286 partials.iter(),
287 strategy,
288 ) {
289 for partial in errs {
290 invalid.insert(partial.index);
291 }
292 }
293
294 let verified = partials
295 .into_iter()
296 .filter(|partial| !invalid.contains(&partial.index))
297 .map(|partial| Attestation {
298 signer: partial.index,
299 signature: partial.value.into(),
300 })
301 .collect();
302
303 Verification::new(verified, invalid.into_iter().collect())
304 }
305
306 pub fn assemble<S, I, T, M>(&self, attestations: I, strategy: &T) -> Option<Certificate<V>>
308 where
309 S: Scheme<Signature = V::Signature>,
310 I: IntoIterator<Item = Attestation<S>>,
311 I::IntoIter: Send,
312 T: Strategy,
313 M: Faults,
314 {
315 let (partials, failures) =
316 strategy.map_partition_collect_vec(attestations.into_iter(), |attestation| {
317 let index = attestation.signer;
318 let value = attestation
319 .signature
320 .get()
321 .map(|&sig| PartialSignature::<V> { index, value: sig });
322 (index, value)
323 });
324 if !failures.is_empty() {
325 return None;
326 }
327
328 let quorum = self.polynomial();
329 if partials.len() < quorum.required::<M>() as usize {
330 return None;
331 }
332
333 threshold::recover::<V, _, M>(quorum, partials.iter(), strategy)
334 .ok()
335 .map(Certificate::new)
336 }
337
338 pub fn verify_certificate<'a, S, R, D, M>(
340 &self,
341 _rng: &mut R,
342 subject: S::Subject<'a, D>,
343 certificate: &Certificate<V>,
344 ) -> bool
345 where
346 S: Scheme,
347 S::Subject<'a, D>: Subject<Namespace = N>,
348 R: CryptoRng,
349 D: Digest,
350 M: Faults,
351 {
352 let Some(signature) = certificate.get() else {
353 return false;
354 };
355 ops::verify_message::<V>(
356 self.identity(),
357 subject.namespace(self.namespace()),
358 &subject.message(),
359 signature,
360 )
361 .is_ok()
362 }
363
364 pub fn verify_certificates<'a, S, R, D, I, T, M>(
366 &self,
367 rng: &mut R,
368 certificates: I,
369 strategy: &T,
370 ) -> bool
371 where
372 S: Scheme,
373 S::Subject<'a, D>: Subject<Namespace = N>,
374 R: CryptoRng,
375 D: Digest,
376 I: Iterator<Item = (S::Subject<'a, D>, &'a Certificate<V>)>,
377 T: Strategy,
378 M: Faults,
379 {
380 let mut entries: Vec<_> = Vec::new();
381
382 for (subject, certificate) in certificates {
383 let Some(signature) = certificate.get() else {
384 return false;
385 };
386 let namespace = subject.namespace(self.namespace());
387 let message = subject.message();
388 entries.push((namespace.to_vec(), message.to_vec(), *signature));
389 }
390
391 if entries.is_empty() {
392 return true;
393 }
394
395 let entries_refs: Vec<_> = entries
396 .iter()
397 .map(|(ns, msg, sig)| (ns.as_ref(), msg.as_ref(), *sig))
398 .collect();
399
400 batch::verify_same_signer::<_, V, _>(rng, self.identity(), &entries_refs, strategy).is_ok()
401 }
402
403 pub const fn is_attributable() -> bool {
404 false
405 }
406
407 pub const fn is_batchable() -> bool {
408 true
409 }
410
411 pub const fn certificate_codec_config(&self) {}
412
413 pub const fn certificate_codec_config_unbounded() {}
414}
415
416#[derive(Clone, Debug, PartialEq, Eq, Hash)]
418pub struct Certificate<V: Variant> {
419 pub signature: Lazy<V::Signature>,
421}
422
423impl<V: Variant> Certificate<V> {
424 pub fn new(signature: V::Signature) -> Self {
426 Self {
427 signature: Lazy::from(signature),
428 }
429 }
430
431 #[allow(clippy::missing_const_for_fn)]
437 pub fn get(&self) -> Option<&V::Signature> {
438 self.signature.get()
439 }
440}
441
442impl<V: Variant> Write for Certificate<V> {
443 fn write(&self, writer: &mut impl BufMut) {
444 self.signature.write(writer);
445 }
446}
447
448impl<V: Variant> Read for Certificate<V> {
449 type Cfg = ();
450
451 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
452 let signature = Lazy::<V::Signature>::read(reader)?;
453 Ok(Self { signature })
454 }
455}
456
457impl<V: Variant> FixedSize for Certificate<V> {
458 const SIZE: usize = V::Signature::SIZE;
459}
460
461#[cfg(feature = "arbitrary")]
462impl<V: Variant> arbitrary::Arbitrary<'_> for Certificate<V>
463where
464 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
465{
466 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
467 Ok(Self {
468 signature: Lazy::from(u.arbitrary::<V::Signature>()?),
469 })
470 }
471}
472
473#[macro_export]
499macro_rules! impl_certificate_bls12381_threshold {
500 ($subject:ty, $namespace:ty) => {
501 #[cfg(feature = "mocks")]
506 #[allow(dead_code)]
507 pub fn fixture<V, R>(
508 rng: &mut R,
509 namespace: &[u8],
510 n: u32,
511 ) -> $crate::certificate::mocks::Fixture<Scheme<$crate::ed25519::PublicKey, V>>
512 where
513 V: $crate::bls12381::primitives::variant::Variant,
514 R: rand_core::CryptoRng,
515 {
516 $crate::bls12381::certificate::threshold::mocks::fixture::<_, V, _>(
517 rng,
518 namespace,
519 n,
520 Scheme::signer,
521 Scheme::verifier,
522 )
523 }
524
525 #[derive(Clone, Debug)]
527 pub struct Scheme<
528 P: $crate::PublicKey,
529 V: $crate::bls12381::primitives::variant::Variant,
530 > {
531 generic: $crate::bls12381::certificate::threshold::Generic<P, V, $namespace>,
532 }
533
534 impl<
535 P: $crate::PublicKey,
536 V: $crate::bls12381::primitives::variant::Variant,
537 > Scheme<P, V> {
538 pub fn signer(
540 namespace: &[u8],
541 participants: commonware_utils::ordered::Set<P>,
542 polynomial: $crate::bls12381::primitives::sharing::Sharing<V>,
543 share: $crate::bls12381::primitives::group::Share,
544 ) -> Option<Self> {
545 Some(Self {
546 generic: $crate::bls12381::certificate::threshold::Generic::signer(
547 namespace,
548 participants,
549 polynomial,
550 share,
551 )?,
552 })
553 }
554
555 pub fn verifier(
557 namespace: &[u8],
558 participants: commonware_utils::ordered::Set<P>,
559 polynomial: $crate::bls12381::primitives::sharing::Sharing<V>,
560 ) -> Self {
561 Self {
562 generic: $crate::bls12381::certificate::threshold::Generic::verifier(
563 namespace,
564 participants,
565 polynomial,
566 ),
567 }
568 }
569
570 pub fn certificate_verifier(namespace: &[u8], identity: V::Public) -> Self {
572 Self {
573 generic: $crate::bls12381::certificate::threshold::Generic::certificate_verifier(
574 namespace,
575 identity,
576 ),
577 }
578 }
579
580 pub fn identity(&self) -> &V::Public {
582 self.generic.identity()
583 }
584
585 pub const fn share(&self) -> Option<&$crate::bls12381::primitives::group::Share> {
587 self.generic.share()
588 }
589 }
590
591 impl<
592 P: $crate::PublicKey,
593 V: $crate::bls12381::primitives::variant::Variant,
594 > $crate::certificate::Verifier for Scheme<P, V> {
595 type Subject<'a, D: $crate::Digest> = $subject;
596 type PublicKey = P;
597 type Certificate = $crate::bls12381::certificate::threshold::Certificate<V>;
598
599 fn verify_certificate<R, D, M>(
600 &self,
601 rng: &mut R,
602 subject: Self::Subject<'_, D>,
603 certificate: &Self::Certificate,
604 _strategy: &impl commonware_parallel::Strategy,
605 ) -> bool
606 where
607 R: rand_core::CryptoRng,
608 D: $crate::Digest,
609 M: commonware_utils::Faults,
610 {
611 self.generic
612 .verify_certificate::<Self, _, D, M>(rng, subject, certificate)
613 }
614
615 fn verify_certificates<'a, R, D, I, M>(
616 &self,
617 rng: &mut R,
618 certificates: I,
619 strategy: &impl commonware_parallel::Strategy,
620 ) -> bool
621 where
622 R: rand_core::CryptoRng,
623 D: $crate::Digest,
624 I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
625 M: commonware_utils::Faults,
626 {
627 self.generic
628 .verify_certificates::<Self, _, D, _, _, M>(rng, certificates, strategy)
629 }
630
631 fn is_batchable() -> bool {
632 $crate::bls12381::certificate::threshold::Generic::<P, V, $namespace>::is_batchable()
633 }
634
635 fn certificate_codec_config(
636 &self,
637 ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
638 self.generic.certificate_codec_config()
639 }
640
641 fn certificate_codec_config_unbounded(
642 ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
643 $crate::bls12381::certificate::threshold::Generic::<P, V, $namespace>::certificate_codec_config_unbounded()
644 }
645 }
646
647 impl<
648 P: $crate::PublicKey,
649 V: $crate::bls12381::primitives::variant::Variant,
650 > $crate::certificate::Scheme for Scheme<P, V> {
651 type Signature = V::Signature;
652
653 fn me(&self) -> Option<commonware_utils::Participant> {
654 self.generic.me()
655 }
656
657 fn participants(&self) -> &commonware_utils::ordered::Set<Self::PublicKey> {
658 self.generic.participants()
659 }
660
661 fn sign<D: $crate::Digest>(
662 &self,
663 subject: Self::Subject<'_, D>,
664 ) -> Option<$crate::certificate::Attestation<Self>> {
665 self.generic.sign::<_, D>(subject)
666 }
667
668 fn verify_attestation<R, D>(
669 &self,
670 _rng: &mut R,
671 subject: Self::Subject<'_, D>,
672 attestation: &$crate::certificate::Attestation<Self>,
673 _strategy: &impl commonware_parallel::Strategy,
674 ) -> bool
675 where
676 R: rand_core::CryptoRng,
677 D: $crate::Digest,
678 {
679 self.generic
680 .verify_attestation::<_, D>(subject, attestation)
681 }
682
683 fn verify_attestations<R, D, I>(
684 &self,
685 rng: &mut R,
686 subject: Self::Subject<'_, D>,
687 attestations: I,
688 strategy: &impl commonware_parallel::Strategy,
689 ) -> $crate::certificate::Verification<Self>
690 where
691 R: rand_core::CryptoRng,
692 D: $crate::Digest,
693 I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
694 I::IntoIter: Send
695 {
696 self.generic
697 .verify_attestations::<_, _, D, _, _>(rng, subject, attestations, strategy)
698 }
699
700 fn assemble<I, M>(
701 &self,
702 attestations: I,
703 strategy: &impl commonware_parallel::Strategy,
704 ) -> Option<Self::Certificate>
705 where
706 I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
707 I::IntoIter: Send,
708 M: commonware_utils::Faults,
709 {
710 self.generic.assemble::<Self, _, _, M>(attestations, strategy)
711 }
712
713 fn is_attributable() -> bool {
714 $crate::bls12381::certificate::threshold::Generic::<P, V, $namespace>::is_attributable()
715 }
716 }
717 };
718}
719
720#[cfg(test)]
721mod tests {
722 use super::*;
723 use crate::{
724 bls12381::{
725 dkg::feldman_desmedt as dkg,
726 primitives::{
727 ops::threshold::sign_message,
728 variant::{MinPk, MinSig, Variant},
729 },
730 },
731 certificate::{Scheme as _, Verifier as _},
732 ed25519::{self, PrivateKey as Ed25519PrivateKey},
733 sha256::Digest as Sha256Digest,
734 Signer as _,
735 };
736 use bytes::Bytes;
737 use commonware_codec::{DecodeExt, Encode};
738 use commonware_math::algebra::{Additive, Random};
739 use commonware_parallel::Sequential;
740 use commonware_utils::{ordered::Set, test_rng, Faults, N3f1, TryCollect, NZU32};
741
742 const NAMESPACE: &[u8] = b"test-bls12381-threshold";
743 const MESSAGE: &[u8] = b"test message";
744
745 #[derive(Clone, Debug)]
747 pub struct TestSubject {
748 pub message: Bytes,
749 }
750
751 impl Subject for TestSubject {
752 type Namespace = Vec<u8>;
753
754 fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] {
755 derived
756 }
757
758 fn message(&self) -> Bytes {
759 self.message.clone()
760 }
761 }
762
763 impl_certificate_bls12381_threshold!(TestSubject, Vec<u8>);
765
766 #[allow(clippy::type_complexity)]
767 fn setup_signers<V: Variant>(
768 rng: &mut impl CryptoRng,
769 n: u32,
770 ) -> (
771 Vec<Scheme<ed25519::PublicKey, V>>,
772 Scheme<ed25519::PublicKey, V>,
773 Sharing<V>,
774 ) {
775 let identity_keys: Vec<_> = (0..n)
777 .map(|_| Ed25519PrivateKey::random(&mut *rng))
778 .collect();
779 let participants: Set<ed25519::PublicKey> = identity_keys
780 .iter()
781 .map(|sk| sk.public_key())
782 .try_collect()
783 .unwrap();
784
785 let (polynomial, shares) =
787 dkg::deal_anonymous::<V, N3f1>(&mut *rng, Default::default(), NZU32!(n));
788
789 let signers = shares
790 .into_iter()
791 .map(|share| {
792 Scheme::signer(NAMESPACE, participants.clone(), polynomial.clone(), share).unwrap()
793 })
794 .collect();
795
796 let verifier = Scheme::verifier(NAMESPACE, participants, polynomial.clone());
797
798 (signers, verifier, polynomial)
799 }
800
801 fn test_sign_vote_roundtrip<V: Variant>() {
802 let mut rng = test_rng();
803 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
804 let scheme = &schemes[0];
805
806 let attestation = scheme
807 .sign::<Sha256Digest>(TestSubject {
808 message: Bytes::from_static(MESSAGE),
809 })
810 .unwrap();
811 assert!(scheme.verify_attestation::<_, Sha256Digest>(
812 &mut rng,
813 TestSubject {
814 message: Bytes::from_static(MESSAGE),
815 },
816 &attestation,
817 &Sequential,
818 ));
819 }
820
821 #[test]
822 fn test_sign_vote_roundtrip_variants() {
823 test_sign_vote_roundtrip::<MinPk>();
824 test_sign_vote_roundtrip::<MinSig>();
825 }
826
827 fn test_verifier_cannot_sign<V: Variant>() {
828 let mut rng = test_rng();
829 let (_, verifier, _) = setup_signers::<V>(&mut rng, 4);
830 assert!(verifier
831 .sign::<Sha256Digest>(TestSubject {
832 message: Bytes::from_static(MESSAGE),
833 })
834 .is_none());
835 }
836
837 #[test]
838 fn test_verifier_cannot_sign_variants() {
839 test_verifier_cannot_sign::<MinPk>();
840 test_verifier_cannot_sign::<MinSig>();
841 }
842
843 fn test_verify_attestations_filters_invalid<V: Variant>() {
844 let mut rng = test_rng();
845 let (schemes, _, _) = setup_signers::<V>(&mut rng, 5);
846 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
847
848 let attestations: Vec<_> = schemes
849 .iter()
850 .take(quorum)
851 .map(|s| {
852 s.sign::<Sha256Digest>(TestSubject {
853 message: Bytes::from_static(MESSAGE),
854 })
855 .unwrap()
856 })
857 .collect();
858
859 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
860 &mut rng,
861 TestSubject {
862 message: Bytes::from_static(MESSAGE),
863 },
864 attestations.clone(),
865 &Sequential,
866 );
867 assert!(result.invalid.is_empty());
868 assert_eq!(result.verified.len(), quorum);
869
870 let mut attestations_corrupted = attestations.clone();
872 attestations_corrupted[0].signer = Participant::new(999);
873 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
874 &mut rng,
875 TestSubject {
876 message: Bytes::from_static(MESSAGE),
877 },
878 attestations_corrupted,
879 &Sequential,
880 );
881 assert_eq!(result.invalid, vec![Participant::new(999)]);
882 assert_eq!(result.verified.len(), quorum - 1);
883
884 let mut attestations_corrupted = attestations;
886 attestations_corrupted[0].signature = attestations_corrupted[1].signature.clone();
887 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
888 &mut rng,
889 TestSubject {
890 message: Bytes::from_static(MESSAGE),
891 },
892 attestations_corrupted,
893 &Sequential,
894 );
895 assert_eq!(result.invalid.len(), 1);
896 assert_eq!(result.verified.len(), quorum - 1);
897 }
898
899 #[test]
900 fn test_verify_attestations_filters_invalid_variants() {
901 test_verify_attestations_filters_invalid::<MinPk>();
902 test_verify_attestations_filters_invalid::<MinSig>();
903 }
904
905 fn test_assemble_certificate<V: Variant>() {
906 let mut rng = test_rng();
907 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
908 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
909
910 let attestations: Vec<_> = schemes
911 .iter()
912 .take(quorum)
913 .map(|s| {
914 s.sign::<Sha256Digest>(TestSubject {
915 message: Bytes::from_static(MESSAGE),
916 })
917 .unwrap()
918 })
919 .collect();
920
921 let certificate = schemes[0]
922 .assemble::<_, N3f1>(attestations, &Sequential)
923 .unwrap();
924
925 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
927 &mut rng,
928 TestSubject {
929 message: Bytes::from_static(MESSAGE),
930 },
931 &certificate,
932 &Sequential,
933 ));
934 }
935
936 #[test]
937 fn test_assemble_certificate_variants() {
938 test_assemble_certificate::<MinPk>();
939 test_assemble_certificate::<MinSig>();
940 }
941
942 fn test_verify_certificate<V: Variant>() {
943 let mut rng = test_rng();
944 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
945 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
946
947 let attestations: Vec<_> = schemes
948 .iter()
949 .take(quorum)
950 .map(|s| {
951 s.sign::<Sha256Digest>(TestSubject {
952 message: Bytes::from_static(MESSAGE),
953 })
954 .unwrap()
955 })
956 .collect();
957
958 let certificate = schemes[0]
959 .assemble::<_, N3f1>(attestations, &Sequential)
960 .unwrap();
961
962 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
963 &mut rng,
964 TestSubject {
965 message: Bytes::from_static(MESSAGE),
966 },
967 &certificate,
968 &Sequential,
969 ));
970 }
971
972 #[test]
973 fn test_verify_certificate_variants() {
974 test_verify_certificate::<MinPk>();
975 test_verify_certificate::<MinSig>();
976 }
977
978 fn test_verify_certificate_detects_corruption<V: Variant>() {
979 let mut rng = test_rng();
980 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
981 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
982
983 let attestations: Vec<_> = schemes
984 .iter()
985 .take(quorum)
986 .map(|s| {
987 s.sign::<Sha256Digest>(TestSubject {
988 message: Bytes::from_static(MESSAGE),
989 })
990 .unwrap()
991 })
992 .collect();
993
994 let certificate = schemes[0]
995 .assemble::<_, N3f1>(attestations, &Sequential)
996 .unwrap();
997
998 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1000 &mut rng,
1001 TestSubject {
1002 message: Bytes::from_static(MESSAGE),
1003 },
1004 &certificate,
1005 &Sequential,
1006 ));
1007
1008 let corrupted = Certificate::new(V::Signature::zero());
1010 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1011 &mut rng,
1012 TestSubject {
1013 message: Bytes::from_static(MESSAGE),
1014 },
1015 &corrupted,
1016 &Sequential,
1017 ));
1018 }
1019
1020 #[test]
1021 fn test_verify_certificate_detects_corruption_variants() {
1022 test_verify_certificate_detects_corruption::<MinPk>();
1023 test_verify_certificate_detects_corruption::<MinSig>();
1024 }
1025
1026 fn test_certificate_codec_roundtrip<V: Variant>() {
1027 let mut rng = test_rng();
1028 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
1029 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
1030
1031 let attestations: Vec<_> = schemes
1032 .iter()
1033 .take(quorum)
1034 .map(|s| {
1035 s.sign::<Sha256Digest>(TestSubject {
1036 message: Bytes::from_static(MESSAGE),
1037 })
1038 .unwrap()
1039 })
1040 .collect();
1041
1042 let certificate = schemes[0]
1043 .assemble::<_, N3f1>(attestations, &Sequential)
1044 .unwrap();
1045 let encoded = certificate.encode();
1046 let decoded = Certificate::<V>::decode(encoded).expect("decode certificate");
1047 assert_eq!(decoded, certificate);
1048 }
1049
1050 #[test]
1051 fn test_certificate_codec_roundtrip_variants() {
1052 test_certificate_codec_roundtrip::<MinPk>();
1053 test_certificate_codec_roundtrip::<MinSig>();
1054 }
1055
1056 fn test_certificate_rejects_sub_quorum<V: Variant>() {
1057 let mut rng = test_rng();
1058 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
1059 let sub_quorum = 2; let attestations: Vec<_> = schemes
1062 .iter()
1063 .take(sub_quorum)
1064 .map(|s| {
1065 s.sign::<Sha256Digest>(TestSubject {
1066 message: Bytes::from_static(MESSAGE),
1067 })
1068 .unwrap()
1069 })
1070 .collect();
1071
1072 assert!(schemes[0]
1073 .assemble::<_, N3f1>(attestations, &Sequential)
1074 .is_none());
1075 }
1076
1077 #[test]
1078 fn test_certificate_rejects_sub_quorum_variants() {
1079 test_certificate_rejects_sub_quorum::<MinPk>();
1080 test_certificate_rejects_sub_quorum::<MinSig>();
1081 }
1082
1083 fn test_verify_certificates_batch<V: Variant>() {
1084 let mut rng = test_rng();
1085 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
1086 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
1087
1088 let messages: [Bytes; 3] = [
1089 Bytes::from_static(b"msg1"),
1090 Bytes::from_static(b"msg2"),
1091 Bytes::from_static(b"msg3"),
1092 ];
1093 let mut certificates = Vec::new();
1094
1095 for msg in &messages {
1096 let attestations: Vec<_> = schemes
1097 .iter()
1098 .take(quorum)
1099 .map(|s| {
1100 s.sign::<Sha256Digest>(TestSubject {
1101 message: msg.clone(),
1102 })
1103 .unwrap()
1104 })
1105 .collect();
1106 certificates.push(
1107 schemes[0]
1108 .assemble::<_, N3f1>(attestations, &Sequential)
1109 .unwrap(),
1110 );
1111 }
1112
1113 let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
1114 (
1115 TestSubject {
1116 message: msg.clone(),
1117 },
1118 cert,
1119 )
1120 });
1121
1122 assert!(verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1123 &mut rng,
1124 certs_iter,
1125 &Sequential
1126 ));
1127 }
1128
1129 #[test]
1130 fn test_verify_certificates_batch_variants() {
1131 test_verify_certificates_batch::<MinPk>();
1132 test_verify_certificates_batch::<MinSig>();
1133 }
1134
1135 fn test_verify_certificates_batch_detects_failure<V: Variant>() {
1136 let mut rng = test_rng();
1137 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
1138 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
1139
1140 let messages: [Bytes; 2] = [Bytes::from_static(b"msg1"), Bytes::from_static(b"msg2")];
1141 let mut certificates = Vec::new();
1142
1143 for msg in &messages {
1144 let attestations: Vec<_> = schemes
1145 .iter()
1146 .take(quorum)
1147 .map(|s| {
1148 s.sign::<Sha256Digest>(TestSubject {
1149 message: msg.clone(),
1150 })
1151 .unwrap()
1152 })
1153 .collect();
1154 certificates.push(
1155 schemes[0]
1156 .assemble::<_, N3f1>(attestations, &Sequential)
1157 .unwrap(),
1158 );
1159 }
1160
1161 certificates[1] = Certificate::new(V::Signature::zero());
1163
1164 let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
1165 (
1166 TestSubject {
1167 message: msg.clone(),
1168 },
1169 cert,
1170 )
1171 });
1172
1173 assert!(!verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1174 &mut rng,
1175 certs_iter,
1176 &Sequential
1177 ));
1178 }
1179
1180 #[test]
1181 fn test_verify_certificates_batch_detects_failure_variants() {
1182 test_verify_certificates_batch_detects_failure::<MinPk>();
1183 test_verify_certificates_batch_detects_failure::<MinSig>();
1184 }
1185
1186 fn test_certificate_verifier<V: Variant>() {
1187 let mut rng = test_rng();
1188 let (schemes, _, polynomial) = setup_signers::<V>(&mut rng, 4);
1189 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
1190
1191 let attestations: Vec<_> = schemes
1192 .iter()
1193 .take(quorum)
1194 .map(|s| {
1195 s.sign::<Sha256Digest>(TestSubject {
1196 message: Bytes::from_static(MESSAGE),
1197 })
1198 .unwrap()
1199 })
1200 .collect();
1201
1202 let certificate = schemes[0]
1203 .assemble::<_, N3f1>(attestations, &Sequential)
1204 .unwrap();
1205
1206 let identity = polynomial.public();
1208 let cert_verifier =
1209 Scheme::<ed25519::PublicKey, V>::certificate_verifier(NAMESPACE, *identity);
1210
1211 assert!(cert_verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1213 &mut rng,
1214 TestSubject {
1215 message: Bytes::from_static(MESSAGE),
1216 },
1217 &certificate,
1218 &Sequential,
1219 ));
1220
1221 assert!(cert_verifier
1223 .sign::<Sha256Digest>(TestSubject {
1224 message: Bytes::from_static(MESSAGE),
1225 })
1226 .is_none());
1227 }
1228
1229 #[test]
1230 fn test_certificate_verifier_variants() {
1231 test_certificate_verifier::<MinPk>();
1232 test_certificate_verifier::<MinSig>();
1233 }
1234
1235 #[test]
1236 fn test_is_not_attributable() {
1237 assert!(!Generic::<ed25519::PublicKey, MinPk, Vec<u8>>::is_attributable());
1238 assert!(!Scheme::<ed25519::PublicKey, MinPk>::is_attributable());
1239 assert!(!Generic::<ed25519::PublicKey, MinSig, Vec<u8>>::is_attributable());
1240 assert!(!Scheme::<ed25519::PublicKey, MinSig>::is_attributable());
1241 }
1242
1243 #[test]
1244 fn test_is_batchable() {
1245 assert!(Generic::<ed25519::PublicKey, MinPk, Vec<u8>>::is_batchable());
1246 assert!(Scheme::<ed25519::PublicKey, MinPk>::is_batchable());
1247 assert!(Generic::<ed25519::PublicKey, MinSig, Vec<u8>>::is_batchable());
1248 assert!(Scheme::<ed25519::PublicKey, MinSig>::is_batchable());
1249 }
1250
1251 fn test_verifier_accepts_votes<V: Variant>() {
1252 let mut rng = test_rng();
1253 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
1254
1255 let vote = schemes[1]
1256 .sign::<Sha256Digest>(TestSubject {
1257 message: Bytes::from_static(MESSAGE),
1258 })
1259 .unwrap();
1260 assert!(verifier.verify_attestation::<_, Sha256Digest>(
1261 &mut rng,
1262 TestSubject {
1263 message: Bytes::from_static(MESSAGE),
1264 },
1265 &vote,
1266 &Sequential,
1267 ));
1268 }
1269
1270 #[test]
1271 fn test_verifier_accepts_votes_variants() {
1272 test_verifier_accepts_votes::<MinPk>();
1273 test_verifier_accepts_votes::<MinSig>();
1274 }
1275
1276 fn test_scheme_clone_and_verifier<V: Variant>() {
1277 let mut rng = test_rng();
1278 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
1279
1280 let signer = schemes[0].clone();
1282 assert!(
1283 signer
1284 .sign::<Sha256Digest>(TestSubject {
1285 message: Bytes::from_static(MESSAGE),
1286 })
1287 .is_some(),
1288 "signer should produce votes"
1289 );
1290
1291 assert!(
1293 verifier
1294 .sign::<Sha256Digest>(TestSubject {
1295 message: Bytes::from_static(MESSAGE),
1296 })
1297 .is_none(),
1298 "verifier should not produce votes"
1299 );
1300 }
1301
1302 #[test]
1303 fn test_scheme_clone_and_verifier_variants() {
1304 test_scheme_clone_and_verifier::<MinPk>();
1305 test_scheme_clone_and_verifier::<MinSig>();
1306 }
1307
1308 fn certificate_verifier_panics_on_vote<V: Variant>() {
1309 let mut rng = test_rng();
1310 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
1311 let certificate_verifier = Scheme::<ed25519::PublicKey, V>::certificate_verifier(
1312 NAMESPACE,
1313 *schemes[0].identity(),
1314 );
1315
1316 let vote = schemes[1]
1317 .sign::<Sha256Digest>(TestSubject {
1318 message: Bytes::from_static(MESSAGE),
1319 })
1320 .unwrap();
1321
1322 certificate_verifier.verify_attestation::<_, Sha256Digest>(
1324 &mut rng,
1325 TestSubject {
1326 message: Bytes::from_static(MESSAGE),
1327 },
1328 &vote,
1329 &Sequential,
1330 );
1331 }
1332
1333 #[test]
1334 #[should_panic(expected = "can only be called for signer and verifier")]
1335 fn test_certificate_verifier_panics_on_vote_min_pk() {
1336 certificate_verifier_panics_on_vote::<MinPk>();
1337 }
1338
1339 #[test]
1340 #[should_panic(expected = "can only be called for signer and verifier")]
1341 fn test_certificate_verifier_panics_on_vote_min_sig() {
1342 certificate_verifier_panics_on_vote::<MinSig>();
1343 }
1344
1345 fn signer_shares_must_match_participant_indices<V: Variant>() {
1346 let mut rng = test_rng();
1347
1348 let identity_keys: Vec<_> = (0..4)
1350 .map(|_| Ed25519PrivateKey::random(&mut rng))
1351 .collect();
1352 let participants: Set<ed25519::PublicKey> = identity_keys
1353 .iter()
1354 .map(|sk| sk.public_key())
1355 .try_collect()
1356 .unwrap();
1357
1358 let (polynomial, mut shares) =
1359 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(4));
1360 shares[0].index = Participant::new(999);
1361 Scheme::<ed25519::PublicKey, V>::signer(
1362 NAMESPACE,
1363 participants,
1364 polynomial,
1365 shares[0].clone(),
1366 );
1367 }
1368
1369 #[test]
1370 #[should_panic(expected = "share index must match participant indices")]
1371 fn test_signer_shares_must_match_participant_indices_min_pk() {
1372 signer_shares_must_match_participant_indices::<MinPk>();
1373 }
1374
1375 #[test]
1376 #[should_panic(expected = "share index must match participant indices")]
1377 fn test_signer_shares_must_match_participant_indices_min_sig() {
1378 signer_shares_must_match_participant_indices::<MinSig>();
1379 }
1380
1381 fn make_participants<R: rand_core::CryptoRng>(rng: &mut R, n: u32) -> Set<ed25519::PublicKey> {
1382 (0..n)
1383 .map(|_| Ed25519PrivateKey::random(&mut *rng).public_key())
1384 .try_collect()
1385 .expect("participants are unique")
1386 }
1387
1388 fn signer_polynomial_threshold_must_equal_quorum<V: Variant>() {
1389 let mut rng = test_rng();
1390 let participants = make_participants(&mut rng, 5);
1391 let (polynomial, shares) =
1395 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(2));
1396 Scheme::<ed25519::PublicKey, V>::signer(
1397 NAMESPACE,
1398 participants,
1399 polynomial,
1400 shares[0].clone(),
1401 );
1402 }
1403
1404 #[test]
1405 #[should_panic(expected = "polynomial total must equal participant len")]
1406 fn test_signer_polynomial_threshold_must_equal_quorum_min_pk() {
1407 signer_polynomial_threshold_must_equal_quorum::<MinPk>();
1408 }
1409
1410 #[test]
1411 #[should_panic(expected = "polynomial total must equal participant len")]
1412 fn test_signer_polynomial_threshold_must_equal_quorum_min_sig() {
1413 signer_polynomial_threshold_must_equal_quorum::<MinSig>();
1414 }
1415
1416 fn verifier_polynomial_threshold_must_equal_quorum<V: Variant>() {
1417 let mut rng = test_rng();
1418 let participants = make_participants(&mut rng, 5);
1419 let (polynomial, _) =
1422 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(2));
1423 Scheme::<ed25519::PublicKey, V>::verifier(NAMESPACE, participants, polynomial);
1424 }
1425
1426 #[test]
1427 #[should_panic(expected = "polynomial total must equal participant len")]
1428 fn test_verifier_polynomial_threshold_must_equal_quorum_min_pk() {
1429 verifier_polynomial_threshold_must_equal_quorum::<MinPk>();
1430 }
1431
1432 #[test]
1433 #[should_panic(expected = "polynomial total must equal participant len")]
1434 fn test_verifier_polynomial_threshold_must_equal_quorum_min_sig() {
1435 verifier_polynomial_threshold_must_equal_quorum::<MinSig>();
1436 }
1437
1438 fn certificate_decode_rejects_length_mismatch<V: Variant>() {
1439 let mut rng = test_rng();
1440 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
1441 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
1442
1443 let attestations: Vec<_> = schemes
1444 .iter()
1445 .take(quorum)
1446 .map(|s| {
1447 s.sign::<Sha256Digest>(TestSubject {
1448 message: Bytes::from_static(MESSAGE),
1449 })
1450 .unwrap()
1451 })
1452 .collect();
1453
1454 let certificate = schemes[0]
1455 .assemble::<_, N3f1>(attestations, &Sequential)
1456 .unwrap();
1457 let mut encoded = certificate.encode();
1458 encoded.truncate(encoded.len() - 1);
1459 assert!(V::Signature::decode(encoded).is_err());
1460 }
1461
1462 #[test]
1463 fn test_certificate_decode_rejects_length_mismatch_variants() {
1464 certificate_decode_rejects_length_mismatch::<MinPk>();
1465 certificate_decode_rejects_length_mismatch::<MinSig>();
1466 }
1467
1468 fn sign_vote_partial_matches_share<V: Variant>() {
1469 let mut rng = test_rng();
1470 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
1471 let scheme = &schemes[0];
1472
1473 let signature = scheme
1474 .sign::<Sha256Digest>(TestSubject {
1475 message: Bytes::from_static(MESSAGE),
1476 })
1477 .unwrap();
1478
1479 let share = scheme.share().expect("expected signer");
1481
1482 let expected = sign_message::<V>(share, NAMESPACE, MESSAGE);
1483
1484 assert_eq!(signature.signer, share.index);
1485 assert_eq!(signature.signature.get().unwrap(), &expected.value);
1486 }
1487
1488 #[test]
1489 fn test_sign_vote_partial_matches_share_variants() {
1490 sign_vote_partial_matches_share::<MinPk>();
1491 sign_vote_partial_matches_share::<MinSig>();
1492 }
1493
1494 #[cfg(feature = "arbitrary")]
1495 mod conformance {
1496 use super::*;
1497 use commonware_codec::conformance::CodecConformance;
1498
1499 commonware_conformance::conformance_tests! {
1500 CodecConformance<Certificate<MinSig>>,
1501 }
1502 }
1503}