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::CryptoRngCore;
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: CryptoRngCore,
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: CryptoRngCore,
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: CryptoRngCore,
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 pub fn get(&self) -> Option<&V::Signature> {
435 self.signature.get()
436 }
437}
438
439impl<V: Variant> Write for Certificate<V> {
440 fn write(&self, writer: &mut impl BufMut) {
441 self.signature.write(writer);
442 }
443}
444
445impl<V: Variant> Read for Certificate<V> {
446 type Cfg = ();
447
448 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
449 let signature = Lazy::<V::Signature>::read(reader)?;
450 Ok(Self { signature })
451 }
452}
453
454impl<V: Variant> FixedSize for Certificate<V> {
455 const SIZE: usize = V::Signature::SIZE;
456}
457
458#[cfg(feature = "arbitrary")]
459impl<V: Variant> arbitrary::Arbitrary<'_> for Certificate<V>
460where
461 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
462{
463 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
464 Ok(Self {
465 signature: Lazy::from(u.arbitrary::<V::Signature>()?),
466 })
467 }
468}
469
470#[macro_export]
496macro_rules! impl_certificate_bls12381_threshold {
497 ($subject:ty, $namespace:ty) => {
498 #[cfg(feature = "mocks")]
503 #[allow(dead_code)]
504 pub fn fixture<V, R>(
505 rng: &mut R,
506 namespace: &[u8],
507 n: u32,
508 ) -> $crate::certificate::mocks::Fixture<Scheme<$crate::ed25519::PublicKey, V>>
509 where
510 V: $crate::bls12381::primitives::variant::Variant,
511 R: rand::RngCore + rand::CryptoRng,
512 {
513 $crate::bls12381::certificate::threshold::mocks::fixture::<_, V, _>(
514 rng,
515 namespace,
516 n,
517 Scheme::signer,
518 Scheme::verifier,
519 )
520 }
521
522 #[derive(Clone, Debug)]
524 pub struct Scheme<
525 P: $crate::PublicKey,
526 V: $crate::bls12381::primitives::variant::Variant,
527 > {
528 generic: $crate::bls12381::certificate::threshold::Generic<P, V, $namespace>,
529 }
530
531 impl<
532 P: $crate::PublicKey,
533 V: $crate::bls12381::primitives::variant::Variant,
534 > Scheme<P, V> {
535 pub fn signer(
537 namespace: &[u8],
538 participants: commonware_utils::ordered::Set<P>,
539 polynomial: $crate::bls12381::primitives::sharing::Sharing<V>,
540 share: $crate::bls12381::primitives::group::Share,
541 ) -> Option<Self> {
542 Some(Self {
543 generic: $crate::bls12381::certificate::threshold::Generic::signer(
544 namespace,
545 participants,
546 polynomial,
547 share,
548 )?,
549 })
550 }
551
552 pub fn verifier(
554 namespace: &[u8],
555 participants: commonware_utils::ordered::Set<P>,
556 polynomial: $crate::bls12381::primitives::sharing::Sharing<V>,
557 ) -> Self {
558 Self {
559 generic: $crate::bls12381::certificate::threshold::Generic::verifier(
560 namespace,
561 participants,
562 polynomial,
563 ),
564 }
565 }
566
567 pub fn certificate_verifier(namespace: &[u8], identity: V::Public) -> Self {
569 Self {
570 generic: $crate::bls12381::certificate::threshold::Generic::certificate_verifier(
571 namespace,
572 identity,
573 ),
574 }
575 }
576
577 pub fn identity(&self) -> &V::Public {
579 self.generic.identity()
580 }
581
582 pub const fn share(&self) -> Option<&$crate::bls12381::primitives::group::Share> {
584 self.generic.share()
585 }
586 }
587
588 impl<
589 P: $crate::PublicKey,
590 V: $crate::bls12381::primitives::variant::Variant,
591 > $crate::certificate::Scheme for Scheme<P, V> {
592 type Subject<'a, D: $crate::Digest> = $subject;
593 type PublicKey = P;
594 type Signature = V::Signature;
595 type Certificate = $crate::bls12381::certificate::threshold::Certificate<V>;
596
597 fn me(&self) -> Option<commonware_utils::Participant> {
598 self.generic.me()
599 }
600
601 fn participants(&self) -> &commonware_utils::ordered::Set<Self::PublicKey> {
602 self.generic.participants()
603 }
604
605 fn sign<D: $crate::Digest>(
606 &self,
607 subject: Self::Subject<'_, D>,
608 ) -> Option<$crate::certificate::Attestation<Self>> {
609 self.generic.sign::<_, D>(subject)
610 }
611
612 fn verify_attestation<R, D>(
613 &self,
614 _rng: &mut R,
615 subject: Self::Subject<'_, D>,
616 attestation: &$crate::certificate::Attestation<Self>,
617 _strategy: &impl commonware_parallel::Strategy,
618 ) -> bool
619 where
620 R: rand_core::CryptoRngCore,
621 D: $crate::Digest,
622 {
623 self.generic
624 .verify_attestation::<_, D>(subject, attestation)
625 }
626
627 fn verify_attestations<R, D, I>(
628 &self,
629 rng: &mut R,
630 subject: Self::Subject<'_, D>,
631 attestations: I,
632 strategy: &impl commonware_parallel::Strategy,
633 ) -> $crate::certificate::Verification<Self>
634 where
635 R: rand_core::CryptoRngCore,
636 D: $crate::Digest,
637 I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
638 I::IntoIter: Send
639 {
640 self.generic
641 .verify_attestations::<_, _, D, _, _>(rng, subject, attestations, strategy)
642 }
643
644 fn assemble<I, M>(
645 &self,
646 attestations: I,
647 strategy: &impl commonware_parallel::Strategy,
648 ) -> Option<Self::Certificate>
649 where
650 I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
651 I::IntoIter: Send,
652 M: commonware_utils::Faults,
653 {
654 self.generic.assemble::<Self, _, _, M>(attestations, strategy)
655 }
656
657 fn verify_certificate<R, D, M>(
658 &self,
659 rng: &mut R,
660 subject: Self::Subject<'_, D>,
661 certificate: &Self::Certificate,
662 _strategy: &impl commonware_parallel::Strategy,
663 ) -> bool
664 where
665 R: rand_core::CryptoRngCore,
666 D: $crate::Digest,
667 M: commonware_utils::Faults,
668 {
669 self.generic
670 .verify_certificate::<Self, _, D, M>(rng, subject, certificate)
671 }
672
673 fn verify_certificates<'a, R, D, I, M>(
674 &self,
675 rng: &mut R,
676 certificates: I,
677 strategy: &impl commonware_parallel::Strategy,
678 ) -> bool
679 where
680 R: rand_core::CryptoRngCore,
681 D: $crate::Digest,
682 I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
683 M: commonware_utils::Faults,
684 {
685 self.generic
686 .verify_certificates::<Self, _, D, _, _, M>(rng, certificates, strategy)
687 }
688
689 fn is_attributable() -> bool {
690 $crate::bls12381::certificate::threshold::Generic::<P, V, $namespace>::is_attributable()
691 }
692
693 fn is_batchable() -> bool {
694 $crate::bls12381::certificate::threshold::Generic::<P, V, $namespace>::is_batchable()
695 }
696
697 fn certificate_codec_config(
698 &self,
699 ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
700 self.generic.certificate_codec_config()
701 }
702
703 fn certificate_codec_config_unbounded(
704 ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
705 $crate::bls12381::certificate::threshold::Generic::<P, V, $namespace>::certificate_codec_config_unbounded()
706 }
707 }
708 };
709}
710
711#[cfg(test)]
712mod tests {
713 use super::*;
714 use crate::{
715 bls12381::{
716 dkg,
717 primitives::{
718 ops::threshold::sign_message,
719 variant::{MinPk, MinSig, Variant},
720 },
721 },
722 certificate::Scheme as _,
723 ed25519::{self, PrivateKey as Ed25519PrivateKey},
724 sha256::Digest as Sha256Digest,
725 Signer as _,
726 };
727 use bytes::Bytes;
728 use commonware_codec::{DecodeExt, Encode};
729 use commonware_math::algebra::{Additive, Random};
730 use commonware_parallel::Sequential;
731 use commonware_utils::{ordered::Set, test_rng, Faults, N3f1, TryCollect, NZU32};
732
733 const NAMESPACE: &[u8] = b"test-bls12381-threshold";
734 const MESSAGE: &[u8] = b"test message";
735
736 #[derive(Clone, Debug)]
738 pub struct TestSubject {
739 pub message: Bytes,
740 }
741
742 impl Subject for TestSubject {
743 type Namespace = Vec<u8>;
744
745 fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] {
746 derived
747 }
748
749 fn message(&self) -> Bytes {
750 self.message.clone()
751 }
752 }
753
754 impl_certificate_bls12381_threshold!(TestSubject, Vec<u8>);
756
757 #[allow(clippy::type_complexity)]
758 fn setup_signers<V: Variant>(
759 rng: &mut impl CryptoRngCore,
760 n: u32,
761 ) -> (
762 Vec<Scheme<ed25519::PublicKey, V>>,
763 Scheme<ed25519::PublicKey, V>,
764 Sharing<V>,
765 ) {
766 let identity_keys: Vec<_> = (0..n)
768 .map(|_| Ed25519PrivateKey::random(&mut *rng))
769 .collect();
770 let participants: Set<ed25519::PublicKey> = identity_keys
771 .iter()
772 .map(|sk| sk.public_key())
773 .try_collect()
774 .unwrap();
775
776 let (polynomial, shares) =
778 dkg::deal_anonymous::<V, N3f1>(&mut *rng, Default::default(), NZU32!(n));
779
780 let signers = shares
781 .into_iter()
782 .map(|share| {
783 Scheme::signer(NAMESPACE, participants.clone(), polynomial.clone(), share).unwrap()
784 })
785 .collect();
786
787 let verifier = Scheme::verifier(NAMESPACE, participants, polynomial.clone());
788
789 (signers, verifier, polynomial)
790 }
791
792 fn test_sign_vote_roundtrip<V: Variant>() {
793 let mut rng = test_rng();
794 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
795 let scheme = &schemes[0];
796
797 let attestation = scheme
798 .sign::<Sha256Digest>(TestSubject {
799 message: Bytes::from_static(MESSAGE),
800 })
801 .unwrap();
802 assert!(scheme.verify_attestation::<_, Sha256Digest>(
803 &mut rng,
804 TestSubject {
805 message: Bytes::from_static(MESSAGE),
806 },
807 &attestation,
808 &Sequential,
809 ));
810 }
811
812 #[test]
813 fn test_sign_vote_roundtrip_variants() {
814 test_sign_vote_roundtrip::<MinPk>();
815 test_sign_vote_roundtrip::<MinSig>();
816 }
817
818 fn test_verifier_cannot_sign<V: Variant>() {
819 let mut rng = test_rng();
820 let (_, verifier, _) = setup_signers::<V>(&mut rng, 4);
821 assert!(verifier
822 .sign::<Sha256Digest>(TestSubject {
823 message: Bytes::from_static(MESSAGE),
824 })
825 .is_none());
826 }
827
828 #[test]
829 fn test_verifier_cannot_sign_variants() {
830 test_verifier_cannot_sign::<MinPk>();
831 test_verifier_cannot_sign::<MinSig>();
832 }
833
834 fn test_verify_attestations_filters_invalid<V: Variant>() {
835 let mut rng = test_rng();
836 let (schemes, _, _) = setup_signers::<V>(&mut rng, 5);
837 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
838
839 let attestations: Vec<_> = schemes
840 .iter()
841 .take(quorum)
842 .map(|s| {
843 s.sign::<Sha256Digest>(TestSubject {
844 message: Bytes::from_static(MESSAGE),
845 })
846 .unwrap()
847 })
848 .collect();
849
850 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
851 &mut rng,
852 TestSubject {
853 message: Bytes::from_static(MESSAGE),
854 },
855 attestations.clone(),
856 &Sequential,
857 );
858 assert!(result.invalid.is_empty());
859 assert_eq!(result.verified.len(), quorum);
860
861 let mut attestations_corrupted = attestations.clone();
863 attestations_corrupted[0].signer = Participant::new(999);
864 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
865 &mut rng,
866 TestSubject {
867 message: Bytes::from_static(MESSAGE),
868 },
869 attestations_corrupted,
870 &Sequential,
871 );
872 assert_eq!(result.invalid, vec![Participant::new(999)]);
873 assert_eq!(result.verified.len(), quorum - 1);
874
875 let mut attestations_corrupted = attestations;
877 attestations_corrupted[0].signature = attestations_corrupted[1].signature.clone();
878 let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
879 &mut rng,
880 TestSubject {
881 message: Bytes::from_static(MESSAGE),
882 },
883 attestations_corrupted,
884 &Sequential,
885 );
886 assert_eq!(result.invalid.len(), 1);
887 assert_eq!(result.verified.len(), quorum - 1);
888 }
889
890 #[test]
891 fn test_verify_attestations_filters_invalid_variants() {
892 test_verify_attestations_filters_invalid::<MinPk>();
893 test_verify_attestations_filters_invalid::<MinSig>();
894 }
895
896 fn test_assemble_certificate<V: Variant>() {
897 let mut rng = test_rng();
898 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
899 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
900
901 let attestations: Vec<_> = schemes
902 .iter()
903 .take(quorum)
904 .map(|s| {
905 s.sign::<Sha256Digest>(TestSubject {
906 message: Bytes::from_static(MESSAGE),
907 })
908 .unwrap()
909 })
910 .collect();
911
912 let certificate = schemes[0]
913 .assemble::<_, N3f1>(attestations, &Sequential)
914 .unwrap();
915
916 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
918 &mut rng,
919 TestSubject {
920 message: Bytes::from_static(MESSAGE),
921 },
922 &certificate,
923 &Sequential,
924 ));
925 }
926
927 #[test]
928 fn test_assemble_certificate_variants() {
929 test_assemble_certificate::<MinPk>();
930 test_assemble_certificate::<MinSig>();
931 }
932
933 fn test_verify_certificate<V: Variant>() {
934 let mut rng = test_rng();
935 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
936 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
937
938 let attestations: Vec<_> = schemes
939 .iter()
940 .take(quorum)
941 .map(|s| {
942 s.sign::<Sha256Digest>(TestSubject {
943 message: Bytes::from_static(MESSAGE),
944 })
945 .unwrap()
946 })
947 .collect();
948
949 let certificate = schemes[0]
950 .assemble::<_, N3f1>(attestations, &Sequential)
951 .unwrap();
952
953 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
954 &mut rng,
955 TestSubject {
956 message: Bytes::from_static(MESSAGE),
957 },
958 &certificate,
959 &Sequential,
960 ));
961 }
962
963 #[test]
964 fn test_verify_certificate_variants() {
965 test_verify_certificate::<MinPk>();
966 test_verify_certificate::<MinSig>();
967 }
968
969 fn test_verify_certificate_detects_corruption<V: Variant>() {
970 let mut rng = test_rng();
971 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
972 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
973
974 let attestations: Vec<_> = schemes
975 .iter()
976 .take(quorum)
977 .map(|s| {
978 s.sign::<Sha256Digest>(TestSubject {
979 message: Bytes::from_static(MESSAGE),
980 })
981 .unwrap()
982 })
983 .collect();
984
985 let certificate = schemes[0]
986 .assemble::<_, N3f1>(attestations, &Sequential)
987 .unwrap();
988
989 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
991 &mut rng,
992 TestSubject {
993 message: Bytes::from_static(MESSAGE),
994 },
995 &certificate,
996 &Sequential,
997 ));
998
999 let corrupted = Certificate::new(V::Signature::zero());
1001 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1002 &mut rng,
1003 TestSubject {
1004 message: Bytes::from_static(MESSAGE),
1005 },
1006 &corrupted,
1007 &Sequential,
1008 ));
1009 }
1010
1011 #[test]
1012 fn test_verify_certificate_detects_corruption_variants() {
1013 test_verify_certificate_detects_corruption::<MinPk>();
1014 test_verify_certificate_detects_corruption::<MinSig>();
1015 }
1016
1017 fn test_certificate_codec_roundtrip<V: Variant>() {
1018 let mut rng = test_rng();
1019 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
1020 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
1021
1022 let attestations: Vec<_> = schemes
1023 .iter()
1024 .take(quorum)
1025 .map(|s| {
1026 s.sign::<Sha256Digest>(TestSubject {
1027 message: Bytes::from_static(MESSAGE),
1028 })
1029 .unwrap()
1030 })
1031 .collect();
1032
1033 let certificate = schemes[0]
1034 .assemble::<_, N3f1>(attestations, &Sequential)
1035 .unwrap();
1036 let encoded = certificate.encode();
1037 let decoded = Certificate::<V>::decode(encoded).expect("decode certificate");
1038 assert_eq!(decoded, certificate);
1039 }
1040
1041 #[test]
1042 fn test_certificate_codec_roundtrip_variants() {
1043 test_certificate_codec_roundtrip::<MinPk>();
1044 test_certificate_codec_roundtrip::<MinSig>();
1045 }
1046
1047 fn test_certificate_rejects_sub_quorum<V: Variant>() {
1048 let mut rng = test_rng();
1049 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
1050 let sub_quorum = 2; let attestations: Vec<_> = schemes
1053 .iter()
1054 .take(sub_quorum)
1055 .map(|s| {
1056 s.sign::<Sha256Digest>(TestSubject {
1057 message: Bytes::from_static(MESSAGE),
1058 })
1059 .unwrap()
1060 })
1061 .collect();
1062
1063 assert!(schemes[0]
1064 .assemble::<_, N3f1>(attestations, &Sequential)
1065 .is_none());
1066 }
1067
1068 #[test]
1069 fn test_certificate_rejects_sub_quorum_variants() {
1070 test_certificate_rejects_sub_quorum::<MinPk>();
1071 test_certificate_rejects_sub_quorum::<MinSig>();
1072 }
1073
1074 fn test_verify_certificates_batch<V: Variant>() {
1075 let mut rng = test_rng();
1076 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
1077 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
1078
1079 let messages: [Bytes; 3] = [
1080 Bytes::from_static(b"msg1"),
1081 Bytes::from_static(b"msg2"),
1082 Bytes::from_static(b"msg3"),
1083 ];
1084 let mut certificates = Vec::new();
1085
1086 for msg in &messages {
1087 let attestations: Vec<_> = schemes
1088 .iter()
1089 .take(quorum)
1090 .map(|s| {
1091 s.sign::<Sha256Digest>(TestSubject {
1092 message: msg.clone(),
1093 })
1094 .unwrap()
1095 })
1096 .collect();
1097 certificates.push(
1098 schemes[0]
1099 .assemble::<_, N3f1>(attestations, &Sequential)
1100 .unwrap(),
1101 );
1102 }
1103
1104 let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
1105 (
1106 TestSubject {
1107 message: msg.clone(),
1108 },
1109 cert,
1110 )
1111 });
1112
1113 assert!(verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1114 &mut rng,
1115 certs_iter,
1116 &Sequential
1117 ));
1118 }
1119
1120 #[test]
1121 fn test_verify_certificates_batch_variants() {
1122 test_verify_certificates_batch::<MinPk>();
1123 test_verify_certificates_batch::<MinSig>();
1124 }
1125
1126 fn test_verify_certificates_batch_detects_failure<V: Variant>() {
1127 let mut rng = test_rng();
1128 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
1129 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
1130
1131 let messages: [Bytes; 2] = [Bytes::from_static(b"msg1"), Bytes::from_static(b"msg2")];
1132 let mut certificates = Vec::new();
1133
1134 for msg in &messages {
1135 let attestations: Vec<_> = schemes
1136 .iter()
1137 .take(quorum)
1138 .map(|s| {
1139 s.sign::<Sha256Digest>(TestSubject {
1140 message: msg.clone(),
1141 })
1142 .unwrap()
1143 })
1144 .collect();
1145 certificates.push(
1146 schemes[0]
1147 .assemble::<_, N3f1>(attestations, &Sequential)
1148 .unwrap(),
1149 );
1150 }
1151
1152 certificates[1] = Certificate::new(V::Signature::zero());
1154
1155 let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
1156 (
1157 TestSubject {
1158 message: msg.clone(),
1159 },
1160 cert,
1161 )
1162 });
1163
1164 assert!(!verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1165 &mut rng,
1166 certs_iter,
1167 &Sequential
1168 ));
1169 }
1170
1171 #[test]
1172 fn test_verify_certificates_batch_detects_failure_variants() {
1173 test_verify_certificates_batch_detects_failure::<MinPk>();
1174 test_verify_certificates_batch_detects_failure::<MinSig>();
1175 }
1176
1177 fn test_certificate_verifier<V: Variant>() {
1178 let mut rng = test_rng();
1179 let (schemes, _, polynomial) = setup_signers::<V>(&mut rng, 4);
1180 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
1181
1182 let attestations: Vec<_> = schemes
1183 .iter()
1184 .take(quorum)
1185 .map(|s| {
1186 s.sign::<Sha256Digest>(TestSubject {
1187 message: Bytes::from_static(MESSAGE),
1188 })
1189 .unwrap()
1190 })
1191 .collect();
1192
1193 let certificate = schemes[0]
1194 .assemble::<_, N3f1>(attestations, &Sequential)
1195 .unwrap();
1196
1197 let identity = polynomial.public();
1199 let cert_verifier =
1200 Scheme::<ed25519::PublicKey, V>::certificate_verifier(NAMESPACE, *identity);
1201
1202 assert!(cert_verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1204 &mut rng,
1205 TestSubject {
1206 message: Bytes::from_static(MESSAGE),
1207 },
1208 &certificate,
1209 &Sequential,
1210 ));
1211
1212 assert!(cert_verifier
1214 .sign::<Sha256Digest>(TestSubject {
1215 message: Bytes::from_static(MESSAGE),
1216 })
1217 .is_none());
1218 }
1219
1220 #[test]
1221 fn test_certificate_verifier_variants() {
1222 test_certificate_verifier::<MinPk>();
1223 test_certificate_verifier::<MinSig>();
1224 }
1225
1226 #[test]
1227 fn test_is_not_attributable() {
1228 assert!(!Generic::<ed25519::PublicKey, MinPk, Vec<u8>>::is_attributable());
1229 assert!(!Scheme::<ed25519::PublicKey, MinPk>::is_attributable());
1230 assert!(!Generic::<ed25519::PublicKey, MinSig, Vec<u8>>::is_attributable());
1231 assert!(!Scheme::<ed25519::PublicKey, MinSig>::is_attributable());
1232 }
1233
1234 #[test]
1235 fn test_is_batchable() {
1236 assert!(Generic::<ed25519::PublicKey, MinPk, Vec<u8>>::is_batchable());
1237 assert!(Scheme::<ed25519::PublicKey, MinPk>::is_batchable());
1238 assert!(Generic::<ed25519::PublicKey, MinSig, Vec<u8>>::is_batchable());
1239 assert!(Scheme::<ed25519::PublicKey, MinSig>::is_batchable());
1240 }
1241
1242 fn test_verifier_accepts_votes<V: Variant>() {
1243 let mut rng = test_rng();
1244 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
1245
1246 let vote = schemes[1]
1247 .sign::<Sha256Digest>(TestSubject {
1248 message: Bytes::from_static(MESSAGE),
1249 })
1250 .unwrap();
1251 assert!(verifier.verify_attestation::<_, Sha256Digest>(
1252 &mut rng,
1253 TestSubject {
1254 message: Bytes::from_static(MESSAGE),
1255 },
1256 &vote,
1257 &Sequential,
1258 ));
1259 }
1260
1261 #[test]
1262 fn test_verifier_accepts_votes_variants() {
1263 test_verifier_accepts_votes::<MinPk>();
1264 test_verifier_accepts_votes::<MinSig>();
1265 }
1266
1267 fn test_scheme_clone_and_verifier<V: Variant>() {
1268 let mut rng = test_rng();
1269 let (schemes, verifier, _) = setup_signers::<V>(&mut rng, 4);
1270
1271 let signer = schemes[0].clone();
1273 assert!(
1274 signer
1275 .sign::<Sha256Digest>(TestSubject {
1276 message: Bytes::from_static(MESSAGE),
1277 })
1278 .is_some(),
1279 "signer should produce votes"
1280 );
1281
1282 assert!(
1284 verifier
1285 .sign::<Sha256Digest>(TestSubject {
1286 message: Bytes::from_static(MESSAGE),
1287 })
1288 .is_none(),
1289 "verifier should not produce votes"
1290 );
1291 }
1292
1293 #[test]
1294 fn test_scheme_clone_and_verifier_variants() {
1295 test_scheme_clone_and_verifier::<MinPk>();
1296 test_scheme_clone_and_verifier::<MinSig>();
1297 }
1298
1299 fn certificate_verifier_panics_on_vote<V: Variant>() {
1300 let mut rng = test_rng();
1301 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
1302 let certificate_verifier = Scheme::<ed25519::PublicKey, V>::certificate_verifier(
1303 NAMESPACE,
1304 *schemes[0].identity(),
1305 );
1306
1307 let vote = schemes[1]
1308 .sign::<Sha256Digest>(TestSubject {
1309 message: Bytes::from_static(MESSAGE),
1310 })
1311 .unwrap();
1312
1313 certificate_verifier.verify_attestation::<_, Sha256Digest>(
1315 &mut rng,
1316 TestSubject {
1317 message: Bytes::from_static(MESSAGE),
1318 },
1319 &vote,
1320 &Sequential,
1321 );
1322 }
1323
1324 #[test]
1325 #[should_panic(expected = "can only be called for signer and verifier")]
1326 fn test_certificate_verifier_panics_on_vote_min_pk() {
1327 certificate_verifier_panics_on_vote::<MinPk>();
1328 }
1329
1330 #[test]
1331 #[should_panic(expected = "can only be called for signer and verifier")]
1332 fn test_certificate_verifier_panics_on_vote_min_sig() {
1333 certificate_verifier_panics_on_vote::<MinSig>();
1334 }
1335
1336 fn signer_shares_must_match_participant_indices<V: Variant>() {
1337 let mut rng = test_rng();
1338
1339 let identity_keys: Vec<_> = (0..4)
1341 .map(|_| Ed25519PrivateKey::random(&mut rng))
1342 .collect();
1343 let participants: Set<ed25519::PublicKey> = identity_keys
1344 .iter()
1345 .map(|sk| sk.public_key())
1346 .try_collect()
1347 .unwrap();
1348
1349 let (polynomial, mut shares) =
1350 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(4));
1351 shares[0].index = Participant::new(999);
1352 Scheme::<ed25519::PublicKey, V>::signer(
1353 NAMESPACE,
1354 participants,
1355 polynomial,
1356 shares[0].clone(),
1357 );
1358 }
1359
1360 #[test]
1361 #[should_panic(expected = "share index must match participant indices")]
1362 fn test_signer_shares_must_match_participant_indices_min_pk() {
1363 signer_shares_must_match_participant_indices::<MinPk>();
1364 }
1365
1366 #[test]
1367 #[should_panic(expected = "share index must match participant indices")]
1368 fn test_signer_shares_must_match_participant_indices_min_sig() {
1369 signer_shares_must_match_participant_indices::<MinSig>();
1370 }
1371
1372 fn make_participants<R: rand::RngCore + rand::CryptoRng + Clone>(
1373 rng: &mut R,
1374 n: u32,
1375 ) -> Set<ed25519::PublicKey> {
1376 (0..n)
1377 .map(|_| Ed25519PrivateKey::random(&mut *rng).public_key())
1378 .try_collect()
1379 .expect("participants are unique")
1380 }
1381
1382 fn signer_polynomial_threshold_must_equal_quorum<V: Variant>() {
1383 let mut rng = test_rng();
1384 let participants = make_participants(&mut rng, 5);
1385 let (polynomial, shares) =
1389 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(2));
1390 Scheme::<ed25519::PublicKey, V>::signer(
1391 NAMESPACE,
1392 participants,
1393 polynomial,
1394 shares[0].clone(),
1395 );
1396 }
1397
1398 #[test]
1399 #[should_panic(expected = "polynomial total must equal participant len")]
1400 fn test_signer_polynomial_threshold_must_equal_quorum_min_pk() {
1401 signer_polynomial_threshold_must_equal_quorum::<MinPk>();
1402 }
1403
1404 #[test]
1405 #[should_panic(expected = "polynomial total must equal participant len")]
1406 fn test_signer_polynomial_threshold_must_equal_quorum_min_sig() {
1407 signer_polynomial_threshold_must_equal_quorum::<MinSig>();
1408 }
1409
1410 fn verifier_polynomial_threshold_must_equal_quorum<V: Variant>() {
1411 let mut rng = test_rng();
1412 let participants = make_participants(&mut rng, 5);
1413 let (polynomial, _) =
1416 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(2));
1417 Scheme::<ed25519::PublicKey, V>::verifier(NAMESPACE, participants, polynomial);
1418 }
1419
1420 #[test]
1421 #[should_panic(expected = "polynomial total must equal participant len")]
1422 fn test_verifier_polynomial_threshold_must_equal_quorum_min_pk() {
1423 verifier_polynomial_threshold_must_equal_quorum::<MinPk>();
1424 }
1425
1426 #[test]
1427 #[should_panic(expected = "polynomial total must equal participant len")]
1428 fn test_verifier_polynomial_threshold_must_equal_quorum_min_sig() {
1429 verifier_polynomial_threshold_must_equal_quorum::<MinSig>();
1430 }
1431
1432 fn certificate_decode_rejects_length_mismatch<V: Variant>() {
1433 let mut rng = test_rng();
1434 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
1435 let quorum = N3f1::quorum(schemes.len() as u32) as usize;
1436
1437 let attestations: Vec<_> = schemes
1438 .iter()
1439 .take(quorum)
1440 .map(|s| {
1441 s.sign::<Sha256Digest>(TestSubject {
1442 message: Bytes::from_static(MESSAGE),
1443 })
1444 .unwrap()
1445 })
1446 .collect();
1447
1448 let certificate = schemes[0]
1449 .assemble::<_, N3f1>(attestations, &Sequential)
1450 .unwrap();
1451 let mut encoded = certificate.encode();
1452 encoded.truncate(encoded.len() - 1);
1453 assert!(V::Signature::decode(encoded).is_err());
1454 }
1455
1456 #[test]
1457 fn test_certificate_decode_rejects_length_mismatch_variants() {
1458 certificate_decode_rejects_length_mismatch::<MinPk>();
1459 certificate_decode_rejects_length_mismatch::<MinSig>();
1460 }
1461
1462 fn sign_vote_partial_matches_share<V: Variant>() {
1463 let mut rng = test_rng();
1464 let (schemes, _, _) = setup_signers::<V>(&mut rng, 4);
1465 let scheme = &schemes[0];
1466
1467 let signature = scheme
1468 .sign::<Sha256Digest>(TestSubject {
1469 message: Bytes::from_static(MESSAGE),
1470 })
1471 .unwrap();
1472
1473 let share = scheme.share().expect("expected signer");
1475
1476 let expected = sign_message::<V>(share, NAMESPACE, MESSAGE);
1477
1478 assert_eq!(signature.signer, share.index);
1479 assert_eq!(signature.signature.get().unwrap(), &expected.value);
1480 }
1481
1482 #[test]
1483 fn test_sign_vote_partial_matches_share_variants() {
1484 sign_vote_partial_matches_share::<MinPk>();
1485 sign_vote_partial_matches_share::<MinSig>();
1486 }
1487
1488 #[cfg(feature = "arbitrary")]
1489 mod conformance {
1490 use super::*;
1491 use commonware_codec::conformance::CodecConformance;
1492
1493 commonware_conformance::conformance_tests! {
1494 CodecConformance<Certificate<MinSig>>,
1495 }
1496 }
1497}