1#[commonware_macros::stability(ALPHA)]
34use crate::simplex::scheme::seed_namespace;
35use crate::{
36 simplex::{
37 scheme::Namespace,
38 types::{Finalization, Notarization, Subject},
39 },
40 types::{Epoch, Participant, Round, View},
41 Epochable, Viewable,
42};
43use bytes::{Buf, BufMut};
44use commonware_codec::{
45 types::lazy::Lazy, Encode, EncodeSize, Error, FixedSize, Read, ReadExt, Write,
46};
47#[commonware_macros::stability(ALPHA)]
48use commonware_cryptography::bls12381::tle;
49use commonware_cryptography::{
50 bls12381::primitives::{
51 group::Share,
52 ops::{self, batch, threshold},
53 sharing::Sharing,
54 variant::{PartialSignature, Variant},
55 },
56 certificate::{self, Attestation, Subject as CertificateSubject, Verification},
57 Digest, PublicKey,
58};
59use commonware_macros::stability;
60use commonware_parallel::Strategy;
61use commonware_utils::{ordered::Set, Faults};
62use rand::rngs::StdRng;
63use rand_core::{CryptoRng, SeedableRng};
64use std::{
65 collections::{BTreeSet, HashMap},
66 fmt::Debug,
67};
68
69#[derive(Clone, Debug)]
71enum Role<P: PublicKey, V: Variant> {
72 Signer {
73 participants: Set<P>,
75 polynomial: Sharing<V>,
77 share: Share,
79 namespace: Namespace,
81 },
82 Verifier {
83 participants: Set<P>,
85 polynomial: Sharing<V>,
87 namespace: Namespace,
89 },
90 CertificateVerifier {
91 identity: V::Public,
93 namespace: Namespace,
95 },
96}
97
98#[derive(Clone, Debug)]
107pub struct Scheme<P: PublicKey, V: Variant> {
108 role: Role<P, V>,
109}
110
111impl<P: PublicKey, V: Variant> Scheme<P, V> {
112 pub fn signer(
125 namespace: &[u8],
126 participants: Set<P>,
127 polynomial: Sharing<V>,
128 share: Share,
129 ) -> Option<Self> {
130 assert_eq!(
131 polynomial.total().get() as usize,
132 participants.len(),
133 "polynomial total must equal participant len"
134 );
135 polynomial.precompute_partial_publics();
136 let partial_public = polynomial
137 .partial_public(share.index)
138 .expect("share index must match participant indices");
139 if partial_public == share.public::<V>() {
140 Some(Self {
141 role: Role::Signer {
142 participants,
143 polynomial,
144 share,
145 namespace: Namespace::new(namespace),
146 },
147 })
148 } else {
149 None
150 }
151 }
152
153 pub fn verifier(namespace: &[u8], participants: Set<P>, polynomial: Sharing<V>) -> Self {
163 assert_eq!(
164 polynomial.total().get() as usize,
165 participants.len(),
166 "polynomial total must equal participant len"
167 );
168 polynomial.precompute_partial_publics();
169
170 Self {
171 role: Role::Verifier {
172 participants,
173 polynomial,
174 namespace: Namespace::new(namespace),
175 },
176 }
177 }
178
179 pub fn certificate_verifier(namespace: &[u8], identity: V::Public) -> Self {
187 Self {
188 role: Role::CertificateVerifier {
189 identity,
190 namespace: Namespace::new(namespace),
191 },
192 }
193 }
194
195 pub fn participants(&self) -> &Set<P> {
197 match &self.role {
198 Role::Signer { participants, .. } => participants,
199 Role::Verifier { participants, .. } => participants,
200 Role::CertificateVerifier { .. } => {
201 panic!("can only be called for signer and verifier")
202 }
203 }
204 }
205
206 pub fn identity(&self) -> &V::Public {
208 match &self.role {
209 Role::Signer { polynomial, .. } => polynomial.public(),
210 Role::Verifier { polynomial, .. } => polynomial.public(),
211 Role::CertificateVerifier { identity, .. } => identity,
212 }
213 }
214
215 pub const fn share(&self) -> Option<&Share> {
217 match &self.role {
218 Role::Signer { share, .. } => Some(share),
219 _ => None,
220 }
221 }
222
223 pub fn polynomial(&self) -> &Sharing<V> {
225 match &self.role {
226 Role::Signer { polynomial, .. } => polynomial,
227 Role::Verifier { polynomial, .. } => polynomial,
228 Role::CertificateVerifier { .. } => {
229 panic!("can only be called for signer and verifier")
230 }
231 }
232 }
233
234 const fn namespace(&self) -> &Namespace {
236 match &self.role {
237 Role::Signer { namespace, .. } => namespace,
238 Role::Verifier { namespace, .. } => namespace,
239 Role::CertificateVerifier { namespace, .. } => namespace,
240 }
241 }
242
243 #[stability(ALPHA)]
249 pub fn encrypt<R: CryptoRng>(
250 &self,
251 rng: &mut R,
252 target: Round,
253 message: impl Into<tle::Block>,
254 ) -> tle::Ciphertext<V> {
255 let block = message.into();
256 let target_message = target.encode();
257 tle::encrypt(
258 rng,
259 *self.identity(),
260 (&self.namespace().seed, &target_message),
261 &block,
262 )
263 }
264}
265
266#[stability(ALPHA)]
272pub fn encrypt<R: CryptoRng, V: Variant>(
273 rng: &mut R,
274 identity: V::Public,
275 namespace: &[u8],
276 target: Round,
277 message: impl Into<tle::Block>,
278) -> tle::Ciphertext<V> {
279 let block = message.into();
280 let seed_ns = seed_namespace(namespace);
281 let target_message = target.encode();
282 tle::encrypt(rng, identity, (&seed_ns, &target_message), &block)
283}
284
285#[cfg(feature = "mocks")]
290pub fn fixture<V, R>(
291 rng: &mut R,
292 namespace: &[u8],
293 n: u32,
294) -> commonware_cryptography::certificate::mocks::Fixture<
295 Scheme<commonware_cryptography::ed25519::PublicKey, V>,
296>
297where
298 V: Variant,
299 R: rand_core::CryptoRng,
300{
301 commonware_cryptography::bls12381::certificate::threshold::mocks::fixture::<_, V, _>(
302 rng,
303 namespace,
304 n,
305 |namespace, participants, polynomial, share| {
306 Scheme::signer(namespace, participants, polynomial, share)
307 },
308 |namespace, participants, polynomial| Scheme::verifier(namespace, participants, polynomial),
309 )
310}
311
312#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
314pub struct Signature<V: Variant> {
315 pub vote_signature: V::Signature,
317 pub seed_signature: V::Signature,
319}
320
321impl<V: Variant> Write for Signature<V> {
322 fn write(&self, writer: &mut impl BufMut) {
323 self.vote_signature.write(writer);
324 self.seed_signature.write(writer);
325 }
326}
327
328impl<V: Variant> Read for Signature<V> {
329 type Cfg = ();
330
331 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
332 let vote_signature = V::Signature::read(reader)?;
333 let seed_signature = V::Signature::read(reader)?;
334
335 Ok(Self {
336 vote_signature,
337 seed_signature,
338 })
339 }
340}
341
342impl<V: Variant> FixedSize for Signature<V> {
343 const SIZE: usize = V::Signature::SIZE * 2;
344}
345
346#[cfg(feature = "arbitrary")]
347impl<V: Variant> arbitrary::Arbitrary<'_> for Signature<V>
348where
349 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
350{
351 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
352 Ok(Self {
353 vote_signature: u.arbitrary()?,
354 seed_signature: u.arbitrary()?,
355 })
356 }
357}
358
359#[derive(Clone, Debug, PartialEq, Eq, Hash)]
361pub struct Certificate<V: Variant> {
362 pub signature: Lazy<Signature<V>>,
364}
365
366impl<V: Variant> Certificate<V> {
367 pub fn get(&self) -> Option<&Signature<V>> {
371 self.signature.get()
372 }
373}
374
375impl<V: Variant> From<Signature<V>> for Certificate<V> {
376 fn from(signature: Signature<V>) -> Self {
377 Self {
378 signature: Lazy::from(signature),
379 }
380 }
381}
382
383impl<V: Variant> Write for Certificate<V> {
384 fn write(&self, writer: &mut impl BufMut) {
385 self.signature.write(writer);
386 }
387}
388
389impl<V: Variant> Read for Certificate<V> {
390 type Cfg = ();
391
392 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
393 let signature = Lazy::<Signature<V>>::read(reader)?;
394 Ok(Self { signature })
395 }
396}
397
398impl<V: Variant> FixedSize for Certificate<V> {
399 const SIZE: usize = Signature::<V>::SIZE;
400}
401
402#[cfg(feature = "arbitrary")]
403impl<V: Variant> arbitrary::Arbitrary<'_> for Certificate<V>
404where
405 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
406{
407 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
408 Ok(Self {
409 signature: Lazy::from(u.arbitrary::<Signature<V>>()?),
410 })
411 }
412}
413
414#[derive(Clone, Debug, PartialEq, Hash, Eq)]
416pub struct Seed<V: Variant> {
417 pub round: Round,
419 pub signature: V::Signature,
421}
422
423impl<V: Variant> Seed<V> {
424 pub const fn new(round: Round, signature: V::Signature) -> Self {
426 Self { round, signature }
427 }
428
429 pub fn verify<P: PublicKey>(&self, scheme: &Scheme<P, V>) -> bool {
431 let seed_message = self.round.encode();
432
433 ops::verify_message::<V>(
434 scheme.identity(),
435 &scheme.namespace().seed,
436 &seed_message,
437 &self.signature,
438 )
439 .is_ok()
440 }
441
442 pub const fn round(&self) -> Round {
444 self.round
445 }
446
447 #[stability(ALPHA)]
452 pub fn decrypt(&self, ciphertext: &tle::Ciphertext<V>) -> Option<tle::Block> {
453 decrypt(self, ciphertext)
454 }
455}
456
457#[stability(ALPHA)]
463pub fn decrypt<V: Variant>(seed: &Seed<V>, ciphertext: &tle::Ciphertext<V>) -> Option<tle::Block> {
464 tle::decrypt(&seed.signature, ciphertext)
465}
466
467impl<V: Variant> Epochable for Seed<V> {
468 fn epoch(&self) -> Epoch {
469 self.round.epoch()
470 }
471}
472
473impl<V: Variant> Viewable for Seed<V> {
474 fn view(&self) -> View {
475 self.round.view()
476 }
477}
478
479impl<V: Variant> Write for Seed<V> {
480 fn write(&self, writer: &mut impl BufMut) {
481 self.round.write(writer);
482 self.signature.write(writer);
483 }
484}
485
486impl<V: Variant> Read for Seed<V> {
487 type Cfg = ();
488
489 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
490 let round = Round::read(reader)?;
491 let signature = V::Signature::read(reader)?;
492
493 Ok(Self { round, signature })
494 }
495}
496
497impl<V: Variant> EncodeSize for Seed<V> {
498 fn encode_size(&self) -> usize {
499 self.round.encode_size() + self.signature.encode_size()
500 }
501}
502
503#[cfg(feature = "arbitrary")]
504impl<V: Variant> arbitrary::Arbitrary<'_> for Seed<V>
505where
506 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
507{
508 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
509 Ok(Self {
510 round: u.arbitrary()?,
511 signature: u.arbitrary()?,
512 })
513 }
514}
515
516pub trait Seedable<V: Variant> {
518 fn seed(&self) -> Seed<V>;
520}
521
522impl<P: PublicKey, V: Variant, D: Digest> Seedable<V> for Notarization<Scheme<P, V>, D> {
523 fn seed(&self) -> Seed<V> {
524 let cert = self
525 .certificate
526 .get()
527 .expect("verified certificate must decode");
528 Seed::new(self.proposal.round, cert.seed_signature)
529 }
530}
531
532impl<P: PublicKey, V: Variant, D: Digest> Seedable<V> for Finalization<Scheme<P, V>, D> {
533 fn seed(&self) -> Seed<V> {
534 let cert = self
535 .certificate
536 .get()
537 .expect("verified certificate must decode");
538 Seed::new(self.proposal.round, cert.seed_signature)
539 }
540}
541
542fn seed_message_from_subject<D: Digest>(subject: &Subject<'_, D>) -> bytes::Bytes {
546 match subject {
547 Subject::Notarize { proposal } | Subject::Finalize { proposal } => proposal.round.encode(),
548 Subject::Nullify { round } => round.encode(),
549 }
550}
551
552impl<P: PublicKey, V: Variant> certificate::Verifier for Scheme<P, V> {
553 type Subject<'a, D: Digest> = Subject<'a, D>;
554 type PublicKey = P;
555 type Certificate = Certificate<V>;
556
557 fn verify_certificate<R, D, M>(
558 &self,
559 rng: &mut R,
560 subject: Subject<'_, D>,
561 certificate: &Self::Certificate,
562 strategy: &impl Strategy,
563 ) -> bool
564 where
565 R: CryptoRng,
566 D: Digest,
567 M: Faults,
568 {
569 let Some(cert) = certificate.get() else {
570 return false;
571 };
572
573 let identity = self.identity();
574 let namespace = self.namespace();
575
576 let vote_namespace = subject.namespace(namespace);
577 let vote_message = subject.message();
578 let seed_message = seed_message_from_subject(&subject);
579
580 let entries = &[
581 (vote_namespace, vote_message.as_ref(), cert.vote_signature),
582 (&namespace.seed, seed_message.as_ref(), cert.seed_signature),
583 ];
584 batch::verify_same_signer::<_, V, _>(rng, identity, entries, strategy).is_ok()
585 }
586
587 fn verify_certificates<'a, R, D, I, M>(
588 &self,
589 rng: &mut R,
590 certificates: I,
591 strategy: &impl Strategy,
592 ) -> bool
593 where
594 R: CryptoRng,
595 D: Digest,
596 I: Iterator<Item = (Subject<'a, D>, &'a Self::Certificate)>,
597 M: Faults,
598 {
599 let identity = self.identity();
600 let namespace = self.namespace();
601
602 let mut seeds = HashMap::new();
603 let mut entries: Vec<_> = Vec::new();
604
605 for (context, certificate) in certificates {
606 let Some(cert) = certificate.get() else {
607 return false;
608 };
609
610 let vote_namespace = context.namespace(namespace);
612 let vote_message = context.message();
613 entries.push((vote_namespace, vote_message, cert.vote_signature));
614
615 let seed_message = seed_message_from_subject(&context);
620 if let Some(previous) = seeds.get(&seed_message) {
621 if *previous != cert.seed_signature {
622 return false;
623 }
624 } else {
625 entries.push((&namespace.seed, seed_message.clone(), cert.seed_signature));
626 seeds.insert(seed_message, cert.seed_signature);
627 }
628 }
629
630 let entries_refs: Vec<_> = entries
633 .iter()
634 .map(|(ns, msg, sig)| (*ns, msg.as_ref(), *sig))
635 .collect();
636 batch::verify_same_signer::<_, V, _>(rng, identity, &entries_refs, strategy).is_ok()
637 }
638
639 fn is_batchable() -> bool {
640 true
641 }
642
643 fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg {}
644
645 fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg {}
646}
647
648impl<P: PublicKey, V: Variant> certificate::Scheme for Scheme<P, V> {
649 type Signature = Signature<V>;
650
651 fn me(&self) -> Option<Participant> {
652 match &self.role {
653 Role::Signer { share, .. } => Some(share.index),
654 _ => None,
655 }
656 }
657
658 fn participants(&self) -> &Set<Self::PublicKey> {
659 self.participants()
660 }
661
662 fn sign<D: Digest>(&self, subject: Subject<'_, D>) -> Option<Attestation<Self>> {
663 let share = self.share()?;
664
665 let namespace = self.namespace();
666 let vote_namespace = subject.namespace(namespace);
667 let vote_message = subject.message();
668 let vote_signature =
669 threshold::sign_message::<V>(share, vote_namespace, &vote_message).value;
670
671 let seed_message = seed_message_from_subject(&subject);
672 let seed_signature =
673 threshold::sign_message::<V>(share, &namespace.seed, &seed_message).value;
674
675 let signature = Signature {
676 vote_signature,
677 seed_signature,
678 };
679
680 Some(Attestation {
681 signer: share.index,
682 signature: signature.into(),
683 })
684 }
685
686 fn verify_attestation<R, D>(
687 &self,
688 rng: &mut R,
689 subject: Subject<'_, D>,
690 attestation: &Attestation<Self>,
691 strategy: &impl Strategy,
692 ) -> bool
693 where
694 R: CryptoRng,
695 D: Digest,
696 {
697 let Ok(evaluated) = self.polynomial().partial_public(attestation.signer) else {
698 return false;
699 };
700
701 let namespace = self.namespace();
702 let vote_namespace = subject.namespace(namespace);
703 let vote_message = subject.message();
704 let seed_message = seed_message_from_subject(&subject);
705
706 let Some(signature) = attestation.signature.get() else {
707 return false;
708 };
709
710 let entries = &[
711 (
712 vote_namespace,
713 vote_message.as_ref(),
714 signature.vote_signature,
715 ),
716 (
717 &namespace.seed,
718 seed_message.as_ref(),
719 signature.seed_signature,
720 ),
721 ];
722 batch::verify_same_signer::<_, V, _>(rng, &evaluated, entries, strategy).is_ok()
723 }
724
725 fn verify_attestations<R, D, I>(
726 &self,
727 rng: &mut R,
728 subject: Subject<'_, D>,
729 attestations: I,
730 strategy: &impl Strategy,
731 ) -> Verification<Self>
732 where
733 R: CryptoRng,
734 D: Digest,
735 I: IntoIterator<Item = Attestation<Self>>,
736 I::IntoIter: Send,
737 {
738 let namespace = self.namespace();
739 let (partials, failures) =
740 strategy.map_partition_collect_vec(attestations.into_iter(), |attestation| {
741 let index = attestation.signer;
742 let value = attestation.signature.get().map(|sig| {
743 (
744 PartialSignature::<V> {
745 index,
746 value: sig.vote_signature,
747 },
748 PartialSignature::<V> {
749 index,
750 value: sig.seed_signature,
751 },
752 )
753 });
754 (index, value)
755 });
756
757 let polynomial = self.polynomial();
758 let vote_namespace = subject.namespace(namespace);
759 let vote_message = subject.message();
760 let seed_message = seed_message_from_subject(&subject);
761
762 let mut vote_rng_seed = [0u8; 32];
764 let mut seed_rng_seed = [0u8; 32];
765 rng.fill_bytes(&mut vote_rng_seed);
766 rng.fill_bytes(&mut seed_rng_seed);
767
768 let (vote_invalid, seed_invalid) = strategy.join(
770 || {
771 let mut vote_rng = StdRng::from_seed(vote_rng_seed);
772 match threshold::batch_verify_same_message::<_, V, _>(
773 &mut vote_rng,
774 polynomial,
775 vote_namespace,
776 &vote_message,
777 partials.iter().map(|(vote, _)| vote),
778 strategy,
779 ) {
780 Ok(()) => BTreeSet::new(),
781 Err(errs) => errs.into_iter().map(|p| p.index).collect(),
782 }
783 },
784 || {
785 let mut seed_rng = StdRng::from_seed(seed_rng_seed);
786 match threshold::batch_verify_same_message::<_, V, _>(
787 &mut seed_rng,
788 polynomial,
789 &namespace.seed,
790 &seed_message,
791 partials.iter().map(|(_, seed)| seed),
792 strategy,
793 ) {
794 Ok(()) => BTreeSet::new(),
795 Err(errs) => errs.into_iter().map(|p| p.index).collect(),
796 }
797 },
798 );
799
800 let mut invalid: BTreeSet<_> = vote_invalid.union(&seed_invalid).copied().collect();
802 invalid.extend(failures);
803
804 let verified = partials
806 .into_iter()
807 .filter(|(vote, _)| !invalid.contains(&vote.index))
808 .map(|(vote, seed)| Attestation {
809 signer: vote.index,
810 signature: Signature {
811 vote_signature: vote.value,
812 seed_signature: seed.value,
813 }
814 .into(),
815 })
816 .collect();
817
818 Verification::new(verified, invalid.into_iter().collect())
819 }
820
821 fn assemble<I, M>(&self, attestations: I, strategy: &impl Strategy) -> Option<Self::Certificate>
822 where
823 I: IntoIterator<Item = Attestation<Self>>,
824 I::IntoIter: Send,
825 M: Faults,
826 {
827 let (partials, failures) =
828 strategy.map_partition_collect_vec(attestations.into_iter(), |attestation| {
829 let index = attestation.signer;
830 let value = attestation.signature.get().map(|sig| {
831 (
832 PartialSignature::<V> {
833 index,
834 value: sig.vote_signature,
835 },
836 PartialSignature::<V> {
837 index,
838 value: sig.seed_signature,
839 },
840 )
841 });
842 (index, value)
843 });
844 if !failures.is_empty() {
845 return None;
846 }
847 let (vote_partials, seed_partials): (Vec<_>, Vec<_>) = partials.into_iter().unzip();
848
849 let quorum = self.polynomial();
850 if vote_partials.len() < quorum.required::<M>() as usize {
851 return None;
852 }
853
854 let (vote_signature, seed_signature) = threshold::recover_pair::<V, _, M>(
855 quorum,
856 vote_partials.iter(),
857 seed_partials.iter(),
858 strategy,
859 )
860 .ok()?;
861
862 Some(
863 Signature {
864 vote_signature,
865 seed_signature,
866 }
867 .into(),
868 )
869 }
870
871 fn is_attributable() -> bool {
872 false
873 }
874}
875
876#[cfg(test)]
877mod tests {
878 use super::*;
879 use crate::{
880 simplex::{
881 scheme::{notarize_namespace, seed_namespace},
882 types::{Finalization, Finalize, Notarization, Notarize, Proposal, Subject},
883 },
884 types::{Round, View},
885 };
886 use commonware_codec::{Decode, Encode};
887 use commonware_cryptography::{
888 bls12381::{
889 dkg::feldman_desmedt as dkg,
890 primitives::{
891 group::Scalar,
892 ops::threshold,
893 variant::{MinPk, MinSig, Variant},
894 },
895 },
896 certificate::{mocks::Fixture, Scheme as _, Verifier as _},
897 ed25519,
898 ed25519::certificate::mocks::participants as ed25519_participants,
899 sha256::Digest as Sha256Digest,
900 Hasher, Sha256,
901 };
902 use commonware_math::algebra::{CryptoGroup, Random};
903 use commonware_parallel::Sequential;
904 use commonware_utils::{test_rng, Faults, N3f1, NZU32};
905 use rand::{rngs::StdRng, SeedableRng};
906
907 const NAMESPACE: &[u8] = b"bls-threshold-signing-scheme";
908
909 type Scheme<V> = super::Scheme<ed25519::PublicKey, V>;
910 type Signature<V> = super::Signature<V>;
911
912 fn setup_signers<V: Variant>(n: u32, seed: u64) -> (Vec<Scheme<V>>, Scheme<V>) {
913 let mut rng = StdRng::seed_from_u64(seed);
914 let Fixture {
915 schemes, verifier, ..
916 } = fixture::<V, _>(&mut rng, NAMESPACE, n);
917
918 (schemes, verifier)
919 }
920
921 fn sample_proposal(epoch: Epoch, view: View, tag: u8) -> Proposal<Sha256Digest> {
922 Proposal::new(
923 Round::new(epoch, view),
924 view.previous().unwrap(),
925 Sha256::hash(&[tag]),
926 )
927 }
928
929 fn signer_shares_must_match_participant_indices<V: Variant>() {
930 let mut rng = test_rng();
931 let participants = ed25519_participants(&mut rng, 4);
932 let (polynomial, mut shares) =
933 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(4));
934 shares[0].index = Participant::new(999);
935 Scheme::<V>::signer(
936 NAMESPACE,
937 participants.keys().clone(),
938 polynomial,
939 shares[0].clone(),
940 );
941 }
942
943 #[test]
944 #[should_panic(expected = "share index must match participant indices")]
945 fn test_signer_shares_must_match_participant_indices_min_pk() {
946 signer_shares_must_match_participant_indices::<MinPk>();
947 }
948
949 #[test]
950 #[should_panic(expected = "share index must match participant indices")]
951 fn test_signer_shares_must_match_participant_indices_min_sig() {
952 signer_shares_must_match_participant_indices::<MinSig>();
953 }
954 fn scheme_polynomial_threshold_must_equal_quorum<V: Variant>() {
955 let mut rng = test_rng();
956 let participants = ed25519_participants(&mut rng, 5);
957 let (polynomial, shares) =
958 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(4));
959 Scheme::<V>::signer(
960 NAMESPACE,
961 participants.keys().clone(),
962 polynomial,
963 shares[0].clone(),
964 );
965 }
966
967 #[test]
968 #[should_panic(expected = "polynomial total must equal participant len")]
969 fn test_scheme_polynomial_threshold_must_equal_quorum_min_pk() {
970 scheme_polynomial_threshold_must_equal_quorum::<MinPk>();
971 }
972
973 #[test]
974 #[should_panic(expected = "polynomial total must equal participant len")]
975 fn test_scheme_polynomial_threshold_must_equal_quorum_min_sig() {
976 scheme_polynomial_threshold_must_equal_quorum::<MinSig>();
977 }
978
979 fn verifier_polynomial_threshold_must_equal_quorum<V: Variant>() {
980 let mut rng = test_rng();
981 let participants = ed25519_participants(&mut rng, 5);
982 let (polynomial, _) =
983 dkg::deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(4));
984 Scheme::<V>::verifier(NAMESPACE, participants.keys().clone(), polynomial);
985 }
986
987 #[test]
988 #[should_panic(expected = "polynomial total must equal participant len")]
989 fn test_verifier_polynomial_threshold_must_equal_quorum_min_pk() {
990 verifier_polynomial_threshold_must_equal_quorum::<MinPk>();
991 }
992
993 #[test]
994 #[should_panic(expected = "polynomial total must equal participant len")]
995 fn test_verifier_polynomial_threshold_must_equal_quorum_min_sig() {
996 verifier_polynomial_threshold_must_equal_quorum::<MinSig>();
997 }
998
999 #[test]
1000 fn test_is_not_attributable() {
1001 assert!(!Scheme::<MinPk>::is_attributable());
1002 assert!(!Scheme::<MinSig>::is_attributable());
1003 }
1004
1005 #[test]
1006 fn test_is_batchable() {
1007 assert!(Scheme::<MinPk>::is_batchable());
1008 assert!(Scheme::<MinSig>::is_batchable());
1009 }
1010
1011 fn sign_vote_roundtrip_for_each_context<V: Variant>() {
1012 let (schemes, _) = setup_signers::<V>(4, 7);
1013 let scheme = &schemes[0];
1014 let mut rng = test_rng();
1015
1016 let proposal = sample_proposal(Epoch::new(0), View::new(2), 1);
1017 let notarize_vote = scheme
1018 .sign(Subject::Notarize {
1019 proposal: &proposal,
1020 })
1021 .unwrap();
1022 assert!(scheme.verify_attestation::<_, Sha256Digest>(
1023 &mut rng,
1024 Subject::Notarize {
1025 proposal: &proposal,
1026 },
1027 ¬arize_vote,
1028 &Sequential,
1029 ));
1030
1031 let nullify_vote = scheme
1032 .sign::<Sha256Digest>(Subject::Nullify {
1033 round: proposal.round,
1034 })
1035 .unwrap();
1036 assert!(scheme.verify_attestation::<_, Sha256Digest>(
1037 &mut rng,
1038 Subject::Nullify {
1039 round: proposal.round,
1040 },
1041 &nullify_vote,
1042 &Sequential,
1043 ));
1044
1045 let finalize_vote = scheme
1046 .sign(Subject::Finalize {
1047 proposal: &proposal,
1048 })
1049 .unwrap();
1050 assert!(scheme.verify_attestation::<_, Sha256Digest>(
1051 &mut rng,
1052 Subject::Finalize {
1053 proposal: &proposal,
1054 },
1055 &finalize_vote,
1056 &Sequential,
1057 ));
1058 }
1059
1060 #[test]
1061 fn test_sign_vote_roundtrip_for_each_context() {
1062 sign_vote_roundtrip_for_each_context::<MinPk>();
1063 sign_vote_roundtrip_for_each_context::<MinSig>();
1064 }
1065
1066 fn verifier_cannot_sign<V: Variant>() {
1067 let (_, verifier) = setup_signers::<V>(4, 11);
1068
1069 let proposal = sample_proposal(Epoch::new(0), View::new(3), 2);
1070 assert!(
1071 verifier
1072 .sign(Subject::Notarize {
1073 proposal: &proposal,
1074 })
1075 .is_none(),
1076 "verifier should not produce signatures"
1077 );
1078 }
1079
1080 #[test]
1081 fn test_verifier_cannot_sign() {
1082 verifier_cannot_sign::<MinPk>();
1083 verifier_cannot_sign::<MinSig>();
1084 }
1085
1086 fn verifier_accepts_votes<V: Variant>() {
1087 let (schemes, verifier) = setup_signers::<V>(4, 11);
1088 let proposal = sample_proposal(Epoch::new(0), View::new(3), 2);
1089 let vote = schemes[1]
1090 .sign(Subject::Notarize {
1091 proposal: &proposal,
1092 })
1093 .unwrap();
1094 assert!(verifier.verify_attestation::<_, Sha256Digest>(
1095 &mut test_rng(),
1096 Subject::Notarize {
1097 proposal: &proposal,
1098 },
1099 &vote,
1100 &Sequential,
1101 ));
1102 }
1103
1104 #[test]
1105 fn test_verifier_accepts_votes() {
1106 verifier_accepts_votes::<MinPk>();
1107 verifier_accepts_votes::<MinSig>();
1108 }
1109
1110 fn verify_votes_filters_bad_signers<V: Variant>() {
1111 let mut rng = test_rng();
1112 let (schemes, _) = setup_signers::<V>(5, 13);
1113 let quorum = N3f1::quorum(schemes.len()) as usize;
1114 let proposal = sample_proposal(Epoch::new(0), View::new(5), 3);
1115
1116 let mut votes: Vec<_> = schemes
1117 .iter()
1118 .take(quorum)
1119 .map(|scheme| {
1120 scheme
1121 .sign(Subject::Notarize {
1122 proposal: &proposal,
1123 })
1124 .unwrap()
1125 })
1126 .collect();
1127
1128 let verification = schemes[0].verify_attestations(
1129 &mut rng,
1130 Subject::Notarize {
1131 proposal: &proposal,
1132 },
1133 votes.clone(),
1134 &Sequential,
1135 );
1136 assert!(verification.invalid.is_empty());
1137 assert_eq!(verification.verified.len(), quorum);
1138
1139 votes[0].signer = Participant::new(999);
1140 let verification = schemes[0].verify_attestations(
1141 &mut rng,
1142 Subject::Notarize {
1143 proposal: &proposal,
1144 },
1145 votes,
1146 &Sequential,
1147 );
1148 assert_eq!(verification.invalid, vec![Participant::new(999)]);
1149 assert_eq!(verification.verified.len(), quorum - 1);
1150 }
1151
1152 #[test]
1153 fn test_verify_votes_filters_bad_signers() {
1154 verify_votes_filters_bad_signers::<MinPk>();
1155 verify_votes_filters_bad_signers::<MinSig>();
1156 }
1157
1158 fn assemble_certificate_requires_quorum<V: Variant>() {
1159 let (schemes, _) = setup_signers::<V>(4, 17);
1160 let quorum = N3f1::quorum(schemes.len()) as usize;
1161 let proposal = sample_proposal(Epoch::new(0), View::new(7), 4);
1162
1163 let votes: Vec<_> = schemes
1164 .iter()
1165 .take(quorum - 1)
1166 .map(|scheme| {
1167 scheme
1168 .sign(Subject::Notarize {
1169 proposal: &proposal,
1170 })
1171 .unwrap()
1172 })
1173 .collect();
1174
1175 assert!(schemes[0].assemble::<_, N3f1>(votes, &Sequential).is_none());
1176 }
1177
1178 #[test]
1179 fn test_assemble_certificate_requires_quorum() {
1180 assemble_certificate_requires_quorum::<MinPk>();
1181 assemble_certificate_requires_quorum::<MinSig>();
1182 }
1183
1184 fn verify_certificate<V: Variant>() {
1185 let (schemes, verifier) = setup_signers::<V>(4, 19);
1186 let quorum = N3f1::quorum(schemes.len()) as usize;
1187 let proposal = sample_proposal(Epoch::new(0), View::new(9), 5);
1188
1189 let votes: Vec<_> = schemes
1190 .iter()
1191 .take(quorum)
1192 .map(|scheme| {
1193 scheme
1194 .sign(Subject::Finalize {
1195 proposal: &proposal,
1196 })
1197 .unwrap()
1198 })
1199 .collect();
1200
1201 let certificate = schemes[0]
1202 .assemble::<_, N3f1>(votes, &Sequential)
1203 .expect("assemble certificate");
1204
1205 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1206 &mut test_rng(),
1207 Subject::Finalize {
1208 proposal: &proposal,
1209 },
1210 &certificate,
1211 &Sequential,
1212 ));
1213 }
1214
1215 #[test]
1216 fn test_verify_certificate() {
1217 verify_certificate::<MinPk>();
1218 verify_certificate::<MinSig>();
1219 }
1220
1221 fn verify_certificate_detects_corruption<V: Variant>() {
1222 let mut rng = test_rng();
1223 let (schemes, verifier) = setup_signers::<V>(4, 23);
1224 let quorum = N3f1::quorum(schemes.len()) as usize;
1225 let proposal = sample_proposal(Epoch::new(0), View::new(11), 6);
1226
1227 let votes: Vec<_> = schemes
1228 .iter()
1229 .take(quorum)
1230 .map(|scheme| {
1231 scheme
1232 .sign(Subject::Notarize {
1233 proposal: &proposal,
1234 })
1235 .unwrap()
1236 })
1237 .collect();
1238
1239 let certificate = schemes[0]
1240 .assemble::<_, N3f1>(votes, &Sequential)
1241 .expect("assemble certificate");
1242
1243 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1244 &mut rng,
1245 Subject::Notarize {
1246 proposal: &proposal,
1247 },
1248 &certificate,
1249 &Sequential,
1250 ));
1251
1252 let cert = certificate.get().unwrap();
1253 let corrupted: Certificate<V> = Signature {
1254 vote_signature: cert.seed_signature,
1255 seed_signature: cert.seed_signature,
1256 }
1257 .into();
1258 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1259 &mut rng,
1260 Subject::Notarize {
1261 proposal: &proposal,
1262 },
1263 &corrupted,
1264 &Sequential,
1265 ));
1266 }
1267
1268 #[test]
1269 fn test_verify_certificate_detects_corruption() {
1270 verify_certificate_detects_corruption::<MinPk>();
1271 verify_certificate_detects_corruption::<MinSig>();
1272 }
1273
1274 fn certificate_codec_roundtrip<V: Variant>() {
1275 let (schemes, _) = setup_signers::<V>(5, 29);
1276 let quorum = N3f1::quorum(schemes.len()) as usize;
1277 let proposal = sample_proposal(Epoch::new(0), View::new(13), 7);
1278
1279 let votes: Vec<_> = schemes
1280 .iter()
1281 .take(quorum)
1282 .map(|scheme| {
1283 scheme
1284 .sign(Subject::Notarize {
1285 proposal: &proposal,
1286 })
1287 .unwrap()
1288 })
1289 .collect();
1290
1291 let certificate = schemes[0]
1292 .assemble::<_, N3f1>(votes, &Sequential)
1293 .expect("assemble certificate");
1294
1295 let encoded = certificate.encode();
1296 let decoded = Certificate::<V>::decode_cfg(encoded, &()).expect("decode certificate");
1297 assert_eq!(decoded, certificate);
1298 }
1299
1300 #[test]
1301 fn test_certificate_codec_roundtrip() {
1302 certificate_codec_roundtrip::<MinPk>();
1303 certificate_codec_roundtrip::<MinSig>();
1304 }
1305
1306 fn seed_codec_roundtrip<V: Variant>() {
1307 let (schemes, _) = setup_signers::<V>(4, 5);
1308 let quorum = N3f1::quorum(schemes.len()) as usize;
1309 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1310
1311 let votes: Vec<_> = schemes
1312 .iter()
1313 .take(quorum)
1314 .map(|scheme| {
1315 scheme
1316 .sign(Subject::Finalize {
1317 proposal: &proposal,
1318 })
1319 .unwrap()
1320 })
1321 .collect();
1322
1323 let certificate = schemes[0]
1324 .assemble::<_, N3f1>(votes, &Sequential)
1325 .expect("assemble certificate");
1326 let cert = certificate.get().unwrap();
1327
1328 let seed = Seed::new(proposal.round, cert.seed_signature);
1329
1330 let encoded = seed.encode();
1331 let decoded = Seed::<V>::decode_cfg(encoded, &()).expect("decode seed");
1332 assert_eq!(decoded, seed);
1333 }
1334
1335 #[test]
1336 fn test_seed_codec_roundtrip() {
1337 seed_codec_roundtrip::<MinPk>();
1338 seed_codec_roundtrip::<MinSig>();
1339 }
1340
1341 fn seed_verify<V: Variant>() {
1342 let (schemes, _) = setup_signers::<V>(4, 5);
1343 let quorum = N3f1::quorum(schemes.len()) as usize;
1344 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1345
1346 let votes: Vec<_> = schemes
1347 .iter()
1348 .take(quorum)
1349 .map(|scheme| {
1350 scheme
1351 .sign(Subject::Finalize {
1352 proposal: &proposal,
1353 })
1354 .unwrap()
1355 })
1356 .collect();
1357
1358 let certificate = schemes[0]
1359 .assemble::<_, N3f1>(votes, &Sequential)
1360 .expect("assemble certificate");
1361 let cert = certificate.get().unwrap();
1362
1363 let seed = Seed::new(proposal.round, cert.seed_signature);
1364
1365 assert!(seed.verify(&schemes[0]));
1366
1367 let invalid_seed = Seed::new(
1369 Round::new(proposal.epoch(), proposal.view().next()),
1370 cert.seed_signature,
1371 );
1372
1373 assert!(!invalid_seed.verify(&schemes[0]));
1374 }
1375
1376 #[test]
1377 fn test_seed_verify() {
1378 seed_verify::<MinPk>();
1379 seed_verify::<MinSig>();
1380 }
1381
1382 fn seedable<V: Variant>() {
1383 let (schemes, _) = setup_signers::<V>(4, 5);
1384 let quorum = N3f1::quorum(schemes.len()) as usize;
1385 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1386
1387 let notarizes: Vec<_> = schemes
1388 .iter()
1389 .take(quorum)
1390 .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
1391 .collect();
1392
1393 let notarization =
1394 Notarization::from_notarizes(&schemes[0], ¬arizes, &Sequential).unwrap();
1395
1396 let finalizes: Vec<_> = schemes
1397 .iter()
1398 .take(quorum)
1399 .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap())
1400 .collect();
1401
1402 let finalization =
1403 Finalization::from_finalizes(&schemes[0], &finalizes, &Sequential).unwrap();
1404
1405 assert_eq!(notarization.seed(), finalization.seed());
1406 assert!(notarization.seed().verify(&schemes[0]));
1407 }
1408
1409 #[test]
1410 fn test_seedable() {
1411 seedable::<MinPk>();
1412 seedable::<MinSig>();
1413 }
1414
1415 fn scheme_clone_and_verifier<V: Variant>() {
1416 let (schemes, verifier) = setup_signers::<V>(4, 31);
1417 let signer = schemes[0].clone();
1418 let proposal = sample_proposal(Epoch::new(0), View::new(21), 9);
1419
1420 assert!(
1421 signer
1422 .sign(Subject::Notarize {
1423 proposal: &proposal,
1424 })
1425 .is_some(),
1426 "signer should produce votes"
1427 );
1428
1429 assert!(
1430 verifier
1431 .sign(Subject::Notarize {
1432 proposal: &proposal,
1433 })
1434 .is_none(),
1435 "verifier should not produce votes"
1436 );
1437 }
1438
1439 #[test]
1440 fn test_scheme_clone_and_verifier() {
1441 scheme_clone_and_verifier::<MinPk>();
1442 scheme_clone_and_verifier::<MinSig>();
1443 }
1444
1445 fn certificate_verifier_accepts_certificates<V: Variant>() {
1446 let (schemes, _) = setup_signers::<V>(4, 37);
1447 let quorum = N3f1::quorum(schemes.len()) as usize;
1448 let proposal = sample_proposal(Epoch::new(0), View::new(15), 8);
1449
1450 let votes: Vec<_> = schemes
1451 .iter()
1452 .take(quorum)
1453 .map(|scheme| {
1454 scheme
1455 .sign(Subject::Finalize {
1456 proposal: &proposal,
1457 })
1458 .unwrap()
1459 })
1460 .collect();
1461
1462 let certificate = schemes[0]
1463 .assemble::<_, N3f1>(votes, &Sequential)
1464 .expect("assemble certificate");
1465
1466 let certificate_verifier =
1467 Scheme::<V>::certificate_verifier(NAMESPACE, *schemes[0].identity());
1468 assert!(
1469 certificate_verifier
1470 .sign(Subject::Finalize {
1471 proposal: &proposal,
1472 })
1473 .is_none(),
1474 "certificate verifier should not produce votes"
1475 );
1476 assert!(
1477 certificate_verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1478 &mut test_rng(),
1479 Subject::Finalize {
1480 proposal: &proposal,
1481 },
1482 &certificate,
1483 &Sequential,
1484 )
1485 );
1486 }
1487
1488 #[test]
1489 fn test_certificate_verifier_accepts_certificates() {
1490 certificate_verifier_accepts_certificates::<MinPk>();
1491 certificate_verifier_accepts_certificates::<MinSig>();
1492 }
1493
1494 fn certificate_verifier_panics_on_vote<V: Variant>() {
1495 let (schemes, _) = setup_signers::<V>(4, 37);
1496 let certificate_verifier =
1497 Scheme::<V>::certificate_verifier(NAMESPACE, *schemes[0].identity());
1498 let proposal = sample_proposal(Epoch::new(0), View::new(15), 8);
1499 let vote = schemes[1]
1500 .sign(Subject::Finalize {
1501 proposal: &proposal,
1502 })
1503 .unwrap();
1504
1505 certificate_verifier.verify_attestation::<_, Sha256Digest>(
1506 &mut test_rng(),
1507 Subject::Finalize {
1508 proposal: &proposal,
1509 },
1510 &vote,
1511 &Sequential,
1512 );
1513 }
1514
1515 #[test]
1516 #[should_panic(expected = "can only be called for signer and verifier")]
1517 fn test_certificate_verifier_panics_on_vote_min_pk() {
1518 certificate_verifier_panics_on_vote::<MinPk>();
1519 }
1520
1521 #[test]
1522 #[should_panic(expected = "can only be called for signer and verifier")]
1523 fn test_certificate_verifier_panics_on_vote_min_sig() {
1524 certificate_verifier_panics_on_vote::<MinSig>();
1525 }
1526
1527 fn verify_certificate_returns_seed_randomness<V: Variant>() {
1528 let (schemes, _) = setup_signers::<V>(4, 43);
1529 let quorum = N3f1::quorum(schemes.len()) as usize;
1530 let proposal = sample_proposal(Epoch::new(0), View::new(19), 10);
1531
1532 let votes: Vec<_> = schemes
1533 .iter()
1534 .take(quorum)
1535 .map(|scheme| {
1536 scheme
1537 .sign(Subject::Notarize {
1538 proposal: &proposal,
1539 })
1540 .unwrap()
1541 })
1542 .collect();
1543
1544 let certificate = schemes[0]
1545 .assemble::<_, N3f1>(votes, &Sequential)
1546 .expect("assemble certificate");
1547 let cert = certificate.get().unwrap();
1548
1549 let seed = Seed::<V>::new(proposal.round, cert.seed_signature);
1550 assert_eq!(seed.signature, cert.seed_signature);
1551 }
1552
1553 #[test]
1554 fn test_verify_certificate_returns_seed_randomness() {
1555 verify_certificate_returns_seed_randomness::<MinPk>();
1556 verify_certificate_returns_seed_randomness::<MinSig>();
1557 }
1558
1559 fn certificate_decode_rejects_length_mismatch<V: Variant>() {
1560 let (schemes, _) = setup_signers::<V>(4, 47);
1561 let quorum = N3f1::quorum(schemes.len()) as usize;
1562 let proposal = sample_proposal(Epoch::new(0), View::new(21), 11);
1563
1564 let votes: Vec<_> = schemes
1565 .iter()
1566 .take(quorum)
1567 .map(|scheme| {
1568 scheme
1569 .sign::<Sha256Digest>(Subject::Nullify {
1570 round: proposal.round,
1571 })
1572 .unwrap()
1573 })
1574 .collect();
1575
1576 let certificate = schemes[0]
1577 .assemble::<_, N3f1>(votes, &Sequential)
1578 .expect("assemble certificate");
1579
1580 let mut encoded = certificate.encode();
1581 let truncated = encoded.split_to(encoded.len() - 1);
1582 assert!(Signature::<V>::decode_cfg(truncated, &()).is_err());
1583 }
1584
1585 #[test]
1586 fn test_certificate_decode_rejects_length_mismatch() {
1587 certificate_decode_rejects_length_mismatch::<MinPk>();
1588 certificate_decode_rejects_length_mismatch::<MinSig>();
1589 }
1590
1591 fn sign_vote_partial_matches_share<V: Variant>() {
1592 let (schemes, _) = setup_signers::<V>(4, 53);
1593 let scheme = &schemes[0];
1594 let share = scheme.share().expect("has share");
1595
1596 let proposal = sample_proposal(Epoch::new(0), View::new(23), 12);
1597 let vote = scheme
1598 .sign(Subject::Notarize {
1599 proposal: &proposal,
1600 })
1601 .unwrap();
1602
1603 let notarize_namespace = notarize_namespace(NAMESPACE);
1604 let notarize_message = proposal.encode();
1605 let expected_message = threshold::sign_message::<V>(
1606 share,
1607 notarize_namespace.as_ref(),
1608 notarize_message.as_ref(),
1609 )
1610 .value;
1611
1612 let seed_namespace = seed_namespace(NAMESPACE);
1613 let seed_message = proposal.round.encode();
1614 let expected_seed =
1615 threshold::sign_message::<V>(share, seed_namespace.as_ref(), seed_message.as_ref())
1616 .value;
1617
1618 assert_eq!(vote.signer, share.index);
1619 let sig = vote.signature.get().unwrap();
1620 assert_eq!(sig.vote_signature, expected_message);
1621 assert_eq!(sig.seed_signature, expected_seed);
1622 }
1623
1624 #[test]
1625 fn test_sign_vote_partial_matches_share() {
1626 sign_vote_partial_matches_share::<MinPk>();
1627 sign_vote_partial_matches_share::<MinSig>();
1628 }
1629
1630 fn verify_certificate_detects_seed_corruption<V: Variant>() {
1631 let mut rng = test_rng();
1632 let (schemes, verifier) = setup_signers::<V>(4, 59);
1633 let quorum = N3f1::quorum(schemes.len()) as usize;
1634 let proposal = sample_proposal(Epoch::new(0), View::new(25), 13);
1635
1636 let votes: Vec<_> = schemes
1637 .iter()
1638 .take(quorum)
1639 .map(|scheme| {
1640 scheme
1641 .sign::<Sha256Digest>(Subject::Nullify {
1642 round: proposal.round,
1643 })
1644 .unwrap()
1645 })
1646 .collect();
1647
1648 let certificate = schemes[0]
1649 .assemble::<_, N3f1>(votes, &Sequential)
1650 .expect("assemble certificate");
1651
1652 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1653 &mut rng,
1654 Subject::Nullify {
1655 round: proposal.round,
1656 },
1657 &certificate,
1658 &Sequential,
1659 ));
1660
1661 let cert = certificate.get().unwrap();
1662 let corrupted: Certificate<V> = Signature {
1663 vote_signature: cert.vote_signature,
1664 seed_signature: cert.vote_signature,
1665 }
1666 .into();
1667 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1668 &mut rng,
1669 Subject::Nullify {
1670 round: proposal.round,
1671 },
1672 &corrupted,
1673 &Sequential,
1674 ));
1675 }
1676
1677 #[test]
1678 fn test_verify_certificate_detects_seed_corruption() {
1679 verify_certificate_detects_seed_corruption::<MinPk>();
1680 verify_certificate_detects_seed_corruption::<MinSig>();
1681 }
1682
1683 fn encrypt_decrypt<V: Variant>() {
1684 let mut rng = test_rng();
1685 let (schemes, verifier) = setup_signers::<V>(4, 61);
1686 let quorum = N3f1::quorum(schemes.len()) as usize;
1687
1688 let message = b"Secret message for future view10";
1690
1691 let target = Round::new(Epoch::new(333), View::new(10));
1693
1694 let ciphertext = schemes[0].encrypt(&mut rng, target, *message);
1696
1697 let ciphertext_verifier = verifier.encrypt(&mut rng, target, *message);
1699
1700 let proposal = sample_proposal(target.epoch(), target.view(), 14);
1702 let notarizes: Vec<_> = schemes
1703 .iter()
1704 .take(quorum)
1705 .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
1706 .collect();
1707
1708 let notarization =
1709 Notarization::from_notarizes(&schemes[0], ¬arizes, &Sequential).unwrap();
1710
1711 let seed = notarization.seed();
1713 let decrypted = seed.decrypt(&ciphertext).unwrap();
1714 assert_eq!(message, decrypted.as_ref());
1715
1716 let decrypted_verifier = seed.decrypt(&ciphertext_verifier).unwrap();
1717 assert_eq!(message, decrypted_verifier.as_ref());
1718 }
1719
1720 #[test]
1721 fn test_encrypt_decrypt() {
1722 encrypt_decrypt::<MinPk>();
1723 encrypt_decrypt::<MinSig>();
1724 }
1725
1726 fn verify_attestation_rejects_malleability<V: Variant>() {
1727 let mut rng = test_rng();
1728 let (schemes, _) = setup_signers::<V>(4, 67);
1729 let proposal = sample_proposal(Epoch::new(0), View::new(27), 14);
1730
1731 let attestation = schemes[0]
1732 .sign(Subject::Notarize {
1733 proposal: &proposal,
1734 })
1735 .unwrap();
1736
1737 assert!(schemes[0].verify_attestation::<_, Sha256Digest>(
1738 &mut rng,
1739 Subject::Notarize {
1740 proposal: &proposal,
1741 },
1742 &attestation,
1743 &Sequential,
1744 ));
1745
1746 let random_scalar = Scalar::random(&mut rng);
1747 let delta = V::Signature::generator() * &random_scalar;
1748 let att_sig = attestation.signature.get().unwrap();
1749 let forged_attestation: Attestation<Scheme<V>> = Attestation {
1750 signer: attestation.signer,
1751 signature: Signature {
1752 vote_signature: att_sig.vote_signature - &delta,
1753 seed_signature: att_sig.seed_signature + &delta,
1754 }
1755 .into(),
1756 };
1757
1758 let forged_sig = forged_attestation.signature.get().unwrap();
1759 let forged_sum = forged_sig.vote_signature + &forged_sig.seed_signature;
1760 let valid_sum = att_sig.vote_signature + &att_sig.seed_signature;
1761 assert_eq!(forged_sum, valid_sum, "signature sums should be equal");
1762
1763 assert!(
1764 !schemes[0].verify_attestation::<_, Sha256Digest>(
1765 &mut rng,
1766 Subject::Notarize {
1767 proposal: &proposal,
1768 },
1769 &forged_attestation,
1770 &Sequential,
1771 ),
1772 "forged attestation should be rejected"
1773 );
1774 }
1775
1776 #[test]
1777 fn test_verify_attestation_rejects_malleability() {
1778 verify_attestation_rejects_malleability::<MinPk>();
1779 verify_attestation_rejects_malleability::<MinSig>();
1780 }
1781
1782 fn verify_attestations_rejects_malleability<V: Variant>() {
1783 let mut rng = test_rng();
1784 let (schemes, _) = setup_signers::<V>(4, 71);
1785 let proposal = sample_proposal(Epoch::new(0), View::new(29), 15);
1786
1787 let attestation1 = schemes[0]
1788 .sign(Subject::Notarize {
1789 proposal: &proposal,
1790 })
1791 .unwrap();
1792 let attestation2 = schemes[1]
1793 .sign(Subject::Notarize {
1794 proposal: &proposal,
1795 })
1796 .unwrap();
1797
1798 let verification = schemes[0].verify_attestations(
1799 &mut rng,
1800 Subject::Notarize {
1801 proposal: &proposal,
1802 },
1803 vec![attestation1.clone(), attestation2.clone()],
1804 &Sequential,
1805 );
1806 assert!(verification.invalid.is_empty());
1807 assert_eq!(verification.verified.len(), 2);
1808
1809 let random_scalar = Scalar::random(&mut rng);
1810 let delta = V::Signature::generator() * &random_scalar;
1811 let att1_sig = attestation1.signature.get().unwrap();
1812 let att2_sig = attestation2.signature.get().unwrap();
1813 let forged_attestation1: Attestation<Scheme<V>> = Attestation {
1814 signer: attestation1.signer,
1815 signature: Signature {
1816 vote_signature: att1_sig.vote_signature - &delta,
1817 seed_signature: att1_sig.seed_signature,
1818 }
1819 .into(),
1820 };
1821 let forged_attestation2: Attestation<Scheme<V>> = Attestation {
1822 signer: attestation2.signer,
1823 signature: Signature {
1824 vote_signature: att2_sig.vote_signature + &delta,
1825 seed_signature: att2_sig.seed_signature,
1826 }
1827 .into(),
1828 };
1829
1830 let forged1_sig = forged_attestation1.signature.get().unwrap();
1831 let forged2_sig = forged_attestation2.signature.get().unwrap();
1832 let forged_vote_sum = forged1_sig.vote_signature + &forged2_sig.vote_signature;
1833 let valid_vote_sum = att1_sig.vote_signature + &att2_sig.vote_signature;
1834 assert_eq!(
1835 forged_vote_sum, valid_vote_sum,
1836 "vote signature sums should be equal"
1837 );
1838
1839 let verification = schemes[0].verify_attestations(
1840 &mut rng,
1841 Subject::Notarize {
1842 proposal: &proposal,
1843 },
1844 vec![forged_attestation1, forged_attestation2],
1845 &Sequential,
1846 );
1847 assert!(
1848 !verification.invalid.is_empty(),
1849 "forged attestations should be detected"
1850 );
1851 }
1852
1853 #[test]
1854 fn test_verify_attestations_rejects_malleability() {
1855 verify_attestations_rejects_malleability::<MinPk>();
1856 verify_attestations_rejects_malleability::<MinSig>();
1857 }
1858
1859 fn verify_certificate_rejects_malleability<V: Variant>() {
1860 let mut rng = test_rng();
1861 let (schemes, verifier) = setup_signers::<V>(4, 73);
1862 let quorum = N3f1::quorum(schemes.len()) as usize;
1863 let proposal = sample_proposal(Epoch::new(0), View::new(31), 16);
1864
1865 let votes: Vec<_> = schemes
1866 .iter()
1867 .take(quorum)
1868 .map(|scheme| {
1869 scheme
1870 .sign(Subject::Notarize {
1871 proposal: &proposal,
1872 })
1873 .unwrap()
1874 })
1875 .collect();
1876
1877 let certificate = schemes[0]
1878 .assemble::<_, N3f1>(votes, &Sequential)
1879 .expect("assemble certificate");
1880
1881 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1882 &mut rng,
1883 Subject::Notarize {
1884 proposal: &proposal,
1885 },
1886 &certificate,
1887 &Sequential,
1888 ));
1889
1890 let cert = certificate.get().unwrap();
1891 let random_scalar = Scalar::random(&mut rng);
1892 let delta = V::Signature::generator() * &random_scalar;
1893 let forged_certificate: Certificate<V> = Signature {
1894 vote_signature: cert.vote_signature - &delta,
1895 seed_signature: cert.seed_signature + &delta,
1896 }
1897 .into();
1898
1899 let forged_cert = forged_certificate.get().unwrap();
1900 let forged_sum = forged_cert.vote_signature + &forged_cert.seed_signature;
1901 let valid_sum = cert.vote_signature + &cert.seed_signature;
1902 assert_eq!(forged_sum, valid_sum, "signature sums should be equal");
1903
1904 assert!(
1905 !verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1906 &mut rng,
1907 Subject::Notarize {
1908 proposal: &proposal,
1909 },
1910 &forged_certificate,
1911 &Sequential,
1912 ),
1913 "forged certificate should be rejected"
1914 );
1915 }
1916
1917 #[test]
1918 fn test_verify_certificate_rejects_malleability() {
1919 verify_certificate_rejects_malleability::<MinPk>();
1920 verify_certificate_rejects_malleability::<MinSig>();
1921 }
1922
1923 fn verify_certificates_rejects_malleability<V: Variant>() {
1924 let mut rng = test_rng();
1925 let (schemes, verifier) = setup_signers::<V>(4, 79);
1926 let quorum = N3f1::quorum(schemes.len()) as usize;
1927 let proposal1 = sample_proposal(Epoch::new(0), View::new(33), 17);
1928 let proposal2 = sample_proposal(Epoch::new(0), View::new(34), 18);
1929
1930 let votes1: Vec<_> = schemes
1931 .iter()
1932 .take(quorum)
1933 .map(|scheme| {
1934 scheme
1935 .sign(Subject::Notarize {
1936 proposal: &proposal1,
1937 })
1938 .unwrap()
1939 })
1940 .collect();
1941 let votes2: Vec<_> = schemes
1942 .iter()
1943 .take(quorum)
1944 .map(|scheme| {
1945 scheme
1946 .sign(Subject::Notarize {
1947 proposal: &proposal2,
1948 })
1949 .unwrap()
1950 })
1951 .collect();
1952
1953 let certificate1 = schemes[0]
1954 .assemble::<_, N3f1>(votes1, &Sequential)
1955 .expect("assemble certificate1");
1956 let certificate2 = schemes[0]
1957 .assemble::<_, N3f1>(votes2, &Sequential)
1958 .expect("assemble certificate2");
1959
1960 assert!(verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1961 &mut rng,
1962 [
1963 (
1964 Subject::Notarize {
1965 proposal: &proposal1,
1966 },
1967 &certificate1
1968 ),
1969 (
1970 Subject::Notarize {
1971 proposal: &proposal2,
1972 },
1973 &certificate2
1974 ),
1975 ]
1976 .into_iter(),
1977 &Sequential,
1978 ));
1979
1980 let cert1 = certificate1.get().unwrap();
1981 let cert2 = certificate2.get().unwrap();
1982 let random_scalar = Scalar::random(&mut rng);
1983 let delta = V::Signature::generator() * &random_scalar;
1984 let forged_certificate1: Certificate<V> = Signature {
1985 vote_signature: cert1.vote_signature - &delta,
1986 seed_signature: cert1.seed_signature,
1987 }
1988 .into();
1989 let forged_certificate2: Certificate<V> = Signature {
1990 vote_signature: cert2.vote_signature + &delta,
1991 seed_signature: cert2.seed_signature,
1992 }
1993 .into();
1994
1995 let forged1 = forged_certificate1.get().unwrap();
1996 let forged2 = forged_certificate2.get().unwrap();
1997 let forged_vote_sum = forged1.vote_signature + &forged2.vote_signature;
1998 let valid_vote_sum = cert1.vote_signature + &cert2.vote_signature;
1999 assert_eq!(
2000 forged_vote_sum, valid_vote_sum,
2001 "vote signature sums should be equal"
2002 );
2003
2004 assert!(
2005 !verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
2006 &mut rng,
2007 [
2008 (
2009 Subject::Notarize {
2010 proposal: &proposal1,
2011 },
2012 &forged_certificate1
2013 ),
2014 (
2015 Subject::Notarize {
2016 proposal: &proposal2,
2017 },
2018 &forged_certificate2
2019 ),
2020 ]
2021 .into_iter(),
2022 &Sequential,
2023 ),
2024 "forged certificates should be rejected"
2025 );
2026 }
2027
2028 #[test]
2029 fn test_verify_certificates_rejects_malleability() {
2030 verify_certificates_rejects_malleability::<MinPk>();
2031 verify_certificates_rejects_malleability::<MinSig>();
2032 }
2033
2034 fn assemble_notarization_certificate<V: Variant>(
2035 schemes: &[Scheme<V>],
2036 proposal: &Proposal<Sha256Digest>,
2037 ) -> Certificate<V> {
2038 let quorum = N3f1::quorum(schemes.len()) as usize;
2039 let votes: Vec<_> = schemes
2040 .iter()
2041 .take(quorum)
2042 .map(|scheme| scheme.sign(Subject::Notarize { proposal }).unwrap())
2043 .collect();
2044
2045 schemes[0]
2046 .assemble::<_, N3f1>(votes, &Sequential)
2047 .expect("assemble notarization certificate")
2048 }
2049
2050 fn assemble_finalization_certificate<V: Variant>(
2051 schemes: &[Scheme<V>],
2052 proposal: &Proposal<Sha256Digest>,
2053 ) -> Certificate<V> {
2054 let quorum = N3f1::quorum(schemes.len()) as usize;
2055 let votes: Vec<_> = schemes
2056 .iter()
2057 .skip(schemes.len() - quorum)
2058 .map(|scheme| scheme.sign(Subject::Finalize { proposal }).unwrap())
2059 .collect();
2060
2061 schemes[0]
2062 .assemble::<_, N3f1>(votes, &Sequential)
2063 .expect("assemble finalization certificate")
2064 }
2065
2066 fn verify_certificates_accepts_shared_round_seed<V: Variant>() {
2067 let mut rng = test_rng();
2068 let (schemes, verifier) = setup_signers::<V>(4, 81);
2069 let proposal = sample_proposal(Epoch::new(1), View::new(35), 19);
2070 let notarization_certificate = assemble_notarization_certificate(&schemes, &proposal);
2071 let finalization_certificate = assemble_finalization_certificate(&schemes, &proposal);
2072
2073 assert!(verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
2074 &mut rng,
2075 [
2076 (
2077 Subject::Notarize {
2078 proposal: &proposal,
2079 },
2080 ¬arization_certificate,
2081 ),
2082 (
2083 Subject::Finalize {
2084 proposal: &proposal,
2085 },
2086 &finalization_certificate,
2087 ),
2088 ]
2089 .into_iter(),
2090 &Sequential,
2091 ));
2092 }
2093
2094 #[test]
2095 fn test_verify_certificates_accepts_shared_round_seed() {
2096 verify_certificates_accepts_shared_round_seed::<MinPk>();
2097 verify_certificates_accepts_shared_round_seed::<MinSig>();
2098 }
2099
2100 fn verify_certificates_rejects_cross_epoch_seed_replay<V: Variant>() {
2101 let mut rng = test_rng();
2102 let (schemes, verifier) = setup_signers::<V>(4, 83);
2103 let view = View::new(35);
2104 let proposal1 = sample_proposal(Epoch::new(1), view, 19);
2105 let proposal2 = sample_proposal(Epoch::new(2), view, 20);
2106 let certificate1 = assemble_notarization_certificate(&schemes, &proposal1);
2107 let certificate2 = assemble_notarization_certificate(&schemes, &proposal2);
2108
2109 assert!(verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
2110 &mut rng,
2111 [
2112 (
2113 Subject::Notarize {
2114 proposal: &proposal1,
2115 },
2116 &certificate1,
2117 ),
2118 (
2119 Subject::Notarize {
2120 proposal: &proposal2,
2121 },
2122 &certificate2,
2123 ),
2124 ]
2125 .into_iter(),
2126 &Sequential,
2127 ));
2128
2129 let cert1 = certificate1.get().unwrap();
2130 let cert2 = certificate2.get().unwrap();
2131 let forged_certificate2: Certificate<V> = Signature {
2132 vote_signature: cert2.vote_signature,
2133 seed_signature: cert1.seed_signature,
2134 }
2135 .into();
2136
2137 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
2138 &mut rng,
2139 Subject::Notarize {
2140 proposal: &proposal2,
2141 },
2142 &forged_certificate2,
2143 &Sequential,
2144 ));
2145
2146 let batch = [
2147 (
2148 Subject::Notarize {
2149 proposal: &proposal1,
2150 },
2151 &certificate1,
2152 ),
2153 (
2154 Subject::Notarize {
2155 proposal: &proposal2,
2156 },
2157 &forged_certificate2,
2158 ),
2159 ];
2160
2161 assert!(!verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
2162 &mut rng,
2163 batch.iter().copied(),
2164 &Sequential,
2165 ));
2166 assert_eq!(
2167 verifier.verify_certificates_bisect::<_, Sha256Digest, N3f1>(
2168 &mut rng,
2169 &batch,
2170 &Sequential,
2171 ),
2172 vec![true, false],
2173 );
2174 }
2175
2176 #[test]
2177 fn test_verify_certificates_rejects_cross_epoch_seed_replay() {
2178 verify_certificates_rejects_cross_epoch_seed_replay::<MinPk>();
2179 verify_certificates_rejects_cross_epoch_seed_replay::<MinSig>();
2180 }
2181
2182 #[cfg(feature = "arbitrary")]
2183 mod conformance {
2184 use super::*;
2185 use commonware_codec::conformance::CodecConformance;
2186
2187 commonware_conformance::conformance_tests! {
2188 CodecConformance<Signature<MinSig>>,
2189 CodecConformance<Certificate<MinSig>>,
2190 CodecConformance<Seed<MinSig>>,
2191 }
2192 }
2193}