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 if let Some(previous) = seeds.get(&context.view()) {
841 if *previous != cert.seed_signature {
842 return false;
843 }
844 } else {
845 let seed_message = seed_message_from_subject(&context);
846 entries.push((&namespace.seed, seed_message, cert.seed_signature));
847 seeds.insert(context.view(), 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::{self, deal_anonymous},
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 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, _) = deal_anonymous::<V, N3f1>(&mut rng, Default::default(), NZU32!(4));
980 Scheme::<V>::verifier(NAMESPACE, participants.keys().clone(), polynomial);
981 }
982
983 #[test]
984 #[should_panic(expected = "polynomial total must equal participant len")]
985 fn test_verifier_polynomial_threshold_must_equal_quorum_min_pk() {
986 verifier_polynomial_threshold_must_equal_quorum::<MinPk>();
987 }
988
989 #[test]
990 #[should_panic(expected = "polynomial total must equal participant len")]
991 fn test_verifier_polynomial_threshold_must_equal_quorum_min_sig() {
992 verifier_polynomial_threshold_must_equal_quorum::<MinSig>();
993 }
994
995 #[test]
996 fn test_is_not_attributable() {
997 assert!(!Scheme::<MinPk>::is_attributable());
998 assert!(!Scheme::<MinSig>::is_attributable());
999 }
1000
1001 #[test]
1002 fn test_is_batchable() {
1003 assert!(Scheme::<MinPk>::is_batchable());
1004 assert!(Scheme::<MinSig>::is_batchable());
1005 }
1006
1007 fn sign_vote_roundtrip_for_each_context<V: Variant>() {
1008 let (schemes, _) = setup_signers::<V>(4, 7);
1009 let scheme = &schemes[0];
1010 let mut rng = test_rng();
1011
1012 let proposal = sample_proposal(Epoch::new(0), View::new(2), 1);
1013 let notarize_vote = scheme
1014 .sign(Subject::Notarize {
1015 proposal: &proposal,
1016 })
1017 .unwrap();
1018 assert!(scheme.verify_attestation::<_, Sha256Digest>(
1019 &mut rng,
1020 Subject::Notarize {
1021 proposal: &proposal,
1022 },
1023 ¬arize_vote,
1024 &Sequential,
1025 ));
1026
1027 let nullify_vote = scheme
1028 .sign::<Sha256Digest>(Subject::Nullify {
1029 round: proposal.round,
1030 })
1031 .unwrap();
1032 assert!(scheme.verify_attestation::<_, Sha256Digest>(
1033 &mut rng,
1034 Subject::Nullify {
1035 round: proposal.round,
1036 },
1037 &nullify_vote,
1038 &Sequential,
1039 ));
1040
1041 let finalize_vote = scheme
1042 .sign(Subject::Finalize {
1043 proposal: &proposal,
1044 })
1045 .unwrap();
1046 assert!(scheme.verify_attestation::<_, Sha256Digest>(
1047 &mut rng,
1048 Subject::Finalize {
1049 proposal: &proposal,
1050 },
1051 &finalize_vote,
1052 &Sequential,
1053 ));
1054 }
1055
1056 #[test]
1057 fn test_sign_vote_roundtrip_for_each_context() {
1058 sign_vote_roundtrip_for_each_context::<MinPk>();
1059 sign_vote_roundtrip_for_each_context::<MinSig>();
1060 }
1061
1062 fn verifier_cannot_sign<V: Variant>() {
1063 let (_, verifier) = setup_signers::<V>(4, 11);
1064
1065 let proposal = sample_proposal(Epoch::new(0), View::new(3), 2);
1066 assert!(
1067 verifier
1068 .sign(Subject::Notarize {
1069 proposal: &proposal,
1070 })
1071 .is_none(),
1072 "verifier should not produce signatures"
1073 );
1074 }
1075
1076 #[test]
1077 fn test_verifier_cannot_sign() {
1078 verifier_cannot_sign::<MinPk>();
1079 verifier_cannot_sign::<MinSig>();
1080 }
1081
1082 fn verifier_accepts_votes<V: Variant>() {
1083 let (schemes, verifier) = setup_signers::<V>(4, 11);
1084 let proposal = sample_proposal(Epoch::new(0), View::new(3), 2);
1085 let vote = schemes[1]
1086 .sign(Subject::Notarize {
1087 proposal: &proposal,
1088 })
1089 .unwrap();
1090 assert!(verifier.verify_attestation::<_, Sha256Digest>(
1091 &mut test_rng(),
1092 Subject::Notarize {
1093 proposal: &proposal,
1094 },
1095 &vote,
1096 &Sequential,
1097 ));
1098 }
1099
1100 #[test]
1101 fn test_verifier_accepts_votes() {
1102 verifier_accepts_votes::<MinPk>();
1103 verifier_accepts_votes::<MinSig>();
1104 }
1105
1106 fn verify_votes_filters_bad_signers<V: Variant>() {
1107 let mut rng = test_rng();
1108 let (schemes, _) = setup_signers::<V>(5, 13);
1109 let quorum = N3f1::quorum(schemes.len()) as usize;
1110 let proposal = sample_proposal(Epoch::new(0), View::new(5), 3);
1111
1112 let mut votes: Vec<_> = schemes
1113 .iter()
1114 .take(quorum)
1115 .map(|scheme| {
1116 scheme
1117 .sign(Subject::Notarize {
1118 proposal: &proposal,
1119 })
1120 .unwrap()
1121 })
1122 .collect();
1123
1124 let verification = schemes[0].verify_attestations(
1125 &mut rng,
1126 Subject::Notarize {
1127 proposal: &proposal,
1128 },
1129 votes.clone(),
1130 &Sequential,
1131 );
1132 assert!(verification.invalid.is_empty());
1133 assert_eq!(verification.verified.len(), quorum);
1134
1135 votes[0].signer = Participant::new(999);
1136 let verification = schemes[0].verify_attestations(
1137 &mut rng,
1138 Subject::Notarize {
1139 proposal: &proposal,
1140 },
1141 votes,
1142 &Sequential,
1143 );
1144 assert_eq!(verification.invalid, vec![Participant::new(999)]);
1145 assert_eq!(verification.verified.len(), quorum - 1);
1146 }
1147
1148 #[test]
1149 fn test_verify_votes_filters_bad_signers() {
1150 verify_votes_filters_bad_signers::<MinPk>();
1151 verify_votes_filters_bad_signers::<MinSig>();
1152 }
1153
1154 fn assemble_certificate_requires_quorum<V: Variant>() {
1155 let (schemes, _) = setup_signers::<V>(4, 17);
1156 let quorum = N3f1::quorum(schemes.len()) as usize;
1157 let proposal = sample_proposal(Epoch::new(0), View::new(7), 4);
1158
1159 let votes: Vec<_> = schemes
1160 .iter()
1161 .take(quorum - 1)
1162 .map(|scheme| {
1163 scheme
1164 .sign(Subject::Notarize {
1165 proposal: &proposal,
1166 })
1167 .unwrap()
1168 })
1169 .collect();
1170
1171 assert!(schemes[0].assemble::<_, N3f1>(votes, &Sequential).is_none());
1172 }
1173
1174 #[test]
1175 fn test_assemble_certificate_requires_quorum() {
1176 assemble_certificate_requires_quorum::<MinPk>();
1177 assemble_certificate_requires_quorum::<MinSig>();
1178 }
1179
1180 fn verify_certificate<V: Variant>() {
1181 let (schemes, verifier) = setup_signers::<V>(4, 19);
1182 let quorum = N3f1::quorum(schemes.len()) as usize;
1183 let proposal = sample_proposal(Epoch::new(0), View::new(9), 5);
1184
1185 let votes: Vec<_> = schemes
1186 .iter()
1187 .take(quorum)
1188 .map(|scheme| {
1189 scheme
1190 .sign(Subject::Finalize {
1191 proposal: &proposal,
1192 })
1193 .unwrap()
1194 })
1195 .collect();
1196
1197 let certificate = schemes[0]
1198 .assemble::<_, N3f1>(votes, &Sequential)
1199 .expect("assemble certificate");
1200
1201 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1202 &mut test_rng(),
1203 Subject::Finalize {
1204 proposal: &proposal,
1205 },
1206 &certificate,
1207 &Sequential,
1208 ));
1209 }
1210
1211 #[test]
1212 fn test_verify_certificate() {
1213 verify_certificate::<MinPk>();
1214 verify_certificate::<MinSig>();
1215 }
1216
1217 fn verify_certificate_detects_corruption<V: Variant>() {
1218 let mut rng = test_rng();
1219 let (schemes, verifier) = setup_signers::<V>(4, 23);
1220 let quorum = N3f1::quorum(schemes.len()) as usize;
1221 let proposal = sample_proposal(Epoch::new(0), View::new(11), 6);
1222
1223 let votes: Vec<_> = schemes
1224 .iter()
1225 .take(quorum)
1226 .map(|scheme| {
1227 scheme
1228 .sign(Subject::Notarize {
1229 proposal: &proposal,
1230 })
1231 .unwrap()
1232 })
1233 .collect();
1234
1235 let certificate = schemes[0]
1236 .assemble::<_, N3f1>(votes, &Sequential)
1237 .expect("assemble certificate");
1238
1239 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1240 &mut rng,
1241 Subject::Notarize {
1242 proposal: &proposal,
1243 },
1244 &certificate,
1245 &Sequential,
1246 ));
1247
1248 let cert = certificate.get().unwrap();
1249 let corrupted: Certificate<V> = Signature {
1250 vote_signature: cert.seed_signature,
1251 seed_signature: cert.seed_signature,
1252 }
1253 .into();
1254 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1255 &mut rng,
1256 Subject::Notarize {
1257 proposal: &proposal,
1258 },
1259 &corrupted,
1260 &Sequential,
1261 ));
1262 }
1263
1264 #[test]
1265 fn test_verify_certificate_detects_corruption() {
1266 verify_certificate_detects_corruption::<MinPk>();
1267 verify_certificate_detects_corruption::<MinSig>();
1268 }
1269
1270 fn certificate_codec_roundtrip<V: Variant>() {
1271 let (schemes, _) = setup_signers::<V>(5, 29);
1272 let quorum = N3f1::quorum(schemes.len()) as usize;
1273 let proposal = sample_proposal(Epoch::new(0), View::new(13), 7);
1274
1275 let votes: Vec<_> = schemes
1276 .iter()
1277 .take(quorum)
1278 .map(|scheme| {
1279 scheme
1280 .sign(Subject::Notarize {
1281 proposal: &proposal,
1282 })
1283 .unwrap()
1284 })
1285 .collect();
1286
1287 let certificate = schemes[0]
1288 .assemble::<_, N3f1>(votes, &Sequential)
1289 .expect("assemble certificate");
1290
1291 let encoded = certificate.encode();
1292 let decoded = Certificate::<V>::decode_cfg(encoded, &()).expect("decode certificate");
1293 assert_eq!(decoded, certificate);
1294 }
1295
1296 #[test]
1297 fn test_certificate_codec_roundtrip() {
1298 certificate_codec_roundtrip::<MinPk>();
1299 certificate_codec_roundtrip::<MinSig>();
1300 }
1301
1302 fn seed_codec_roundtrip<V: Variant>() {
1303 let (schemes, _) = setup_signers::<V>(4, 5);
1304 let quorum = N3f1::quorum(schemes.len()) as usize;
1305 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1306
1307 let votes: Vec<_> = schemes
1308 .iter()
1309 .take(quorum)
1310 .map(|scheme| {
1311 scheme
1312 .sign(Subject::Finalize {
1313 proposal: &proposal,
1314 })
1315 .unwrap()
1316 })
1317 .collect();
1318
1319 let certificate = schemes[0]
1320 .assemble::<_, N3f1>(votes, &Sequential)
1321 .expect("assemble certificate");
1322 let cert = certificate.get().unwrap();
1323
1324 let seed = Seed::new(proposal.round, cert.seed_signature);
1325
1326 let encoded = seed.encode();
1327 let decoded = Seed::<V>::decode_cfg(encoded, &()).expect("decode seed");
1328 assert_eq!(decoded, seed);
1329 }
1330
1331 #[test]
1332 fn test_seed_codec_roundtrip() {
1333 seed_codec_roundtrip::<MinPk>();
1334 seed_codec_roundtrip::<MinSig>();
1335 }
1336
1337 fn seed_verify<V: Variant>() {
1338 let (schemes, _) = setup_signers::<V>(4, 5);
1339 let quorum = N3f1::quorum(schemes.len()) as usize;
1340 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1341
1342 let votes: Vec<_> = schemes
1343 .iter()
1344 .take(quorum)
1345 .map(|scheme| {
1346 scheme
1347 .sign(Subject::Finalize {
1348 proposal: &proposal,
1349 })
1350 .unwrap()
1351 })
1352 .collect();
1353
1354 let certificate = schemes[0]
1355 .assemble::<_, N3f1>(votes, &Sequential)
1356 .expect("assemble certificate");
1357 let cert = certificate.get().unwrap();
1358
1359 let seed = Seed::new(proposal.round, cert.seed_signature);
1360
1361 assert!(seed.verify(&schemes[0]));
1362
1363 let invalid_seed = Seed::new(
1365 Round::new(proposal.epoch(), proposal.view().next()),
1366 cert.seed_signature,
1367 );
1368
1369 assert!(!invalid_seed.verify(&schemes[0]));
1370 }
1371
1372 #[test]
1373 fn test_seed_verify() {
1374 seed_verify::<MinPk>();
1375 seed_verify::<MinSig>();
1376 }
1377
1378 fn seedable<V: Variant>() {
1379 let (schemes, _) = setup_signers::<V>(4, 5);
1380 let quorum = N3f1::quorum(schemes.len()) as usize;
1381 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1382
1383 let notarizes: Vec<_> = schemes
1384 .iter()
1385 .take(quorum)
1386 .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
1387 .collect();
1388
1389 let notarization =
1390 Notarization::from_notarizes(&schemes[0], ¬arizes, &Sequential).unwrap();
1391
1392 let finalizes: Vec<_> = schemes
1393 .iter()
1394 .take(quorum)
1395 .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap())
1396 .collect();
1397
1398 let finalization =
1399 Finalization::from_finalizes(&schemes[0], &finalizes, &Sequential).unwrap();
1400
1401 assert_eq!(notarization.seed(), finalization.seed());
1402 assert!(notarization.seed().verify(&schemes[0]));
1403 }
1404
1405 #[test]
1406 fn test_seedable() {
1407 seedable::<MinPk>();
1408 seedable::<MinSig>();
1409 }
1410
1411 fn scheme_clone_and_verifier<V: Variant>() {
1412 let (schemes, verifier) = setup_signers::<V>(4, 31);
1413 let signer = schemes[0].clone();
1414 let proposal = sample_proposal(Epoch::new(0), View::new(21), 9);
1415
1416 assert!(
1417 signer
1418 .sign(Subject::Notarize {
1419 proposal: &proposal,
1420 })
1421 .is_some(),
1422 "signer should produce votes"
1423 );
1424
1425 assert!(
1426 verifier
1427 .sign(Subject::Notarize {
1428 proposal: &proposal,
1429 })
1430 .is_none(),
1431 "verifier should not produce votes"
1432 );
1433 }
1434
1435 #[test]
1436 fn test_scheme_clone_and_verifier() {
1437 scheme_clone_and_verifier::<MinPk>();
1438 scheme_clone_and_verifier::<MinSig>();
1439 }
1440
1441 fn certificate_verifier_accepts_certificates<V: Variant>() {
1442 let (schemes, _) = setup_signers::<V>(4, 37);
1443 let quorum = N3f1::quorum(schemes.len()) as usize;
1444 let proposal = sample_proposal(Epoch::new(0), View::new(15), 8);
1445
1446 let votes: Vec<_> = schemes
1447 .iter()
1448 .take(quorum)
1449 .map(|scheme| {
1450 scheme
1451 .sign(Subject::Finalize {
1452 proposal: &proposal,
1453 })
1454 .unwrap()
1455 })
1456 .collect();
1457
1458 let certificate = schemes[0]
1459 .assemble::<_, N3f1>(votes, &Sequential)
1460 .expect("assemble certificate");
1461
1462 let certificate_verifier =
1463 Scheme::<V>::certificate_verifier(NAMESPACE, *schemes[0].identity());
1464 assert!(
1465 certificate_verifier
1466 .sign(Subject::Finalize {
1467 proposal: &proposal,
1468 })
1469 .is_none(),
1470 "certificate verifier should not produce votes"
1471 );
1472 assert!(
1473 certificate_verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1474 &mut test_rng(),
1475 Subject::Finalize {
1476 proposal: &proposal,
1477 },
1478 &certificate,
1479 &Sequential,
1480 )
1481 );
1482 }
1483
1484 #[test]
1485 fn test_certificate_verifier_accepts_certificates() {
1486 certificate_verifier_accepts_certificates::<MinPk>();
1487 certificate_verifier_accepts_certificates::<MinSig>();
1488 }
1489
1490 fn certificate_verifier_panics_on_vote<V: Variant>() {
1491 let (schemes, _) = setup_signers::<V>(4, 37);
1492 let certificate_verifier =
1493 Scheme::<V>::certificate_verifier(NAMESPACE, *schemes[0].identity());
1494 let proposal = sample_proposal(Epoch::new(0), View::new(15), 8);
1495 let vote = schemes[1]
1496 .sign(Subject::Finalize {
1497 proposal: &proposal,
1498 })
1499 .unwrap();
1500
1501 certificate_verifier.verify_attestation::<_, Sha256Digest>(
1502 &mut test_rng(),
1503 Subject::Finalize {
1504 proposal: &proposal,
1505 },
1506 &vote,
1507 &Sequential,
1508 );
1509 }
1510
1511 #[test]
1512 #[should_panic(expected = "can only be called for signer and verifier")]
1513 fn test_certificate_verifier_panics_on_vote_min_pk() {
1514 certificate_verifier_panics_on_vote::<MinPk>();
1515 }
1516
1517 #[test]
1518 #[should_panic(expected = "can only be called for signer and verifier")]
1519 fn test_certificate_verifier_panics_on_vote_min_sig() {
1520 certificate_verifier_panics_on_vote::<MinSig>();
1521 }
1522
1523 fn verify_certificate_returns_seed_randomness<V: Variant>() {
1524 let (schemes, _) = setup_signers::<V>(4, 43);
1525 let quorum = N3f1::quorum(schemes.len()) as usize;
1526 let proposal = sample_proposal(Epoch::new(0), View::new(19), 10);
1527
1528 let votes: Vec<_> = schemes
1529 .iter()
1530 .take(quorum)
1531 .map(|scheme| {
1532 scheme
1533 .sign(Subject::Notarize {
1534 proposal: &proposal,
1535 })
1536 .unwrap()
1537 })
1538 .collect();
1539
1540 let certificate = schemes[0]
1541 .assemble::<_, N3f1>(votes, &Sequential)
1542 .expect("assemble certificate");
1543 let cert = certificate.get().unwrap();
1544
1545 let seed = Seed::<V>::new(proposal.round, cert.seed_signature);
1546 assert_eq!(seed.signature, cert.seed_signature);
1547 }
1548
1549 #[test]
1550 fn test_verify_certificate_returns_seed_randomness() {
1551 verify_certificate_returns_seed_randomness::<MinPk>();
1552 verify_certificate_returns_seed_randomness::<MinSig>();
1553 }
1554
1555 fn certificate_decode_rejects_length_mismatch<V: Variant>() {
1556 let (schemes, _) = setup_signers::<V>(4, 47);
1557 let quorum = N3f1::quorum(schemes.len()) as usize;
1558 let proposal = sample_proposal(Epoch::new(0), View::new(21), 11);
1559
1560 let votes: Vec<_> = schemes
1561 .iter()
1562 .take(quorum)
1563 .map(|scheme| {
1564 scheme
1565 .sign::<Sha256Digest>(Subject::Nullify {
1566 round: proposal.round,
1567 })
1568 .unwrap()
1569 })
1570 .collect();
1571
1572 let certificate = schemes[0]
1573 .assemble::<_, N3f1>(votes, &Sequential)
1574 .expect("assemble certificate");
1575
1576 let mut encoded = certificate.encode();
1577 let truncated = encoded.split_to(encoded.len() - 1);
1578 assert!(Signature::<V>::decode_cfg(truncated, &()).is_err());
1579 }
1580
1581 #[test]
1582 fn test_certificate_decode_rejects_length_mismatch() {
1583 certificate_decode_rejects_length_mismatch::<MinPk>();
1584 certificate_decode_rejects_length_mismatch::<MinSig>();
1585 }
1586
1587 fn sign_vote_partial_matches_share<V: Variant>() {
1588 let (schemes, _) = setup_signers::<V>(4, 53);
1589 let scheme = &schemes[0];
1590 let share = scheme.share().expect("has share");
1591
1592 let proposal = sample_proposal(Epoch::new(0), View::new(23), 12);
1593 let vote = scheme
1594 .sign(Subject::Notarize {
1595 proposal: &proposal,
1596 })
1597 .unwrap();
1598
1599 let notarize_namespace = notarize_namespace(NAMESPACE);
1600 let notarize_message = proposal.encode();
1601 let expected_message = threshold::sign_message::<V>(
1602 share,
1603 notarize_namespace.as_ref(),
1604 notarize_message.as_ref(),
1605 )
1606 .value;
1607
1608 let seed_namespace = seed_namespace(NAMESPACE);
1609 let seed_message = proposal.round.encode();
1610 let expected_seed =
1611 threshold::sign_message::<V>(share, seed_namespace.as_ref(), seed_message.as_ref())
1612 .value;
1613
1614 assert_eq!(vote.signer, share.index);
1615 let sig = vote.signature.get().unwrap();
1616 assert_eq!(sig.vote_signature, expected_message);
1617 assert_eq!(sig.seed_signature, expected_seed);
1618 }
1619
1620 #[test]
1621 fn test_sign_vote_partial_matches_share() {
1622 sign_vote_partial_matches_share::<MinPk>();
1623 sign_vote_partial_matches_share::<MinSig>();
1624 }
1625
1626 fn verify_certificate_detects_seed_corruption<V: Variant>() {
1627 let mut rng = test_rng();
1628 let (schemes, verifier) = setup_signers::<V>(4, 59);
1629 let quorum = N3f1::quorum(schemes.len()) as usize;
1630 let proposal = sample_proposal(Epoch::new(0), View::new(25), 13);
1631
1632 let votes: Vec<_> = schemes
1633 .iter()
1634 .take(quorum)
1635 .map(|scheme| {
1636 scheme
1637 .sign::<Sha256Digest>(Subject::Nullify {
1638 round: proposal.round,
1639 })
1640 .unwrap()
1641 })
1642 .collect();
1643
1644 let certificate = schemes[0]
1645 .assemble::<_, N3f1>(votes, &Sequential)
1646 .expect("assemble certificate");
1647
1648 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1649 &mut rng,
1650 Subject::Nullify {
1651 round: proposal.round,
1652 },
1653 &certificate,
1654 &Sequential,
1655 ));
1656
1657 let cert = certificate.get().unwrap();
1658 let corrupted: Certificate<V> = Signature {
1659 vote_signature: cert.vote_signature,
1660 seed_signature: cert.vote_signature,
1661 }
1662 .into();
1663 assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1664 &mut rng,
1665 Subject::Nullify {
1666 round: proposal.round,
1667 },
1668 &corrupted,
1669 &Sequential,
1670 ));
1671 }
1672
1673 #[test]
1674 fn test_verify_certificate_detects_seed_corruption() {
1675 verify_certificate_detects_seed_corruption::<MinPk>();
1676 verify_certificate_detects_seed_corruption::<MinSig>();
1677 }
1678
1679 fn encrypt_decrypt<V: Variant>() {
1680 let mut rng = test_rng();
1681 let (schemes, verifier) = setup_signers::<V>(4, 61);
1682 let quorum = N3f1::quorum(schemes.len()) as usize;
1683
1684 let message = b"Secret message for future view10";
1686
1687 let target = Round::new(Epoch::new(333), View::new(10));
1689
1690 let ciphertext = schemes[0].encrypt(&mut rng, target, *message);
1692
1693 let ciphertext_verifier = verifier.encrypt(&mut rng, target, *message);
1695
1696 let proposal = sample_proposal(target.epoch(), target.view(), 14);
1698 let notarizes: Vec<_> = schemes
1699 .iter()
1700 .take(quorum)
1701 .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap())
1702 .collect();
1703
1704 let notarization =
1705 Notarization::from_notarizes(&schemes[0], ¬arizes, &Sequential).unwrap();
1706
1707 let seed = notarization.seed();
1709 let decrypted = seed.decrypt(&ciphertext).unwrap();
1710 assert_eq!(message, decrypted.as_ref());
1711
1712 let decrypted_verifier = seed.decrypt(&ciphertext_verifier).unwrap();
1713 assert_eq!(message, decrypted_verifier.as_ref());
1714 }
1715
1716 #[test]
1717 fn test_encrypt_decrypt() {
1718 encrypt_decrypt::<MinPk>();
1719 encrypt_decrypt::<MinSig>();
1720 }
1721
1722 fn verify_attestation_rejects_malleability<V: Variant>() {
1723 let mut rng = test_rng();
1724 let (schemes, _) = setup_signers::<V>(4, 67);
1725 let proposal = sample_proposal(Epoch::new(0), View::new(27), 14);
1726
1727 let attestation = schemes[0]
1728 .sign(Subject::Notarize {
1729 proposal: &proposal,
1730 })
1731 .unwrap();
1732
1733 assert!(schemes[0].verify_attestation::<_, Sha256Digest>(
1734 &mut rng,
1735 Subject::Notarize {
1736 proposal: &proposal,
1737 },
1738 &attestation,
1739 &Sequential,
1740 ));
1741
1742 let random_scalar = Scalar::random(&mut rng);
1743 let delta = V::Signature::generator() * &random_scalar;
1744 let att_sig = attestation.signature.get().unwrap();
1745 let forged_attestation: Attestation<Scheme<V>> = Attestation {
1746 signer: attestation.signer,
1747 signature: Signature {
1748 vote_signature: att_sig.vote_signature - &delta,
1749 seed_signature: att_sig.seed_signature + &delta,
1750 }
1751 .into(),
1752 };
1753
1754 let forged_sig = forged_attestation.signature.get().unwrap();
1755 let forged_sum = forged_sig.vote_signature + &forged_sig.seed_signature;
1756 let valid_sum = att_sig.vote_signature + &att_sig.seed_signature;
1757 assert_eq!(forged_sum, valid_sum, "signature sums should be equal");
1758
1759 assert!(
1760 !schemes[0].verify_attestation::<_, Sha256Digest>(
1761 &mut rng,
1762 Subject::Notarize {
1763 proposal: &proposal,
1764 },
1765 &forged_attestation,
1766 &Sequential,
1767 ),
1768 "forged attestation should be rejected"
1769 );
1770 }
1771
1772 #[test]
1773 fn test_verify_attestation_rejects_malleability() {
1774 verify_attestation_rejects_malleability::<MinPk>();
1775 verify_attestation_rejects_malleability::<MinSig>();
1776 }
1777
1778 fn verify_attestations_rejects_malleability<V: Variant>() {
1779 let mut rng = test_rng();
1780 let (schemes, _) = setup_signers::<V>(4, 71);
1781 let proposal = sample_proposal(Epoch::new(0), View::new(29), 15);
1782
1783 let attestation1 = schemes[0]
1784 .sign(Subject::Notarize {
1785 proposal: &proposal,
1786 })
1787 .unwrap();
1788 let attestation2 = schemes[1]
1789 .sign(Subject::Notarize {
1790 proposal: &proposal,
1791 })
1792 .unwrap();
1793
1794 let verification = schemes[0].verify_attestations(
1795 &mut rng,
1796 Subject::Notarize {
1797 proposal: &proposal,
1798 },
1799 vec![attestation1.clone(), attestation2.clone()],
1800 &Sequential,
1801 );
1802 assert!(verification.invalid.is_empty());
1803 assert_eq!(verification.verified.len(), 2);
1804
1805 let random_scalar = Scalar::random(&mut rng);
1806 let delta = V::Signature::generator() * &random_scalar;
1807 let att1_sig = attestation1.signature.get().unwrap();
1808 let att2_sig = attestation2.signature.get().unwrap();
1809 let forged_attestation1: Attestation<Scheme<V>> = Attestation {
1810 signer: attestation1.signer,
1811 signature: Signature {
1812 vote_signature: att1_sig.vote_signature - &delta,
1813 seed_signature: att1_sig.seed_signature,
1814 }
1815 .into(),
1816 };
1817 let forged_attestation2: Attestation<Scheme<V>> = Attestation {
1818 signer: attestation2.signer,
1819 signature: Signature {
1820 vote_signature: att2_sig.vote_signature + &delta,
1821 seed_signature: att2_sig.seed_signature,
1822 }
1823 .into(),
1824 };
1825
1826 let forged1_sig = forged_attestation1.signature.get().unwrap();
1827 let forged2_sig = forged_attestation2.signature.get().unwrap();
1828 let forged_vote_sum = forged1_sig.vote_signature + &forged2_sig.vote_signature;
1829 let valid_vote_sum = att1_sig.vote_signature + &att2_sig.vote_signature;
1830 assert_eq!(
1831 forged_vote_sum, valid_vote_sum,
1832 "vote signature sums should be equal"
1833 );
1834
1835 let verification = schemes[0].verify_attestations(
1836 &mut rng,
1837 Subject::Notarize {
1838 proposal: &proposal,
1839 },
1840 vec![forged_attestation1, forged_attestation2],
1841 &Sequential,
1842 );
1843 assert!(
1844 !verification.invalid.is_empty(),
1845 "forged attestations should be detected"
1846 );
1847 }
1848
1849 #[test]
1850 fn test_verify_attestations_rejects_malleability() {
1851 verify_attestations_rejects_malleability::<MinPk>();
1852 verify_attestations_rejects_malleability::<MinSig>();
1853 }
1854
1855 fn verify_certificate_rejects_malleability<V: Variant>() {
1856 let mut rng = test_rng();
1857 let (schemes, verifier) = setup_signers::<V>(4, 73);
1858 let quorum = N3f1::quorum(schemes.len()) as usize;
1859 let proposal = sample_proposal(Epoch::new(0), View::new(31), 16);
1860
1861 let votes: Vec<_> = schemes
1862 .iter()
1863 .take(quorum)
1864 .map(|scheme| {
1865 scheme
1866 .sign(Subject::Notarize {
1867 proposal: &proposal,
1868 })
1869 .unwrap()
1870 })
1871 .collect();
1872
1873 let certificate = schemes[0]
1874 .assemble::<_, N3f1>(votes, &Sequential)
1875 .expect("assemble certificate");
1876
1877 assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1878 &mut rng,
1879 Subject::Notarize {
1880 proposal: &proposal,
1881 },
1882 &certificate,
1883 &Sequential,
1884 ));
1885
1886 let cert = certificate.get().unwrap();
1887 let random_scalar = Scalar::random(&mut rng);
1888 let delta = V::Signature::generator() * &random_scalar;
1889 let forged_certificate: Certificate<V> = Signature {
1890 vote_signature: cert.vote_signature - &delta,
1891 seed_signature: cert.seed_signature + &delta,
1892 }
1893 .into();
1894
1895 let forged_cert = forged_certificate.get().unwrap();
1896 let forged_sum = forged_cert.vote_signature + &forged_cert.seed_signature;
1897 let valid_sum = cert.vote_signature + &cert.seed_signature;
1898 assert_eq!(forged_sum, valid_sum, "signature sums should be equal");
1899
1900 assert!(
1901 !verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1902 &mut rng,
1903 Subject::Notarize {
1904 proposal: &proposal,
1905 },
1906 &forged_certificate,
1907 &Sequential,
1908 ),
1909 "forged certificate should be rejected"
1910 );
1911 }
1912
1913 #[test]
1914 fn test_verify_certificate_rejects_malleability() {
1915 verify_certificate_rejects_malleability::<MinPk>();
1916 verify_certificate_rejects_malleability::<MinSig>();
1917 }
1918
1919 fn verify_certificates_rejects_malleability<V: Variant>() {
1920 let mut rng = test_rng();
1921 let (schemes, verifier) = setup_signers::<V>(4, 79);
1922 let quorum = N3f1::quorum(schemes.len()) as usize;
1923 let proposal1 = sample_proposal(Epoch::new(0), View::new(33), 17);
1924 let proposal2 = sample_proposal(Epoch::new(0), View::new(34), 18);
1925
1926 let votes1: Vec<_> = schemes
1927 .iter()
1928 .take(quorum)
1929 .map(|scheme| {
1930 scheme
1931 .sign(Subject::Notarize {
1932 proposal: &proposal1,
1933 })
1934 .unwrap()
1935 })
1936 .collect();
1937 let votes2: Vec<_> = schemes
1938 .iter()
1939 .take(quorum)
1940 .map(|scheme| {
1941 scheme
1942 .sign(Subject::Notarize {
1943 proposal: &proposal2,
1944 })
1945 .unwrap()
1946 })
1947 .collect();
1948
1949 let certificate1 = schemes[0]
1950 .assemble::<_, N3f1>(votes1, &Sequential)
1951 .expect("assemble certificate1");
1952 let certificate2 = schemes[0]
1953 .assemble::<_, N3f1>(votes2, &Sequential)
1954 .expect("assemble certificate2");
1955
1956 assert!(verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1957 &mut rng,
1958 [
1959 (
1960 Subject::Notarize {
1961 proposal: &proposal1,
1962 },
1963 &certificate1
1964 ),
1965 (
1966 Subject::Notarize {
1967 proposal: &proposal2,
1968 },
1969 &certificate2
1970 ),
1971 ]
1972 .into_iter(),
1973 &Sequential,
1974 ));
1975
1976 let cert1 = certificate1.get().unwrap();
1977 let cert2 = certificate2.get().unwrap();
1978 let random_scalar = Scalar::random(&mut rng);
1979 let delta = V::Signature::generator() * &random_scalar;
1980 let forged_certificate1: Certificate<V> = Signature {
1981 vote_signature: cert1.vote_signature - &delta,
1982 seed_signature: cert1.seed_signature,
1983 }
1984 .into();
1985 let forged_certificate2: Certificate<V> = Signature {
1986 vote_signature: cert2.vote_signature + &delta,
1987 seed_signature: cert2.seed_signature,
1988 }
1989 .into();
1990
1991 let forged1 = forged_certificate1.get().unwrap();
1992 let forged2 = forged_certificate2.get().unwrap();
1993 let forged_vote_sum = forged1.vote_signature + &forged2.vote_signature;
1994 let valid_vote_sum = cert1.vote_signature + &cert2.vote_signature;
1995 assert_eq!(
1996 forged_vote_sum, valid_vote_sum,
1997 "vote signature sums should be equal"
1998 );
1999
2000 assert!(
2001 !verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
2002 &mut rng,
2003 [
2004 (
2005 Subject::Notarize {
2006 proposal: &proposal1,
2007 },
2008 &forged_certificate1
2009 ),
2010 (
2011 Subject::Notarize {
2012 proposal: &proposal2,
2013 },
2014 &forged_certificate2
2015 ),
2016 ]
2017 .into_iter(),
2018 &Sequential,
2019 ),
2020 "forged certificates should be rejected"
2021 );
2022 }
2023
2024 #[test]
2025 fn test_verify_certificates_rejects_malleability() {
2026 verify_certificates_rejects_malleability::<MinPk>();
2027 verify_certificates_rejects_malleability::<MinSig>();
2028 }
2029
2030 #[cfg(feature = "arbitrary")]
2031 mod conformance {
2032 use super::*;
2033 use commonware_codec::conformance::CodecConformance;
2034
2035 commonware_conformance::conformance_tests! {
2036 CodecConformance<Signature<MinSig>>,
2037 CodecConformance<Certificate<MinSig>>,
2038 CodecConformance<Seed<MinSig>>,
2039 }
2040 }
2041}