1use crate::{
10 simplex::{
11 scheme::{
12 finalize_namespace, notarize_namespace, nullify_namespace, seed_namespace,
13 seed_namespace_and_message, vote_namespace_and_message,
14 },
15 types::{Finalization, Notarization, Subject},
16 },
17 types::{Epoch, Round, View},
18 Epochable, Viewable,
19};
20use bytes::{Buf, BufMut};
21use commonware_codec::{Encode, EncodeSize, Error, FixedSize, Read, ReadExt, Write};
22use commonware_cryptography::{
23 bls12381::{
24 primitives::{
25 group::Share,
26 ops::{
27 aggregate_signatures, aggregate_verify_multiple_messages, partial_sign_message,
28 partial_verify_multiple_public_keys, threshold_signature_recover_pair,
29 verify_message,
30 },
31 sharing::Sharing,
32 variant::{PartialSignature, Variant},
33 },
34 tle,
35 },
36 certificate::{self, Attestation, Verification},
37 Digest, PublicKey,
38};
39use commonware_utils::ordered::Set;
40use rand::{CryptoRng, Rng};
41use std::{
42 collections::{BTreeSet, HashMap},
43 fmt::Debug,
44};
45
46#[derive(Clone, Debug)]
52pub enum Scheme<P: PublicKey, V: Variant> {
53 Signer {
54 participants: Set<P>,
56 polynomial: Sharing<V>,
58 share: Share,
60 },
61 Verifier {
62 participants: Set<P>,
64 polynomial: Sharing<V>,
66 },
67 CertificateVerifier {
68 identity: V::Public,
70 },
71}
72
73impl<P: PublicKey, V: Variant> Scheme<P, V> {
74 pub fn signer(participants: Set<P>, polynomial: Sharing<V>, share: Share) -> Option<Self> {
86 assert_eq!(
87 polynomial.total().get() as usize,
88 participants.len(),
89 "polynomial total must equal participant len"
90 );
91 polynomial.precompute_partial_publics();
92 let partial_public = polynomial
93 .partial_public(share.index)
94 .expect("share index must match participant indices");
95 if partial_public == share.public::<V>() {
96 Some(Self::Signer {
97 participants,
98 polynomial,
99 share,
100 })
101 } else {
102 None
103 }
104 }
105
106 pub fn verifier(participants: Set<P>, polynomial: Sharing<V>) -> Self {
115 assert_eq!(
116 polynomial.total().get() as usize,
117 participants.len(),
118 "polynomial total must equal participant len"
119 );
120 polynomial.precompute_partial_publics();
121
122 Self::Verifier {
123 participants,
124 polynomial,
125 }
126 }
127
128 pub const fn certificate_verifier(identity: V::Public) -> Self {
135 Self::CertificateVerifier { identity }
136 }
137
138 pub fn participants(&self) -> &Set<P> {
140 match self {
141 Self::Signer { participants, .. } => participants,
142 Self::Verifier { participants, .. } => participants,
143 _ => panic!("can only be called for signer and verifier"),
144 }
145 }
146
147 pub fn identity(&self) -> &V::Public {
149 match self {
150 Self::Signer { polynomial, .. } => polynomial.public(),
151 Self::Verifier { polynomial, .. } => polynomial.public(),
152 Self::CertificateVerifier { identity, .. } => identity,
153 }
154 }
155
156 pub const fn share(&self) -> Option<&Share> {
158 match self {
159 Self::Signer { share, .. } => Some(share),
160 _ => None,
161 }
162 }
163
164 pub fn polynomial(&self) -> &Sharing<V> {
166 match self {
167 Self::Signer { polynomial, .. } => polynomial,
168 Self::Verifier { polynomial, .. } => polynomial,
169 _ => panic!("can only be called for signer and verifier"),
170 }
171 }
172
173 pub fn encrypt<R: Rng + CryptoRng>(
179 &self,
180 rng: &mut R,
181 namespace: &[u8],
182 target: Round,
183 message: impl Into<tle::Block>,
184 ) -> tle::Ciphertext<V> {
185 encrypt(rng, *self.identity(), namespace, target, message)
186 }
187}
188
189pub fn encrypt<R: Rng + CryptoRng, V: Variant>(
195 rng: &mut R,
196 identity: V::Public,
197 namespace: &[u8],
198 target: Round,
199 message: impl Into<tle::Block>,
200) -> tle::Ciphertext<V> {
201 let block = message.into();
202 let seed_ns = seed_namespace(namespace);
203 let target_message = target.encode();
204 tle::encrypt(rng, identity, (Some(&seed_ns), &target_message), &block)
205}
206
207#[cfg(feature = "mocks")]
212pub fn fixture<V, R>(
213 rng: &mut R,
214 n: u32,
215) -> commonware_cryptography::certificate::mocks::Fixture<
216 Scheme<commonware_cryptography::ed25519::PublicKey, V>,
217>
218where
219 V: Variant,
220 R: rand::RngCore + rand::CryptoRng,
221{
222 commonware_cryptography::bls12381::certificate::threshold::mocks::fixture::<_, V, _>(
223 rng,
224 n,
225 Scheme::signer,
226 Scheme::verifier,
227 )
228}
229
230#[derive(Clone, Debug, PartialEq, Eq, Hash)]
232pub struct Signature<V: Variant> {
233 pub vote_signature: V::Signature,
235 pub seed_signature: V::Signature,
237}
238
239impl<V: Variant> Write for Signature<V> {
240 fn write(&self, writer: &mut impl BufMut) {
241 self.vote_signature.write(writer);
242 self.seed_signature.write(writer);
243 }
244}
245
246impl<V: Variant> Read for Signature<V> {
247 type Cfg = ();
248
249 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
250 let vote_signature = V::Signature::read(reader)?;
251 let seed_signature = V::Signature::read(reader)?;
252
253 Ok(Self {
254 vote_signature,
255 seed_signature,
256 })
257 }
258}
259
260impl<V: Variant> FixedSize for Signature<V> {
261 const SIZE: usize = V::Signature::SIZE * 2;
262}
263
264#[cfg(feature = "arbitrary")]
265impl<V: Variant> arbitrary::Arbitrary<'_> for Signature<V>
266where
267 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
268{
269 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
270 Ok(Self {
271 vote_signature: u.arbitrary()?,
272 seed_signature: u.arbitrary()?,
273 })
274 }
275}
276
277#[derive(Clone, Debug, PartialEq, Hash, Eq)]
279pub struct Seed<V: Variant> {
280 pub round: Round,
282 pub signature: V::Signature,
284}
285
286impl<V: Variant> Seed<V> {
287 pub const fn new(round: Round, signature: V::Signature) -> Self {
289 Self { round, signature }
290 }
291
292 pub fn verify<P: PublicKey>(&self, scheme: &Scheme<P, V>, namespace: &[u8]) -> bool {
294 let seed_namespace = seed_namespace(namespace);
295 let seed_message = self.round.encode();
296
297 verify_message::<V>(
298 scheme.identity(),
299 Some(&seed_namespace),
300 &seed_message,
301 &self.signature,
302 )
303 .is_ok()
304 }
305
306 pub const fn round(&self) -> Round {
308 self.round
309 }
310
311 pub fn decrypt(&self, ciphertext: &tle::Ciphertext<V>) -> Option<tle::Block> {
316 decrypt(self, ciphertext)
317 }
318}
319
320pub fn decrypt<V: Variant>(seed: &Seed<V>, ciphertext: &tle::Ciphertext<V>) -> Option<tle::Block> {
326 tle::decrypt(&seed.signature, ciphertext)
327}
328
329impl<V: Variant> Epochable for Seed<V> {
330 fn epoch(&self) -> Epoch {
331 self.round.epoch()
332 }
333}
334
335impl<V: Variant> Viewable for Seed<V> {
336 fn view(&self) -> View {
337 self.round.view()
338 }
339}
340
341impl<V: Variant> Write for Seed<V> {
342 fn write(&self, writer: &mut impl BufMut) {
343 self.round.write(writer);
344 self.signature.write(writer);
345 }
346}
347
348impl<V: Variant> Read for Seed<V> {
349 type Cfg = ();
350
351 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
352 let round = Round::read(reader)?;
353 let signature = V::Signature::read(reader)?;
354
355 Ok(Self { round, signature })
356 }
357}
358
359impl<V: Variant> EncodeSize for Seed<V> {
360 fn encode_size(&self) -> usize {
361 self.round.encode_size() + self.signature.encode_size()
362 }
363}
364
365#[cfg(feature = "arbitrary")]
366impl<V: Variant> arbitrary::Arbitrary<'_> for Seed<V>
367where
368 V::Signature: for<'a> arbitrary::Arbitrary<'a>,
369{
370 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
371 Ok(Self {
372 round: u.arbitrary()?,
373 signature: u.arbitrary()?,
374 })
375 }
376}
377
378pub trait Seedable<V: Variant> {
380 fn seed(&self) -> Seed<V>;
382}
383
384impl<P: PublicKey, V: Variant, D: Digest> Seedable<V> for Notarization<Scheme<P, V>, D> {
385 fn seed(&self) -> Seed<V> {
386 Seed::new(self.proposal.round, self.certificate.seed_signature)
387 }
388}
389
390impl<P: PublicKey, V: Variant, D: Digest> Seedable<V> for Finalization<Scheme<P, V>, D> {
391 fn seed(&self) -> Seed<V> {
392 Seed::new(self.proposal.round, self.certificate.seed_signature)
393 }
394}
395
396impl<P: PublicKey, V: Variant + Send + Sync> certificate::Scheme for Scheme<P, V> {
397 type Subject<'a, D: Digest> = Subject<'a, D>;
398 type PublicKey = P;
399 type Signature = Signature<V>;
400 type Certificate = Signature<V>;
401
402 fn me(&self) -> Option<u32> {
403 match self {
404 Self::Signer { share, .. } => Some(share.index),
405 _ => None,
406 }
407 }
408
409 fn participants(&self) -> &Set<Self::PublicKey> {
410 self.participants()
411 }
412
413 fn sign<D: Digest>(
414 &self,
415 namespace: &[u8],
416 subject: Subject<'_, D>,
417 ) -> Option<Attestation<Self>> {
418 let share = self.share()?;
419
420 let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, &subject);
421 let vote_signature =
422 partial_sign_message::<V>(share, Some(vote_namespace.as_ref()), vote_message.as_ref())
423 .value;
424
425 let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, &subject);
426 let seed_signature =
427 partial_sign_message::<V>(share, Some(seed_namespace.as_ref()), seed_message.as_ref())
428 .value;
429
430 let signature = Signature {
431 vote_signature,
432 seed_signature,
433 };
434
435 Some(Attestation {
436 signer: share.index,
437 signature,
438 })
439 }
440
441 fn verify_attestation<D: Digest>(
442 &self,
443 namespace: &[u8],
444 subject: Subject<'_, D>,
445 attestation: &Attestation<Self>,
446 ) -> bool {
447 let Ok(evaluated) = self.polynomial().partial_public(attestation.signer) else {
448 return false;
449 };
450
451 let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, &subject);
452 let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, &subject);
453
454 let sig = aggregate_signatures::<V, _>(&[
455 attestation.signature.vote_signature,
456 attestation.signature.seed_signature,
457 ]);
458
459 aggregate_verify_multiple_messages::<V, _>(
460 &evaluated,
461 &[
462 (Some(vote_namespace.as_ref()), vote_message.as_ref()),
463 (Some(seed_namespace.as_ref()), seed_message.as_ref()),
464 ],
465 &sig,
466 1,
467 )
468 .is_ok()
469 }
470
471 fn verify_attestations<R, D, I>(
472 &self,
473 _rng: &mut R,
474 namespace: &[u8],
475 subject: Subject<'_, D>,
476 attestations: I,
477 ) -> Verification<Self>
478 where
479 R: Rng + CryptoRng,
480 D: Digest,
481 I: IntoIterator<Item = Attestation<Self>>,
482 {
483 let mut invalid = BTreeSet::new();
484 let (vote_partials, seed_partials): (Vec<_>, Vec<_>) = attestations
485 .into_iter()
486 .map(|attestation| {
487 (
488 PartialSignature::<V> {
489 index: attestation.signer,
490 value: attestation.signature.vote_signature,
491 },
492 PartialSignature::<V> {
493 index: attestation.signer,
494 value: attestation.signature.seed_signature,
495 },
496 )
497 })
498 .unzip();
499
500 let polynomial = self.polynomial();
501 let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, &subject);
502 if let Err(errs) = partial_verify_multiple_public_keys::<V, _>(
503 polynomial,
504 Some(vote_namespace.as_ref()),
505 vote_message.as_ref(),
506 vote_partials.iter(),
507 ) {
508 for partial in errs {
509 invalid.insert(partial.index);
510 }
511 }
512
513 let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, &subject);
514 if let Err(errs) = partial_verify_multiple_public_keys::<V, _>(
515 polynomial,
516 Some(seed_namespace.as_ref()),
517 seed_message.as_ref(),
518 seed_partials
519 .iter()
520 .filter(|partial| !invalid.contains(&partial.index)),
521 ) {
522 for partial in errs {
523 invalid.insert(partial.index);
524 }
525 }
526
527 let verified = vote_partials
528 .into_iter()
529 .zip(seed_partials)
530 .map(|(vote, seed)| Attestation {
531 signer: vote.index,
532 signature: Signature {
533 vote_signature: vote.value,
534 seed_signature: seed.value,
535 },
536 })
537 .filter(|attestation| !invalid.contains(&attestation.signer))
538 .collect();
539
540 Verification::new(verified, invalid.into_iter().collect())
541 }
542
543 fn assemble<I>(&self, attestations: I) -> Option<Self::Certificate>
544 where
545 I: IntoIterator<Item = Attestation<Self>>,
546 {
547 let (vote_partials, seed_partials): (Vec<_>, Vec<_>) = attestations
548 .into_iter()
549 .map(|attestation| {
550 (
551 PartialSignature::<V> {
552 index: attestation.signer,
553 value: attestation.signature.vote_signature,
554 },
555 PartialSignature::<V> {
556 index: attestation.signer,
557 value: attestation.signature.seed_signature,
558 },
559 )
560 })
561 .unzip();
562
563 let quorum = self.polynomial();
564 if vote_partials.len() < quorum.required() as usize {
565 return None;
566 }
567
568 let (vote_signature, seed_signature) = threshold_signature_recover_pair::<V, _>(
569 quorum,
570 vote_partials.iter(),
571 seed_partials.iter(),
572 )
573 .ok()?;
574
575 Some(Signature {
576 vote_signature,
577 seed_signature,
578 })
579 }
580
581 fn verify_certificate<R: Rng + CryptoRng, D: Digest>(
582 &self,
583 _rng: &mut R,
584 namespace: &[u8],
585 subject: Subject<'_, D>,
586 certificate: &Self::Certificate,
587 ) -> bool {
588 let identity = self.identity();
589
590 let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, &subject);
591 let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, &subject);
592
593 let signature =
594 aggregate_signatures::<V, _>(&[certificate.vote_signature, certificate.seed_signature]);
595
596 aggregate_verify_multiple_messages::<V, _>(
597 identity,
598 &[
599 (Some(vote_namespace.as_ref()), vote_message.as_ref()),
600 (Some(seed_namespace.as_ref()), seed_message.as_ref()),
601 ],
602 &signature,
603 1,
604 )
605 .is_ok()
606 }
607
608 fn verify_certificates<'a, R, D, I>(
609 &self,
610 _rng: &mut R,
611 namespace: &[u8],
612 certificates: I,
613 ) -> bool
614 where
615 R: Rng + CryptoRng,
616 D: Digest,
617 I: Iterator<Item = (Subject<'a, D>, &'a Self::Certificate)>,
618 {
619 let identity = self.identity();
620
621 let mut seeds = HashMap::new();
622 let mut messages = Vec::new();
623 let mut signatures = Vec::new();
624
625 let notarize_namespace = notarize_namespace(namespace);
626 let nullify_namespace = nullify_namespace(namespace);
627 let finalize_namespace = finalize_namespace(namespace);
628 let seed_namespace = seed_namespace(namespace);
629
630 for (context, certificate) in certificates {
631 match context {
632 Subject::Notarize { proposal } => {
633 let notarize_message = proposal.encode();
635 let notarize_message = (Some(notarize_namespace.as_slice()), notarize_message);
636 messages.push(notarize_message);
637 }
638 Subject::Nullify { round } => {
639 let nullify_encoded = round.encode();
641 let nullify_message =
642 (Some(nullify_namespace.as_slice()), nullify_encoded.clone());
643 messages.push(nullify_message);
644 }
645 Subject::Finalize { proposal } => {
646 let finalize_message = proposal.encode();
648 let finalize_message = (Some(finalize_namespace.as_slice()), finalize_message);
649 messages.push(finalize_message);
650 }
651 }
652 signatures.push(&certificate.vote_signature);
653
654 if let Some(previous) = seeds.get(&context.view()) {
659 if *previous != &certificate.seed_signature {
660 return false;
661 }
662 } else {
663 let seed_message = match context {
664 Subject::Notarize { proposal } | Subject::Finalize { proposal } => {
665 proposal.round.encode()
666 }
667 Subject::Nullify { round } => round.encode(),
668 };
669
670 messages.push((Some(seed_namespace.as_slice()), seed_message));
671 signatures.push(&certificate.seed_signature);
672 seeds.insert(context.view(), &certificate.seed_signature);
673 }
674 }
675
676 let signature = aggregate_signatures::<V, _>(signatures);
678 aggregate_verify_multiple_messages::<V, _>(
679 identity,
680 &messages
681 .iter()
682 .map(|(namespace, message)| (namespace.as_deref(), message.as_ref()))
683 .collect::<Vec<_>>(),
684 &signature,
685 1,
686 )
687 .is_ok()
688 }
689
690 fn is_attributable(&self) -> bool {
691 false
692 }
693
694 fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg {}
695
696 fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg {}
697}
698
699#[cfg(test)]
700mod tests {
701 use super::*;
702 use crate::{
703 simplex::{
704 scheme::{bls12381_threshold, notarize_namespace, seed_namespace},
705 types::{Finalization, Finalize, Notarization, Notarize, Proposal, Subject},
706 },
707 types::{Round, View},
708 };
709 use commonware_codec::{Decode, Encode};
710 use commonware_cryptography::{
711 bls12381::{
712 dkg::{self, deal_anonymous},
713 primitives::{
714 ops::partial_sign_message,
715 variant::{MinPk, MinSig, Variant},
716 },
717 },
718 certificate::{mocks::Fixture, Scheme as _},
719 ed25519,
720 ed25519::certificate::mocks::participants as ed25519_participants,
721 sha256::Digest as Sha256Digest,
722 Hasher, Sha256,
723 };
724 use commonware_utils::{quorum_from_slice, NZU32};
725 use rand::{rngs::StdRng, thread_rng, SeedableRng};
726
727 const NAMESPACE: &[u8] = b"bls-threshold-signing-scheme";
728
729 type Scheme<V> = super::Scheme<ed25519::PublicKey, V>;
730 type Signature<V> = super::Signature<V>;
731
732 fn setup_signers<V: Variant>(n: u32, seed: u64) -> (Vec<Scheme<V>>, Scheme<V>) {
733 let mut rng = StdRng::seed_from_u64(seed);
734 let Fixture {
735 schemes, verifier, ..
736 } = bls12381_threshold::fixture::<V, _>(&mut rng, n);
737
738 (schemes, verifier)
739 }
740
741 fn sample_proposal(epoch: Epoch, view: View, tag: u8) -> Proposal<Sha256Digest> {
742 Proposal::new(
743 Round::new(epoch, view),
744 view.previous().unwrap(),
745 Sha256::hash(&[tag]),
746 )
747 }
748
749 fn signer_shares_must_match_participant_indices<V: Variant>() {
750 let mut rng = StdRng::seed_from_u64(7);
751 let participants = ed25519_participants(&mut rng, 4);
752 let (polynomial, mut shares) =
753 dkg::deal_anonymous::<V>(&mut rng, Default::default(), NZU32!(4));
754 shares[0].index = 999;
755 Scheme::<V>::signer(participants.keys().clone(), polynomial, shares[0].clone());
756 }
757
758 #[test]
759 #[should_panic(expected = "share index must match participant indices")]
760 fn test_signer_shares_must_match_participant_indices_min_pk() {
761 signer_shares_must_match_participant_indices::<MinPk>();
762 }
763
764 #[test]
765 #[should_panic(expected = "share index must match participant indices")]
766 fn test_signer_shares_must_match_participant_indices_min_sig() {
767 signer_shares_must_match_participant_indices::<MinSig>();
768 }
769 fn scheme_polynomial_threshold_must_equal_quorum<V: Variant>() {
770 let mut rng = StdRng::seed_from_u64(7);
771 let participants = ed25519_participants(&mut rng, 5);
772 let (polynomial, shares) = deal_anonymous::<V>(&mut rng, Default::default(), NZU32!(4));
773 Scheme::<V>::signer(participants.keys().clone(), polynomial, shares[0].clone());
774 }
775
776 #[test]
777 #[should_panic(expected = "polynomial total must equal participant len")]
778 fn test_scheme_polynomial_threshold_must_equal_quorum_min_pk() {
779 scheme_polynomial_threshold_must_equal_quorum::<MinPk>();
780 }
781
782 #[test]
783 #[should_panic(expected = "polynomial total must equal participant len")]
784 fn test_scheme_polynomial_threshold_must_equal_quorum_min_sig() {
785 scheme_polynomial_threshold_must_equal_quorum::<MinSig>();
786 }
787
788 fn verifier_polynomial_threshold_must_equal_quorum<V: Variant>() {
789 let mut rng = StdRng::seed_from_u64(7);
790 let participants = ed25519_participants(&mut rng, 5);
791 let (polynomial, _) = deal_anonymous::<V>(&mut rng, Default::default(), NZU32!(4));
792 Scheme::<V>::verifier(participants.keys().clone(), polynomial);
793 }
794
795 #[test]
796 #[should_panic(expected = "polynomial total must equal participant len")]
797 fn test_verifier_polynomial_threshold_must_equal_quorum_min_pk() {
798 verifier_polynomial_threshold_must_equal_quorum::<MinPk>();
799 }
800
801 #[test]
802 #[should_panic(expected = "polynomial total must equal participant len")]
803 fn test_verifier_polynomial_threshold_must_equal_quorum_min_sig() {
804 verifier_polynomial_threshold_must_equal_quorum::<MinSig>();
805 }
806
807 fn sign_vote_roundtrip_for_each_context<V: Variant>() {
808 let (schemes, _) = setup_signers::<V>(4, 7);
809 let scheme = &schemes[0];
810
811 let proposal = sample_proposal(Epoch::new(0), View::new(2), 1);
812 let notarize_vote = scheme
813 .sign(
814 NAMESPACE,
815 Subject::Notarize {
816 proposal: &proposal,
817 },
818 )
819 .unwrap();
820 assert!(scheme.verify_attestation(
821 NAMESPACE,
822 Subject::Notarize {
823 proposal: &proposal,
824 },
825 ¬arize_vote
826 ));
827
828 let nullify_vote = scheme
829 .sign::<Sha256Digest>(
830 NAMESPACE,
831 Subject::Nullify {
832 round: proposal.round,
833 },
834 )
835 .unwrap();
836 assert!(scheme.verify_attestation::<Sha256Digest>(
837 NAMESPACE,
838 Subject::Nullify {
839 round: proposal.round,
840 },
841 &nullify_vote
842 ));
843
844 let finalize_vote = scheme
845 .sign(
846 NAMESPACE,
847 Subject::Finalize {
848 proposal: &proposal,
849 },
850 )
851 .unwrap();
852 assert!(scheme.verify_attestation(
853 NAMESPACE,
854 Subject::Finalize {
855 proposal: &proposal,
856 },
857 &finalize_vote
858 ));
859 }
860
861 #[test]
862 fn test_sign_vote_roundtrip_for_each_context() {
863 sign_vote_roundtrip_for_each_context::<MinPk>();
864 sign_vote_roundtrip_for_each_context::<MinSig>();
865 }
866
867 fn verifier_cannot_sign<V: Variant>() {
868 let (_, verifier) = setup_signers::<V>(4, 11);
869
870 let proposal = sample_proposal(Epoch::new(0), View::new(3), 2);
871 assert!(
872 verifier
873 .sign(
874 NAMESPACE,
875 Subject::Notarize {
876 proposal: &proposal,
877 },
878 )
879 .is_none(),
880 "verifier should not produce signatures"
881 );
882 }
883
884 #[test]
885 fn test_verifier_cannot_sign() {
886 verifier_cannot_sign::<MinPk>();
887 verifier_cannot_sign::<MinSig>();
888 }
889
890 fn verifier_accepts_votes<V: Variant>() {
891 let (schemes, verifier) = setup_signers::<V>(4, 11);
892 let proposal = sample_proposal(Epoch::new(0), View::new(3), 2);
893 let vote = schemes[1]
894 .sign(
895 NAMESPACE,
896 Subject::Notarize {
897 proposal: &proposal,
898 },
899 )
900 .unwrap();
901 assert!(verifier.verify_attestation(
902 NAMESPACE,
903 Subject::Notarize {
904 proposal: &proposal,
905 },
906 &vote
907 ));
908 }
909
910 #[test]
911 fn test_verifier_accepts_votes() {
912 verifier_accepts_votes::<MinPk>();
913 verifier_accepts_votes::<MinSig>();
914 }
915
916 fn verify_votes_filters_bad_signers<V: Variant>() {
917 let (schemes, _) = setup_signers::<V>(5, 13);
918 let quorum = quorum_from_slice(&schemes) as usize;
919 let proposal = sample_proposal(Epoch::new(0), View::new(5), 3);
920
921 let mut votes: Vec<_> = schemes
922 .iter()
923 .take(quorum)
924 .map(|scheme| {
925 scheme
926 .sign(
927 NAMESPACE,
928 Subject::Notarize {
929 proposal: &proposal,
930 },
931 )
932 .unwrap()
933 })
934 .collect();
935
936 let verification = schemes[0].verify_attestations(
937 &mut thread_rng(),
938 NAMESPACE,
939 Subject::Notarize {
940 proposal: &proposal,
941 },
942 votes.clone(),
943 );
944 assert!(verification.invalid.is_empty());
945 assert_eq!(verification.verified.len(), quorum);
946
947 votes[0].signer = 999;
948 let verification = schemes[0].verify_attestations(
949 &mut thread_rng(),
950 NAMESPACE,
951 Subject::Notarize {
952 proposal: &proposal,
953 },
954 votes,
955 );
956 assert_eq!(verification.invalid, vec![999]);
957 assert_eq!(verification.verified.len(), quorum - 1);
958 }
959
960 #[test]
961 fn test_verify_votes_filters_bad_signers() {
962 verify_votes_filters_bad_signers::<MinPk>();
963 verify_votes_filters_bad_signers::<MinSig>();
964 }
965
966 fn assemble_certificate_requires_quorum<V: Variant>() {
967 let (schemes, _) = setup_signers::<V>(4, 17);
968 let quorum = quorum_from_slice(&schemes) as usize;
969 let proposal = sample_proposal(Epoch::new(0), View::new(7), 4);
970
971 let votes: Vec<_> = schemes
972 .iter()
973 .take(quorum - 1)
974 .map(|scheme| {
975 scheme
976 .sign(
977 NAMESPACE,
978 Subject::Notarize {
979 proposal: &proposal,
980 },
981 )
982 .unwrap()
983 })
984 .collect();
985
986 assert!(schemes[0].assemble(votes).is_none());
987 }
988
989 #[test]
990 fn test_assemble_certificate_requires_quorum() {
991 assemble_certificate_requires_quorum::<MinPk>();
992 assemble_certificate_requires_quorum::<MinSig>();
993 }
994
995 fn verify_certificate<V: Variant>() {
996 let (schemes, verifier) = setup_signers::<V>(4, 19);
997 let quorum = quorum_from_slice(&schemes) as usize;
998 let proposal = sample_proposal(Epoch::new(0), View::new(9), 5);
999
1000 let votes: Vec<_> = schemes
1001 .iter()
1002 .take(quorum)
1003 .map(|scheme| {
1004 scheme
1005 .sign(
1006 NAMESPACE,
1007 Subject::Finalize {
1008 proposal: &proposal,
1009 },
1010 )
1011 .unwrap()
1012 })
1013 .collect();
1014
1015 let certificate = schemes[0].assemble(votes).expect("assemble certificate");
1016
1017 assert!(verifier.verify_certificate(
1018 &mut thread_rng(),
1019 NAMESPACE,
1020 Subject::Finalize {
1021 proposal: &proposal,
1022 },
1023 &certificate,
1024 ));
1025 }
1026
1027 #[test]
1028 fn test_verify_certificate() {
1029 verify_certificate::<MinPk>();
1030 verify_certificate::<MinSig>();
1031 }
1032
1033 fn verify_certificate_detects_corruption<V: Variant>() {
1034 let (schemes, verifier) = setup_signers::<V>(4, 23);
1035 let quorum = quorum_from_slice(&schemes) as usize;
1036 let proposal = sample_proposal(Epoch::new(0), View::new(11), 6);
1037
1038 let votes: Vec<_> = schemes
1039 .iter()
1040 .take(quorum)
1041 .map(|scheme| {
1042 scheme
1043 .sign(
1044 NAMESPACE,
1045 Subject::Notarize {
1046 proposal: &proposal,
1047 },
1048 )
1049 .unwrap()
1050 })
1051 .collect();
1052
1053 let certificate = schemes[0].assemble(votes).expect("assemble certificate");
1054
1055 assert!(verifier.verify_certificate(
1056 &mut thread_rng(),
1057 NAMESPACE,
1058 Subject::Notarize {
1059 proposal: &proposal,
1060 },
1061 &certificate,
1062 ));
1063
1064 let mut corrupted = certificate;
1065 corrupted.vote_signature = corrupted.seed_signature;
1066 assert!(!verifier.verify_certificate(
1067 &mut thread_rng(),
1068 NAMESPACE,
1069 Subject::Notarize {
1070 proposal: &proposal,
1071 },
1072 &corrupted,
1073 ));
1074 }
1075
1076 #[test]
1077 fn test_verify_certificate_detects_corruption() {
1078 verify_certificate_detects_corruption::<MinPk>();
1079 verify_certificate_detects_corruption::<MinSig>();
1080 }
1081
1082 fn certificate_codec_roundtrip<V: Variant>() {
1083 let (schemes, _) = setup_signers::<V>(5, 29);
1084 let quorum = quorum_from_slice(&schemes) as usize;
1085 let proposal = sample_proposal(Epoch::new(0), View::new(13), 7);
1086
1087 let votes: Vec<_> = schemes
1088 .iter()
1089 .take(quorum)
1090 .map(|scheme| {
1091 scheme
1092 .sign(
1093 NAMESPACE,
1094 Subject::Notarize {
1095 proposal: &proposal,
1096 },
1097 )
1098 .unwrap()
1099 })
1100 .collect();
1101
1102 let certificate = schemes[0].assemble(votes).expect("assemble certificate");
1103
1104 let encoded = certificate.encode();
1105 let decoded =
1106 Signature::<V>::decode_cfg(encoded.freeze(), &()).expect("decode certificate");
1107 assert_eq!(decoded, certificate);
1108 }
1109
1110 #[test]
1111 fn test_certificate_codec_roundtrip() {
1112 certificate_codec_roundtrip::<MinPk>();
1113 certificate_codec_roundtrip::<MinSig>();
1114 }
1115
1116 fn seed_codec_roundtrip<V: Variant>() {
1117 let (schemes, _) = setup_signers::<V>(4, 5);
1118 let quorum = quorum_from_slice(&schemes) as usize;
1119 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1120
1121 let votes: Vec<_> = schemes
1122 .iter()
1123 .take(quorum)
1124 .map(|scheme| {
1125 scheme
1126 .sign(
1127 NAMESPACE,
1128 Subject::Finalize {
1129 proposal: &proposal,
1130 },
1131 )
1132 .unwrap()
1133 })
1134 .collect();
1135
1136 let certificate = schemes[0].assemble(votes).expect("assemble certificate");
1137
1138 let seed = Seed::new(proposal.round, certificate.seed_signature);
1139
1140 let encoded = seed.encode();
1141 let decoded = Seed::<V>::decode_cfg(encoded, &()).expect("decode seed");
1142 assert_eq!(decoded, seed);
1143 }
1144
1145 #[test]
1146 fn test_seed_codec_roundtrip() {
1147 seed_codec_roundtrip::<MinPk>();
1148 seed_codec_roundtrip::<MinSig>();
1149 }
1150
1151 fn seed_verify<V: Variant>() {
1152 let (schemes, _) = setup_signers::<V>(4, 5);
1153 let quorum = quorum_from_slice(&schemes) as usize;
1154 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1155
1156 let votes: Vec<_> = schemes
1157 .iter()
1158 .take(quorum)
1159 .map(|scheme| {
1160 scheme
1161 .sign(
1162 NAMESPACE,
1163 Subject::Finalize {
1164 proposal: &proposal,
1165 },
1166 )
1167 .unwrap()
1168 })
1169 .collect();
1170
1171 let certificate = schemes[0].assemble(votes).expect("assemble certificate");
1172
1173 let seed = Seed::new(proposal.round, certificate.seed_signature);
1174
1175 assert!(seed.verify(&schemes[0], NAMESPACE));
1176
1177 let invalid_seed = Seed::new(
1179 Round::new(proposal.epoch(), proposal.view().next()),
1180 certificate.seed_signature,
1181 );
1182
1183 assert!(!invalid_seed.verify(&schemes[0], NAMESPACE));
1184 }
1185
1186 #[test]
1187 fn test_seed_verify() {
1188 seed_verify::<MinPk>();
1189 seed_verify::<MinSig>();
1190 }
1191
1192 fn seedable<V: Variant>() {
1193 let (schemes, _) = setup_signers::<V>(4, 5);
1194 let quorum = quorum_from_slice(&schemes) as usize;
1195 let proposal = sample_proposal(Epoch::new(0), View::new(1), 0);
1196
1197 let notarizes: Vec<_> = schemes
1198 .iter()
1199 .take(quorum)
1200 .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap())
1201 .collect();
1202
1203 let notarization = Notarization::from_notarizes(&schemes[0], ¬arizes).unwrap();
1204
1205 let finalizes: Vec<_> = schemes
1206 .iter()
1207 .take(quorum)
1208 .map(|scheme| Finalize::sign(scheme, NAMESPACE, proposal.clone()).unwrap())
1209 .collect();
1210
1211 let finalization = Finalization::from_finalizes(&schemes[0], &finalizes).unwrap();
1212
1213 assert_eq!(notarization.seed(), finalization.seed());
1214 assert!(notarization.seed().verify(&schemes[0], NAMESPACE));
1215 }
1216
1217 #[test]
1218 fn test_seedable() {
1219 seedable::<MinPk>();
1220 seedable::<MinSig>();
1221 }
1222
1223 fn scheme_clone_and_verifier<V: Variant>() {
1224 let (schemes, verifier) = setup_signers::<V>(4, 31);
1225 let signer = schemes[0].clone();
1226 let proposal = sample_proposal(Epoch::new(0), View::new(21), 9);
1227
1228 assert!(
1229 signer
1230 .sign(
1231 NAMESPACE,
1232 Subject::Notarize {
1233 proposal: &proposal,
1234 },
1235 )
1236 .is_some(),
1237 "signer should produce votes"
1238 );
1239
1240 assert!(
1241 verifier
1242 .sign(
1243 NAMESPACE,
1244 Subject::Notarize {
1245 proposal: &proposal,
1246 },
1247 )
1248 .is_none(),
1249 "verifier should not produce votes"
1250 );
1251 }
1252
1253 #[test]
1254 fn test_scheme_clone_and_verifier() {
1255 scheme_clone_and_verifier::<MinPk>();
1256 scheme_clone_and_verifier::<MinSig>();
1257 }
1258
1259 fn certificate_verifier_accepts_certificates<V: Variant>() {
1260 let (schemes, _) = setup_signers::<V>(4, 37);
1261 let quorum = quorum_from_slice(&schemes) as usize;
1262 let proposal = sample_proposal(Epoch::new(0), View::new(15), 8);
1263
1264 let votes: Vec<_> = schemes
1265 .iter()
1266 .take(quorum)
1267 .map(|scheme| {
1268 scheme
1269 .sign(
1270 NAMESPACE,
1271 Subject::Finalize {
1272 proposal: &proposal,
1273 },
1274 )
1275 .unwrap()
1276 })
1277 .collect();
1278
1279 let certificate = schemes[0].assemble(votes).expect("assemble certificate");
1280
1281 let certificate_verifier = Scheme::<V>::certificate_verifier(*schemes[0].identity());
1282 assert!(
1283 certificate_verifier
1284 .sign(
1285 NAMESPACE,
1286 Subject::Finalize {
1287 proposal: &proposal,
1288 },
1289 )
1290 .is_none(),
1291 "certificate verifier should not produce votes"
1292 );
1293 assert!(certificate_verifier.verify_certificate(
1294 &mut thread_rng(),
1295 NAMESPACE,
1296 Subject::Finalize {
1297 proposal: &proposal,
1298 },
1299 &certificate,
1300 ));
1301 }
1302
1303 #[test]
1304 fn test_certificate_verifier_accepts_certificates() {
1305 certificate_verifier_accepts_certificates::<MinPk>();
1306 certificate_verifier_accepts_certificates::<MinSig>();
1307 }
1308
1309 fn certificate_verifier_panics_on_vote<V: Variant>() {
1310 let (schemes, _) = setup_signers::<V>(4, 37);
1311 let certificate_verifier = Scheme::<V>::certificate_verifier(*schemes[0].identity());
1312 let proposal = sample_proposal(Epoch::new(0), View::new(15), 8);
1313 let vote = schemes[1]
1314 .sign(
1315 NAMESPACE,
1316 Subject::Finalize {
1317 proposal: &proposal,
1318 },
1319 )
1320 .unwrap();
1321
1322 certificate_verifier.verify_attestation(
1323 NAMESPACE,
1324 Subject::Finalize {
1325 proposal: &proposal,
1326 },
1327 &vote,
1328 );
1329 }
1330
1331 #[test]
1332 #[should_panic(expected = "can only be called for signer and verifier")]
1333 fn test_certificate_verifier_panics_on_vote_min_pk() {
1334 certificate_verifier_panics_on_vote::<MinPk>();
1335 }
1336
1337 #[test]
1338 #[should_panic(expected = "can only be called for signer and verifier")]
1339 fn test_certificate_verifier_panics_on_vote_min_sig() {
1340 certificate_verifier_panics_on_vote::<MinSig>();
1341 }
1342
1343 fn verify_certificate_returns_seed_randomness<V: Variant>() {
1344 let (schemes, _) = setup_signers::<V>(4, 43);
1345 let quorum = quorum_from_slice(&schemes) as usize;
1346 let proposal = sample_proposal(Epoch::new(0), View::new(19), 10);
1347
1348 let votes: Vec<_> = schemes
1349 .iter()
1350 .take(quorum)
1351 .map(|scheme| {
1352 scheme
1353 .sign(
1354 NAMESPACE,
1355 Subject::Notarize {
1356 proposal: &proposal,
1357 },
1358 )
1359 .unwrap()
1360 })
1361 .collect();
1362
1363 let certificate = schemes[0].assemble(votes).expect("assemble certificate");
1364
1365 let seed = Seed::<V>::new(proposal.round, certificate.seed_signature);
1366 assert_eq!(seed.signature, certificate.seed_signature);
1367 }
1368
1369 #[test]
1370 fn test_verify_certificate_returns_seed_randomness() {
1371 verify_certificate_returns_seed_randomness::<MinPk>();
1372 verify_certificate_returns_seed_randomness::<MinSig>();
1373 }
1374
1375 fn certificate_decode_rejects_length_mismatch<V: Variant>() {
1376 let (schemes, _) = setup_signers::<V>(4, 47);
1377 let quorum = quorum_from_slice(&schemes) as usize;
1378 let proposal = sample_proposal(Epoch::new(0), View::new(21), 11);
1379
1380 let votes: Vec<_> = schemes
1381 .iter()
1382 .take(quorum)
1383 .map(|scheme| {
1384 scheme
1385 .sign::<Sha256Digest>(
1386 NAMESPACE,
1387 Subject::Nullify {
1388 round: proposal.round,
1389 },
1390 )
1391 .unwrap()
1392 })
1393 .collect();
1394
1395 let certificate = schemes[0].assemble(votes).expect("assemble certificate");
1396
1397 let mut encoded = certificate.encode().freeze();
1398 let truncated = encoded.split_to(encoded.len() - 1);
1399 assert!(Signature::<V>::decode_cfg(truncated, &()).is_err());
1400 }
1401
1402 #[test]
1403 fn test_certificate_decode_rejects_length_mismatch() {
1404 certificate_decode_rejects_length_mismatch::<MinPk>();
1405 certificate_decode_rejects_length_mismatch::<MinSig>();
1406 }
1407
1408 fn sign_vote_partial_matches_share<V: Variant>() {
1409 let (schemes, _) = setup_signers::<V>(4, 53);
1410 let scheme = &schemes[0];
1411 let share = scheme.share().expect("has share");
1412
1413 let proposal = sample_proposal(Epoch::new(0), View::new(23), 12);
1414 let vote = scheme
1415 .sign(
1416 NAMESPACE,
1417 Subject::Notarize {
1418 proposal: &proposal,
1419 },
1420 )
1421 .unwrap();
1422
1423 let notarize_namespace = notarize_namespace(NAMESPACE);
1424 let notarize_message = proposal.encode();
1425 let expected_message = partial_sign_message::<V>(
1426 share,
1427 Some(notarize_namespace.as_ref()),
1428 notarize_message.as_ref(),
1429 )
1430 .value;
1431
1432 let seed_namespace = seed_namespace(NAMESPACE);
1433 let seed_message = proposal.round.encode();
1434 let expected_seed =
1435 partial_sign_message::<V>(share, Some(seed_namespace.as_ref()), seed_message.as_ref())
1436 .value;
1437
1438 assert_eq!(vote.signer, share.index);
1439 assert_eq!(vote.signature.vote_signature, expected_message);
1440 assert_eq!(vote.signature.seed_signature, expected_seed);
1441 }
1442
1443 #[test]
1444 fn test_sign_vote_partial_matches_share() {
1445 sign_vote_partial_matches_share::<MinPk>();
1446 sign_vote_partial_matches_share::<MinSig>();
1447 }
1448
1449 fn verify_certificate_detects_seed_corruption<V: Variant>() {
1450 let (schemes, verifier) = setup_signers::<V>(4, 59);
1451 let quorum = quorum_from_slice(&schemes) as usize;
1452 let proposal = sample_proposal(Epoch::new(0), View::new(25), 13);
1453
1454 let votes: Vec<_> = schemes
1455 .iter()
1456 .take(quorum)
1457 .map(|scheme| {
1458 scheme
1459 .sign::<Sha256Digest>(
1460 NAMESPACE,
1461 Subject::Nullify {
1462 round: proposal.round,
1463 },
1464 )
1465 .unwrap()
1466 })
1467 .collect();
1468
1469 let certificate = schemes[0].assemble(votes).expect("assemble certificate");
1470
1471 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1472 &mut thread_rng(),
1473 NAMESPACE,
1474 Subject::Nullify {
1475 round: proposal.round,
1476 },
1477 &certificate,
1478 ));
1479
1480 let mut corrupted = certificate;
1481 corrupted.seed_signature = corrupted.vote_signature;
1482 assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1483 &mut thread_rng(),
1484 NAMESPACE,
1485 Subject::Nullify {
1486 round: proposal.round,
1487 },
1488 &corrupted,
1489 ));
1490 }
1491
1492 #[test]
1493 fn test_verify_certificate_detects_seed_corruption() {
1494 verify_certificate_detects_seed_corruption::<MinPk>();
1495 verify_certificate_detects_seed_corruption::<MinSig>();
1496 }
1497
1498 fn encrypt_decrypt<V: Variant>() {
1499 let (schemes, verifier) = setup_signers::<V>(4, 61);
1500 let quorum = quorum_from_slice(&schemes) as usize;
1501
1502 let message = b"Secret message for future view10";
1504
1505 let target = Round::new(Epoch::new(333), View::new(10));
1507
1508 let ciphertext = schemes[0].encrypt(&mut thread_rng(), NAMESPACE, target, *message);
1510
1511 let ciphertext_verifier = verifier.encrypt(&mut thread_rng(), NAMESPACE, target, *message);
1513
1514 let proposal = sample_proposal(target.epoch(), target.view(), 14);
1516 let notarizes: Vec<_> = schemes
1517 .iter()
1518 .take(quorum)
1519 .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap())
1520 .collect();
1521
1522 let notarization = Notarization::from_notarizes(&schemes[0], ¬arizes).unwrap();
1523
1524 let seed = notarization.seed();
1526 let decrypted = seed.decrypt(&ciphertext).unwrap();
1527 assert_eq!(message, decrypted.as_ref());
1528
1529 let decrypted_verifier = seed.decrypt(&ciphertext_verifier).unwrap();
1530 assert_eq!(message, decrypted_verifier.as_ref());
1531 }
1532
1533 #[test]
1534 fn test_encrypt_decrypt() {
1535 encrypt_decrypt::<MinPk>();
1536 encrypt_decrypt::<MinSig>();
1537 }
1538
1539 #[cfg(feature = "arbitrary")]
1540 mod conformance {
1541 use super::*;
1542 use commonware_codec::conformance::CodecConformance;
1543
1544 commonware_conformance::conformance_tests! {
1545 CodecConformance<Signature<MinSig>>,
1546 CodecConformance<Seed<MinSig>>,
1547 }
1548 }
1549}