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