1#![cfg_attr(
20 feature = "bls12381",
21 doc = "
22- [`bls12381_multisig`]: Attributable signatures with aggregated verification. Signatures
23 can be aggregated into a single multi-signature for compact certificates while preserving
24 attribution (signer indices are stored alongside the aggregated signature). Callers must verify
25 a proof of possession for every BLS signing key before constructing the scheme.
26
27- [`bls12381_threshold`]: Non-attributable threshold signatures. Produces succinct
28 certificates that are constant-size regardless of committee size. Requires a trusted
29 setup (distributed key generation) and cannot attribute signatures to individual signers.
30"
31)]
32#![cfg_attr(
57 feature = "bls12381",
58 doc = "Some cryptographic schemes are only performant when used in batch verification (like
59[`bls12381_multisig`]) and/or are refreshed frequently (like [`bls12381_threshold`])."
60)]
61#[cfg(feature = "bls12381")]
64pub use crate::bls12381::certificate::{
65 multisig as bls12381_multisig, threshold as bls12381_threshold,
66};
67pub use crate::ed25519::certificate as ed25519;
68#[commonware_macros::stability(ALPHA)]
69pub use crate::secp256r1::certificate as secp256r1;
70use crate::{Digest, PublicKey};
71#[cfg(not(feature = "std"))]
72use alloc::{collections::BTreeSet, sync::Arc, vec, vec::Vec};
73use bytes::{BufMut, Bytes};
74use commonware_codec::{
75 Buf, Codec, CodecFixed, EncodeSize, Error as CodecError, FixedSize, MaxEncodeSize, Read,
76 ReadExt, Write, types::lazy::Lazy, util::max_items_size,
77};
78use commonware_parallel::Strategy;
79use commonware_utils::{Faults, Participant, Widen, bitmap::BitMap, iter::NonEmpty, ordered::Set};
80use core::{fmt::Debug, hash::Hash};
81use rand_chacha::ChaCha20Rng;
82use rand_core::{CryptoRng, SeedableRng};
83#[cfg(feature = "std")]
84use std::{collections::BTreeSet, sync::Arc, vec::Vec};
85use thiserror::Error;
86
87#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89pub enum Membership {
90 Signer(Participant),
92 Member(Participant),
94}
95
96impl Membership {
97 pub const fn index(&self) -> Participant {
99 match self {
100 Self::Signer(index) | Self::Member(index) => *index,
101 }
102 }
103
104 pub const fn signer(&self) -> Option<Participant> {
106 match self {
107 Self::Signer(index) => Some(*index),
108 Self::Member(_) => None,
109 }
110 }
111}
112
113#[derive(Clone, Debug)]
115pub struct Attestation<S: Scheme> {
116 pub signer: Participant,
118 pub signature: Lazy<S::Signature>,
120}
121
122impl<S: Scheme> PartialEq for Attestation<S> {
123 fn eq(&self, other: &Self) -> bool {
124 self.signer == other.signer && self.signature == other.signature
125 }
126}
127
128impl<S: Scheme> Eq for Attestation<S> {}
129
130impl<S: Scheme> Hash for Attestation<S> {
131 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
132 self.signer.hash(state);
133 self.signature.hash(state);
134 }
135}
136
137impl<S: Scheme> Write for Attestation<S> {
138 fn write(&self, writer: &mut impl BufMut) {
139 self.signer.write(writer);
140 self.signature.write(writer);
141 }
142}
143
144impl<S: Scheme> EncodeSize for Attestation<S> {
145 fn encode_size(&self) -> usize {
146 self.signer.encode_size() + self.signature.encode_size()
147 }
148}
149
150impl<S: Scheme> MaxEncodeSize for Attestation<S> {
151 const MAX_ENCODE_SIZE: usize = Participant::MAX_ENCODE_SIZE + S::Signature::SIZE;
152}
153
154impl<S: Scheme> Read for Attestation<S> {
155 type Cfg = ();
156
157 fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
158 let signer = Participant::read(reader)?;
159 let signature = ReadExt::read(reader)?;
160
161 Ok(Self { signer, signature })
162 }
163}
164
165#[cfg(feature = "arbitrary")]
166impl<S: Scheme> arbitrary::Arbitrary<'_> for Attestation<S>
167where
168 S::Signature: for<'a> arbitrary::Arbitrary<'a>,
169{
170 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
171 let signer = Participant::arbitrary(u)?;
172 let signature = S::Signature::arbitrary(u)?;
173 Ok(Self {
174 signer,
175 signature: signature.into(),
176 })
177 }
178}
179
180pub struct Verification<S: Scheme> {
182 pub verified: Vec<Attestation<S>>,
184 pub invalid: Vec<Participant>,
186}
187
188impl<S: Scheme> Verification<S> {
189 pub const fn new(verified: Vec<Attestation<S>>, invalid: Vec<Participant>) -> Self {
191 Self { verified, invalid }
192 }
193}
194
195#[derive(Debug, Error, PartialEq, Eq)]
197pub enum AssemblyError {
198 #[error("insufficient attestations: expected={0}, found={1}")]
200 InsufficientAttestations(u32, u32),
201 #[error("unknown signer: {0}")]
203 UnknownSigner(Participant),
204 #[error("duplicate signer: {0}")]
206 DuplicateSigner(Participant),
207 #[error("malformed signature from signer: {0}")]
209 MalformedSignature(Participant),
210 #[error("certificate recovery failed")]
212 RecoveryFailed,
213}
214
215pub trait Namespace: Clone + Send + Sync {
220 fn derive(namespace: &[u8]) -> Self;
222}
223
224impl Namespace for Vec<u8> {
225 fn derive(namespace: &[u8]) -> Self {
226 namespace.to_vec()
227 }
228}
229
230pub trait Subject: Clone + Debug + Send + Sync {
232 type Namespace: Namespace;
234
235 fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8];
237
238 fn message(&self) -> Bytes;
240}
241
242pub trait Verifier: Clone + Debug + Send + Sync + 'static {
247 type Subject<'a, D: Digest>: Subject;
249
250 type Faults: Faults;
252
253 type PublicKey: PublicKey;
255
256 type Certificate: Clone + Debug + Eq + Hash + Send + Sync + Codec;
258
259 fn verify_certificate<R, D>(
265 &self,
266 rng: &mut R,
267 subject: Self::Subject<'_, D>,
268 certificate: &Self::Certificate,
269 strategy: &impl Strategy,
270 ) -> bool
271 where
272 R: CryptoRng,
273 D: Digest;
274
275 fn verify_certificates<'a, R, D, I>(
278 &self,
279 rng: &mut R,
280 certificates: NonEmpty<I>,
281 strategy: &impl Strategy,
282 ) -> bool
283 where
284 R: CryptoRng,
285 D: Digest,
286 I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
287 {
288 for (subject, certificate) in certificates {
289 if !self.verify_certificate(rng, subject, certificate, strategy) {
290 return false;
291 }
292 }
293
294 true
295 }
296
297 fn verify_certificates_bisect<'a, R, D>(
304 &self,
305 rng: &mut R,
306 certificates: &[(Self::Subject<'a, D>, &'a Self::Certificate)],
307 strategy: &impl Strategy,
308 ) -> Vec<bool>
309 where
310 R: CryptoRng,
311 D: Digest,
312 Self::Subject<'a, D>: Copy,
313 {
314 let len = certificates.len();
315 let mut verified = vec![false; len];
316 if len == 0 {
317 return verified;
318 }
319
320 if !Self::is_batchable() {
323 for (i, (subject, certificate)) in certificates.iter().enumerate() {
324 verified[i] = self.verify_certificate(rng, *subject, certificate, strategy);
325 }
326 return verified;
327 }
328
329 let mut stack = vec![(0, len)];
341 while let Some((start, end)) = stack.pop() {
342 let (first, rest) = certificates[start..end]
343 .split_first()
344 .expect("bisection ranges are non-empty");
345 let certificates = NonEmpty::new(*first, rest.iter().copied());
346 if self.verify_certificates(rng, certificates, strategy) {
347 verified[start..end].fill(true);
348 } else if end - start > 1 {
349 let mid = start + (end - start) / 2;
350 stack.push((mid, end));
351 stack.push((start, mid));
352 }
353 }
354
355 verified
356 }
357
358 fn is_batchable() -> bool;
366
367 fn max_certificate_size(participants: u32) -> usize;
370
371 fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg;
373
374 fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg;
379}
380
381pub trait Scheme: Verifier {
387 type Signature: Clone + Debug + Eq + Hash + Send + Sync + CodecFixed<Cfg = ()>;
389
390 fn me(&self) -> Option<Membership>;
393
394 fn participants(&self) -> &Set<Self::PublicKey>;
396
397 fn sign<D: Digest>(&self, subject: Self::Subject<'_, D>) -> Option<Attestation<Self>>;
400
401 fn verify_attestation<R, D>(
403 &self,
404 rng: &mut R,
405 subject: Self::Subject<'_, D>,
406 attestation: &Attestation<Self>,
407 strategy: &impl Strategy,
408 ) -> bool
409 where
410 R: CryptoRng,
411 D: Digest;
412
413 fn verify_attestations<R, D, I>(
419 &self,
420 rng: &mut R,
421 subject: Self::Subject<'_, D>,
422 attestations: I,
423 strategy: &impl Strategy,
424 ) -> Verification<Self>
425 where
426 R: CryptoRng,
427 D: Digest,
428 I: IntoIterator<Item = Attestation<Self>>,
429 I::IntoIter: Send,
430 {
431 let mut invalid = BTreeSet::new();
432
433 let verified = attestations.into_iter().filter_map(|attestation| {
434 if self.verify_attestation(&mut *rng, subject.clone(), &attestation, strategy) {
435 Some(attestation)
436 } else {
437 invalid.insert(attestation.signer);
438 None
439 }
440 });
441
442 Verification::new(verified.collect(), invalid.into_iter().collect())
443 }
444
445 fn optimistic_assemble<'a, R, D, I, J>(
458 &self,
459 rng: &mut R,
460 subject: Self::Subject<'_, D>,
461 pending: I,
462 verified: J,
463 strategy: &impl Strategy,
464 ) -> Result<Self::Certificate, Verification<Self>>
465 where
466 R: CryptoRng,
467 D: Digest,
468 I: IntoIterator<Item = Attestation<Self>>,
469 I::IntoIter: ExactSizeIterator + Send,
470 J: IntoIterator<Item = &'a Attestation<Self>>,
471 J::IntoIter: Send;
472
473 fn assemble<I>(
482 &self,
483 attestations: NonEmpty<I>,
484 strategy: &impl Strategy,
485 ) -> Result<Self::Certificate, AssemblyError>
486 where
487 I: Iterator<Item = Attestation<Self>> + Send;
488
489 fn is_attributable() -> bool;
494}
495
496pub fn verify_then_assemble<'a, S, R, D, I, J>(
502 scheme: &S,
503 rng: &mut R,
504 subject: S::Subject<'_, D>,
505 pending: I,
506 verified: J,
507 strategy: &impl Strategy,
508) -> Result<S::Certificate, Verification<S>>
509where
510 S: Scheme,
511 R: CryptoRng,
512 D: Digest,
513 I: IntoIterator<Item = Attestation<S>>,
514 I::IntoIter: Send,
515 J: IntoIterator<Item = &'a Attestation<S>>,
516 J::IntoIter: Send,
517{
518 let result = NonEmpty::try_new(pending.into_iter()).map_or_else(
521 || Verification::new(Vec::new(), Vec::new()),
522 |pending| scheme.verify_attestations::<_, D, _>(rng, subject, pending, strategy),
523 );
524 if !result.invalid.is_empty() {
525 return Err(result);
526 }
527
528 let Some(attestations) = NonEmpty::try_new(
531 result
532 .verified
533 .iter()
534 .cloned()
535 .chain(verified.into_iter().cloned()),
536 ) else {
537 return Err(result);
538 };
539
540 scheme.assemble(attestations, strategy).map_err(|_| result)
541}
542
543pub fn optimistic_assemble<'a, S, R, D, I, J>(
548 scheme: &S,
549 rng: &mut R,
550 subject: S::Subject<'_, D>,
551 pending: I,
552 verified: J,
553 strategy: &impl Strategy,
554) -> Result<S::Certificate, Verification<S>>
555where
556 S: Scheme,
557 R: CryptoRng,
558 D: Digest,
559 I: IntoIterator<Item = Attestation<S>>,
560 I::IntoIter: ExactSizeIterator + Send,
561 J: IntoIterator<Item = &'a Attestation<S>>,
562 J::IntoIter: Send,
563{
564 let pending = pending.into_iter();
565 let verified = verified.into_iter();
566
567 if pending.len() == 0 {
569 return verify_then_assemble::<_, _, D, _, _>(
570 scheme, rng, subject, pending, verified, strategy,
571 );
572 }
573
574 let mut pending = strategy.map_collect_vec(pending, |attestation| {
577 let _ = attestation.signature.get();
578 attestation
579 });
580 let attestations = pending.iter().cloned().chain(verified.cloned());
581 if let Some(attestations) = NonEmpty::try_new(attestations)
582 && let Ok(certificate) = scheme.assemble(attestations, strategy)
583 && scheme.verify_certificate::<_, D>(rng, subject.clone(), &certificate, strategy)
584 {
585 return Ok(certificate);
586 }
587
588 if pending.len() == 1 {
590 return Err(scheme.verify_attestations::<_, D, _>(rng, subject, pending, strategy));
591 }
592
593 let rest = pending.split_off(pending.len().div_ceil(2));
597 let mut first_rng = ChaCha20Rng::from_rng(&mut *rng);
598 let mut rest_rng = ChaCha20Rng::from_rng(rng);
599 let first_subject = subject.clone();
600 let (mut result, rest) = strategy.join(
601 || scheme.verify_attestations::<_, D, _>(&mut first_rng, first_subject, pending, strategy),
602 || scheme.verify_attestations::<_, D, _>(&mut rest_rng, subject, rest, strategy),
603 );
604 result.verified.extend(rest.verified);
605 result.invalid.extend(rest.invalid);
606 Err(result)
607}
608
609#[derive(Clone, Debug)]
615pub struct Scoped<S: Scheme> {
616 scheme: Arc<S>,
617 can_sign: bool,
618}
619
620impl<S: Scheme> Scoped<S> {
621 pub const fn verifier(scheme: Arc<S>) -> Self {
623 Self {
624 scheme,
625 can_sign: false,
626 }
627 }
628
629 pub const fn scheme(scheme: Arc<S>) -> Self {
631 Self {
632 scheme,
633 can_sign: true,
634 }
635 }
636
637 pub fn into_scheme(self) -> Option<Arc<S>> {
639 self.can_sign.then_some(self.scheme)
640 }
641}
642
643impl<S: Scheme> Verifier for Scoped<S> {
644 type Subject<'a, D: Digest> = S::Subject<'a, D>;
645 type Faults = S::Faults;
646 type PublicKey = S::PublicKey;
647 type Certificate = S::Certificate;
648
649 fn verify_certificate<R, D>(
650 &self,
651 rng: &mut R,
652 subject: Self::Subject<'_, D>,
653 certificate: &Self::Certificate,
654 strategy: &impl Strategy,
655 ) -> bool
656 where
657 R: CryptoRng,
658 D: Digest,
659 {
660 self.scheme
661 .verify_certificate(rng, subject, certificate, strategy)
662 }
663
664 fn verify_certificates<'a, R, D, I>(
665 &self,
666 rng: &mut R,
667 certificates: NonEmpty<I>,
668 strategy: &impl Strategy,
669 ) -> bool
670 where
671 R: CryptoRng,
672 D: Digest,
673 I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
674 {
675 self.scheme.verify_certificates(rng, certificates, strategy)
676 }
677
678 fn is_batchable() -> bool {
679 S::is_batchable()
680 }
681
682 fn max_certificate_size(participants: u32) -> usize {
683 S::max_certificate_size(participants)
684 }
685
686 fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg {
687 self.scheme.certificate_codec_config()
688 }
689
690 fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg {
691 S::certificate_codec_config_unbounded()
692 }
693}
694
695pub trait Provider: Clone + Send + Sync + 'static {
700 type Scope: Clone + Send + Sync + 'static;
702 type Scheme: Scheme;
704
705 fn scoped(&self, scope: Self::Scope) -> Option<Scoped<Self::Scheme>>;
712
713 fn scheme(&self, scope: Self::Scope) -> Option<Arc<Self::Scheme>> {
719 self.scoped(scope).and_then(Scoped::into_scheme)
720 }
721}
722
723#[derive(Clone, Debug, PartialEq, Eq, Hash)]
727pub struct Signers {
728 bitmap: BitMap<1>,
729}
730
731impl Signers {
732 pub(crate) fn max_encode_size(participants: u32) -> usize {
735 BitMap::<1>::max_encode_size(u64::from(participants))
736 }
737
738 pub fn new(
743 participants: u32,
744 signers: impl IntoIterator<Item = Participant>,
745 ) -> Result<Self, AssemblyError> {
746 let mut bitmap = BitMap::zeroes(u64::from(participants));
747 for signer in signers.into_iter() {
748 let index = u64::from(signer.get());
749 if index >= bitmap.len() {
750 return Err(AssemblyError::UnknownSigner(signer));
751 }
752 if bitmap.get(index) {
753 return Err(AssemblyError::DuplicateSigner(signer));
754 }
755 bitmap.set(index, true);
756 }
757
758 Ok(Self { bitmap })
759 }
760
761 pub(crate) fn require(self, required: u32) -> Result<Self, AssemblyError> {
763 let found = u32::try_from(self.count()).expect("signer count exceeds u32::MAX");
764 if found < required {
765 return Err(AssemblyError::InsufficientAttestations(required, found));
766 }
767
768 Ok(self)
769 }
770
771 #[allow(clippy::len_without_is_empty)]
773 pub const fn len(&self) -> usize {
774 self.bitmap.len() as usize
775 }
776
777 pub fn count(&self) -> usize {
779 self.bitmap.count_ones() as usize
780 }
781
782 pub fn iter(&self) -> impl Iterator<Item = Participant> + '_ {
784 self.bitmap
785 .ones_iter()
786 .map(|index| Participant::from_usize(index as usize))
787 }
788}
789
790pub(crate) fn max_individual_certificate_size(participants: u32, signature_size: usize) -> usize {
793 Signers::max_encode_size(participants)
794 + max_items_size(Widen::widen(participants), signature_size)
795}
796
797impl<'a, P, I> TryFrom<(&'a Set<P>, I)> for Signers
803where
804 I: IntoIterator<Item = Participant>,
805{
806 type Error = AssemblyError;
807
808 fn try_from((participants, signers): (&'a Set<P>, I)) -> Result<Self, Self::Error> {
809 let total = u32::try_from(participants.len()).expect("participant count exceeds u32::MAX");
810 Self::new(total, signers)
811 }
812}
813
814impl Write for Signers {
815 fn write(&self, writer: &mut impl BufMut) {
816 self.bitmap.write(writer);
817 }
818}
819
820impl EncodeSize for Signers {
821 fn encode_size(&self) -> usize {
822 self.bitmap.encode_size()
823 }
824}
825
826impl Read for Signers {
827 type Cfg = usize;
828
829 fn read_cfg(reader: &mut impl Buf, max_participants: &usize) -> Result<Self, CodecError> {
830 let bitmap = BitMap::read_cfg(reader, &(..=*max_participants as u64).into())?;
831 Ok(Self { bitmap })
838 }
839}
840
841#[cfg(feature = "arbitrary")]
842impl arbitrary::Arbitrary<'_> for Signers {
843 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
844 let participants = u.arbitrary_len::<u8>()? % 10;
845 let signer_count = u.arbitrary_len::<u8>()?.min(participants);
846 let signers = (0..u32::try_from(signer_count).expect("signer count exceeds u32::MAX"))
847 .map(Participant::new)
848 .collect::<Vec<_>>();
849 Ok(Self::new(participants.try_into().unwrap(), signers)
850 .expect("indices are unique and in range"))
851 }
852}
853
854#[derive(Clone, Debug)]
856pub struct ConstantProvider<S: Scheme, Sc = ()> {
857 scheme: Arc<S>,
858 _scope: core::marker::PhantomData<Sc>,
859}
860
861impl<S: Scheme, Sc> ConstantProvider<S, Sc> {
862 pub fn new(scheme: S) -> Self {
864 Self {
865 scheme: Arc::new(scheme),
866 _scope: core::marker::PhantomData,
867 }
868 }
869}
870
871impl<S: Scheme, Sc: Clone + Send + Sync + 'static> crate::certificate::Provider
872 for ConstantProvider<S, Sc>
873{
874 type Scope = Sc;
875 type Scheme = S;
876
877 fn scoped(&self, _: Sc) -> Option<Scoped<S>> {
878 Some(Scoped::scheme(self.scheme.clone()))
879 }
880}
881
882#[cfg(feature = "mocks")]
883pub mod mocks;
884
885#[cfg(test)]
886mod tests {
887 use super::*;
888 #[cfg(feature = "bls12381")]
889 use crate::bls12381::{
890 dkg::feldman_desmedt as dkg,
891 primitives::{
892 group::Private as BlsPrivate,
893 ops::{compute_public, sign_proof_of_possession, verify_proof_of_possession},
894 sharing::Mode,
895 variant::{MinPk, MinSig, Variant},
896 },
897 };
898 use crate::{
899 Signer as _,
900 ed25519::{PrivateKey, Signature},
901 sha256::Digest as Sha256Digest,
902 };
903 use commonware_codec::{Decode, Encode, varint::MAX_U32_VARINT_SIZE};
904 use commonware_math::algebra::Random;
905 use commonware_parallel::Sequential;
906 use commonware_utils::{
907 N3f1, TryCollect, Widen, non_empty,
908 ordered::{Quorum as _, Set},
909 test_rng,
910 };
911 #[cfg(feature = "bls12381")]
912 use commonware_utils::{NZU32, ordered::BiMap, sync::Mutex};
913 #[cfg(feature = "bls12381")]
914 use core::sync::atomic::{AtomicUsize, Ordering};
915 use ed25519_fixture::{Scheme as Ed25519Scheme, TestSubject};
916
917 #[test]
918 fn test_max_individual_certificate_size() {
919 let mut previous = 0;
921 for n in [0, 1, 7, 8, 9, 127, 128] {
922 let signers = Signers::new(n, (0..n).map(Participant::new)).unwrap();
923 let signatures = vec![[0u8; Signature::SIZE]; Widen::widen(n)];
924 assert_eq!(Signers::max_encode_size(n), signers.encode().len());
925 let size = max_individual_certificate_size(n, Signature::SIZE);
926 assert_eq!(
927 size,
928 signers.encode().len() + MAX_U32_VARINT_SIZE + signatures.len() * Signature::SIZE
929 );
930 assert!(signers.encode().len() + signatures.encode().len() <= size);
931 assert!(size >= previous);
932 previous = size;
933 }
934
935 let n: usize = Widen::widen(u32::MAX);
937 assert_eq!(
938 max_individual_certificate_size(u32::MAX, Signature::SIZE),
939 u64::SIZE + n.div_ceil(8) + MAX_U32_VARINT_SIZE + n * Signature::SIZE,
940 );
941 }
942
943 #[test]
944 fn test_new_signers() {
945 let signers = Signers::new(6, [0, 3, 5].map(Participant::new)).unwrap();
946 let collected: Vec<_> = signers.iter().collect();
947 assert_eq!(
948 collected,
949 vec![0, 3, 5]
950 .into_iter()
951 .map(Participant::new)
952 .collect::<Vec<_>>()
953 );
954 assert_eq!(signers.count(), 3);
955 }
956
957 #[test]
958 fn test_new_out_of_bounds() {
959 assert_eq!(
960 Signers::new(4, [0, 4].map(Participant::new)),
961 Err(AssemblyError::UnknownSigner(Participant::new(4)))
962 );
963 }
964
965 #[test]
966 fn test_new_duplicate() {
967 assert_eq!(
968 Signers::new(4, [0, 0, 1].map(Participant::new)),
969 Err(AssemblyError::DuplicateSigner(Participant::new(0)))
970 );
971 }
972
973 #[test]
974 fn test_new_not_increasing() {
975 assert!(Signers::new(4, [2, 1].map(Participant::new)).is_ok());
976 }
977
978 #[test]
979 fn test_try_from_set_and_require() {
980 let participants = Set::from_iter_dedup(0..4);
981 let signers = Signers::try_from((&participants, [0, 2, 3].map(Participant::new)))
982 .unwrap()
983 .require(3)
984 .unwrap();
985 assert_eq!(signers.count(), 3);
986 assert_eq!(
987 Signers::try_from((&participants, [0, 2].map(Participant::new)))
988 .unwrap()
989 .require(3),
990 Err(AssemblyError::InsufficientAttestations(3, 2))
991 );
992 }
993
994 #[test]
995 fn test_try_from_set_checks_signer_bounds() {
996 let participants = Set::<u8>::default();
997 let signer = Participant::new(0);
998 assert_eq!(
999 Signers::try_from((&participants, [signer])),
1000 Err(AssemblyError::UnknownSigner(signer))
1001 );
1002 }
1003
1004 #[test]
1005 fn test_codec_round_trip() {
1006 let signers = Signers::new(9, [1, 6].map(Participant::new)).unwrap();
1007 let encoded = signers.encode();
1008 let decoded = Signers::decode_cfg(encoded, &9).unwrap();
1009 assert_eq!(decoded, signers);
1010 }
1011
1012 #[test]
1013 fn test_decode_respects_participant_limit() {
1014 let signers = Signers::new(8, [0, 3, 7].map(Participant::new)).unwrap();
1015 let encoded = signers.encode();
1016 assert!(Signers::decode_cfg(encoded.clone(), &2).is_err());
1018 assert!(Signers::decode_cfg(encoded.clone(), &8).is_ok());
1020 assert!(Signers::decode_cfg(encoded, &10).is_ok());
1022 }
1023
1024 mod ed25519_fixture {
1025 use crate::{certificate::Subject, impl_certificate_ed25519};
1026 use commonware_utils::N3f1;
1027
1028 #[derive(Copy, Clone, Debug)]
1030 pub struct TestSubject {
1031 pub message: &'static [u8],
1032 }
1033
1034 impl Subject for TestSubject {
1035 type Namespace = Vec<u8>;
1036
1037 fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] {
1038 derived
1039 }
1040
1041 fn message(&self) -> bytes::Bytes {
1042 bytes::Bytes::from_static(self.message)
1043 }
1044 }
1045
1046 impl_certificate_ed25519!(TestSubject, Vec<u8>, N3f1);
1048 }
1049
1050 const NAMESPACE: &[u8] = b"test-bisect";
1051 const MESSAGE: &[u8] = b"good message";
1052 const BAD_MESSAGE: &[u8] = b"bad message";
1053
1054 fn make_certificate(
1055 schemes: &[Ed25519Scheme],
1056 message: &'static [u8],
1057 ) -> <Ed25519Scheme as Verifier>::Certificate {
1058 let attestations: Vec<_> = schemes
1059 .iter()
1060 .filter_map(|s| s.sign::<Sha256Digest>(TestSubject { message }))
1061 .collect();
1062 schemes[0]
1063 .assemble(non_empty![@attestations], &Sequential)
1064 .expect("assembly failed")
1065 }
1066
1067 fn setup_ed25519(n: u32) -> (Vec<Ed25519Scheme>, Ed25519Scheme) {
1068 let mut rng = test_rng();
1069 let private_keys: Vec<_> = (0..n).map(|_| PrivateKey::random(&mut rng)).collect();
1070 let participants: Set<crate::ed25519::PublicKey> = private_keys
1071 .iter()
1072 .map(|sk| sk.public_key())
1073 .try_collect()
1074 .unwrap();
1075 let signers: Vec<_> = private_keys
1076 .into_iter()
1077 .map(|sk| Ed25519Scheme::signer(NAMESPACE, participants.clone(), sk).unwrap())
1078 .collect();
1079 let verifier = Ed25519Scheme::verifier(NAMESPACE, participants);
1080 (signers, verifier)
1081 }
1082
1083 #[test]
1086 fn membership_reports_index_and_signing() {
1087 let (signers, verifier) = setup_ed25519(4);
1088 let participants = verifier.participants().clone();
1089 let signer = signers[0].me().unwrap();
1090 let index = signer.index();
1091 let key = participants.key(index).unwrap().clone();
1092
1093 let member = Ed25519Scheme::member(NAMESPACE, participants, &key).unwrap();
1094 assert_eq!(member.me(), Some(Membership::Member(index)));
1095 assert_eq!(member.me().unwrap().index(), index);
1096 assert_eq!(member.me().unwrap().signer(), None);
1097
1098 assert_eq!(signer, Membership::Signer(index));
1099 assert_eq!(signer.signer(), Some(index));
1100
1101 assert_eq!(verifier.me(), None);
1102 }
1103
1104 #[test]
1105 fn test_bisect_empty() {
1106 let mut rng = test_rng();
1107 let (_, verifier) = setup_ed25519(4);
1108 let result =
1109 verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &[], &Sequential);
1110 assert!(result.is_empty());
1111 }
1112
1113 #[test]
1114 fn test_bisect_all_valid() {
1115 let mut rng = test_rng();
1116 let (schemes, verifier) = setup_ed25519(4);
1117 let cert = make_certificate(&schemes, MESSAGE);
1118 let good = TestSubject { message: MESSAGE };
1119 let pairs: Vec<_> = (0..5).map(|_| (good, &cert)).collect();
1120 let result =
1121 verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &pairs, &Sequential);
1122 assert_eq!(result, vec![true; 5]);
1123 }
1124
1125 #[test]
1126 fn test_bisect_mixed() {
1127 let mut rng = test_rng();
1128 let (schemes, verifier) = setup_ed25519(4);
1129 let cert = make_certificate(&schemes, MESSAGE);
1130 let good = TestSubject { message: MESSAGE };
1131 let bad = TestSubject {
1132 message: BAD_MESSAGE,
1133 };
1134 let pairs = vec![
1135 (good, &cert),
1136 (bad, &cert),
1137 (good, &cert),
1138 (bad, &cert),
1139 (good, &cert),
1140 (good, &cert),
1141 (bad, &cert),
1142 (bad, &cert),
1143 ];
1144 let expected = vec![true, false, true, false, true, true, false, false];
1145 let result =
1146 verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &pairs, &Sequential);
1147 assert_eq!(result, expected);
1148 }
1149
1150 #[test]
1151 fn test_bisect_all_invalid() {
1152 let mut rng = test_rng();
1153 let (schemes, verifier) = setup_ed25519(4);
1154 let cert = make_certificate(&schemes, MESSAGE);
1155 let bad = TestSubject {
1156 message: BAD_MESSAGE,
1157 };
1158 let pairs: Vec<_> = (0..4).map(|_| (bad, &cert)).collect();
1159 let result =
1160 verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &pairs, &Sequential);
1161 assert_eq!(result, vec![false; 4]);
1162 }
1163
1164 #[test]
1165 fn test_bisect_single_valid() {
1166 let mut rng = test_rng();
1167 let (schemes, verifier) = setup_ed25519(4);
1168 let cert = make_certificate(&schemes, MESSAGE);
1169 let pairs = vec![(TestSubject { message: MESSAGE }, &cert)];
1170 let result =
1171 verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &pairs, &Sequential);
1172 assert_eq!(result, vec![true]);
1173 }
1174
1175 #[test]
1176 fn test_bisect_single_invalid() {
1177 let mut rng = test_rng();
1178 let (schemes, verifier) = setup_ed25519(4);
1179 let cert = make_certificate(&schemes, MESSAGE);
1180 let pairs = vec![(
1181 TestSubject {
1182 message: BAD_MESSAGE,
1183 },
1184 &cert,
1185 )];
1186 let result =
1187 verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &pairs, &Sequential);
1188 assert_eq!(result, vec![false]);
1189 }
1190
1191 #[test]
1192 fn test_scoped_verifies_and_into_scheme() {
1193 let mut rng = test_rng();
1194 let (schemes, verifier) = setup_ed25519(4);
1195 let cert = make_certificate(&schemes, MESSAGE);
1196 let subject = TestSubject { message: MESSAGE };
1197
1198 let as_verifier = Scoped::verifier(Arc::new(verifier));
1199 let as_scheme = Scoped::scheme(Arc::new(schemes[0].clone()));
1200
1201 assert!(as_verifier.verify_certificate::<_, Sha256Digest>(
1203 &mut rng,
1204 subject,
1205 &cert,
1206 &Sequential,
1207 ));
1208 assert!(as_scheme.verify_certificate::<_, Sha256Digest>(
1209 &mut rng,
1210 subject,
1211 &cert,
1212 &Sequential,
1213 ));
1214 let pairs = [(subject, &cert)];
1215 assert!(as_verifier.verify_certificates::<_, Sha256Digest, _>(
1216 &mut rng,
1217 non_empty![@pairs.iter().copied()],
1218 &Sequential,
1219 ));
1220 assert_eq!(
1221 <Scoped<Ed25519Scheme> as Verifier>::is_batchable(),
1222 <Ed25519Scheme as Verifier>::is_batchable(),
1223 );
1224 assert_eq!(
1225 as_verifier.certificate_codec_config(),
1226 schemes[0].certificate_codec_config(),
1227 );
1228 for participants in [0, 4, 128] {
1229 assert_eq!(
1230 <Scoped<Ed25519Scheme> as Verifier>::max_certificate_size(participants),
1231 <Ed25519Scheme as Verifier>::max_certificate_size(participants),
1232 );
1233 }
1234 let _ = <Scoped<Ed25519Scheme> as Verifier>::certificate_codec_config_unbounded();
1235
1236 assert!(as_verifier.into_scheme().is_none());
1238 assert!(as_scheme.into_scheme().is_some());
1239
1240 let provider = ConstantProvider::<_, ()>::new(schemes[0].clone());
1241 assert!(provider.scoped(()).is_some());
1242 assert!(provider.scheme(()).is_some());
1243 }
1244
1245 #[cfg(feature = "bls12381")]
1246 #[allow(dead_code)]
1247 mod threshold {
1248 use super::TestSubject;
1249 use crate::impl_certificate_bls12381_threshold;
1250 use commonware_utils::N3f1;
1251
1252 impl_certificate_bls12381_threshold!(TestSubject, Vec<u8>, N3f1);
1253 }
1254
1255 #[cfg(feature = "bls12381")]
1256 #[allow(dead_code)]
1257 mod multisig {
1258 use super::TestSubject;
1259 use crate::impl_certificate_bls12381_multisig;
1260 use commonware_utils::N3f1;
1261
1262 impl_certificate_bls12381_multisig!(TestSubject, Vec<u8>, N3f1);
1263 }
1264
1265 const SUBJECT: TestSubject = TestSubject {
1266 message: b"subject",
1267 };
1268 const OTHER_SUBJECT: TestSubject = TestSubject {
1269 message: b"other-subject",
1270 };
1271
1272 #[cfg(feature = "bls12381")]
1273 const THRESHOLD_NAMESPACE: &[u8] =
1274 b"_COMMONWARE_CRYPTOGRAPHY_CERTIFICATE_THRESHOLD_OPTIMISTIC_ASSEMBLE";
1275 #[cfg(feature = "bls12381")]
1276 const MULTISIG_NAMESPACE: &[u8] =
1277 b"_COMMONWARE_CRYPTOGRAPHY_CERTIFICATE_MULTISIG_OPTIMISTIC_ASSEMBLE";
1278 #[cfg(feature = "bls12381")]
1279 const MULTISIG_POP_NAMESPACE: &[u8] =
1280 b"_COMMONWARE_CRYPTOGRAPHY_CERTIFICATE_MULTISIG_OPTIMISTIC_ASSEMBLE_POP";
1281
1282 #[cfg(feature = "bls12381")]
1283 fn threshold_signers<V: Variant>(
1284 rng: &mut impl CryptoRng,
1285 n: u32,
1286 ) -> Vec<threshold::Scheme<crate::ed25519::PublicKey, V>> {
1287 let participants: Set<crate::ed25519::PublicKey> = (0..n)
1288 .map(|_| PrivateKey::random(&mut *rng).public_key())
1289 .try_collect()
1290 .unwrap();
1291 let (polynomial, shares) =
1292 dkg::deal_anonymous::<V, N3f1>(&mut *rng, Mode::NonZeroCounter, NZU32!(n));
1293 shares
1294 .into_iter()
1295 .map(|share| {
1296 threshold::Scheme::signer(
1297 THRESHOLD_NAMESPACE,
1298 participants.clone(),
1299 polynomial.clone(),
1300 share,
1301 )
1302 .unwrap()
1303 })
1304 .collect()
1305 }
1306
1307 #[cfg(feature = "bls12381")]
1308 fn multisig_signers<V: Variant>(
1309 rng: &mut impl CryptoRng,
1310 n: u32,
1311 ) -> Vec<multisig::Scheme<crate::ed25519::PublicKey, V>> {
1312 let identity_keys: Vec<_> = (0..n).map(|_| PrivateKey::random(&mut *rng)).collect();
1313 let signing_keys: Vec<_> = (0..n).map(|_| BlsPrivate::random(&mut *rng)).collect();
1314 let signing_publics: Vec<_> = signing_keys.iter().map(compute_public::<V>).collect();
1315
1316 for (private, public) in signing_keys.iter().zip(&signing_publics) {
1317 let proof = sign_proof_of_possession::<V>(private, MULTISIG_POP_NAMESPACE);
1318 verify_proof_of_possession::<V>(public, MULTISIG_POP_NAMESPACE, &proof)
1319 .expect("proof of possession must verify");
1320 }
1321
1322 let participants: BiMap<_, _> = identity_keys
1323 .iter()
1324 .map(|key| key.public_key())
1325 .zip(signing_publics)
1326 .try_collect()
1327 .unwrap();
1328 signing_keys
1329 .into_iter()
1330 .map(|private| {
1331 multisig::Scheme::signer(MULTISIG_NAMESPACE, participants.clone(), private)
1332 .expect("signer must be a participant")
1333 })
1334 .collect()
1335 }
1336
1337 #[cfg(feature = "bls12381")]
1338 fn threshold_success<V: Variant>() {
1339 let mut rng = test_rng();
1340 let schemes = threshold_signers::<V>(&mut rng, 5);
1341 let quorum = N3f1::quorum(schemes.len()) as usize;
1342 let attestations: Vec<_> = schemes
1343 .iter()
1344 .take(quorum)
1345 .map(|scheme| scheme.sign::<Sha256Digest>(SUBJECT).unwrap())
1346 .collect();
1347 let expected = schemes[0]
1348 .assemble(non_empty![@attestations.clone()], &Sequential)
1349 .unwrap();
1350 let prior: Vec<_> = attestations[..1]
1351 .iter()
1352 .cloned()
1353 .map(Recording::outer)
1354 .collect();
1355 let calls = Arc::new(RecordingCalls::default());
1356 let verifier = Recording {
1357 inner: schemes[0].clone(),
1358 calls: Arc::clone(&calls),
1359 };
1360
1361 let result = verifier.optimistic_assemble::<_, Sha256Digest, _, _>(
1362 &mut rng,
1363 SUBJECT,
1364 attestations[1..].iter().cloned().map(Recording::outer),
1365 &prior,
1366 &Sequential,
1367 );
1368 let certificate = result.ok().expect("valid quorum must certify");
1369 assert_eq!(certificate, expected);
1370 assert!(calls.attestations.lock().is_empty());
1371 assert!(schemes[0].verify_certificate::<_, Sha256Digest>(
1372 &mut rng,
1373 SUBJECT,
1374 &certificate,
1375 &Sequential,
1376 ));
1377 assert!(!schemes[0].verify_certificate::<_, Sha256Digest>(
1378 &mut rng,
1379 OTHER_SUBJECT,
1380 &certificate,
1381 &Sequential,
1382 ));
1383
1384 let unused_bad = schemes[quorum].sign::<Sha256Digest>(OTHER_SUBJECT).unwrap();
1386 let pending: Vec<_> = attestations
1387 .into_iter()
1388 .chain([unused_bad])
1389 .map(Recording::outer)
1390 .collect();
1391 let result = verifier.optimistic_assemble::<_, Sha256Digest, _, _>(
1392 &mut rng,
1393 SUBJECT,
1394 pending,
1395 std::iter::empty(),
1396 &Sequential,
1397 );
1398 assert_eq!(result.ok(), Some(expected));
1399 assert!(calls.attestations.lock().is_empty());
1400 }
1401
1402 #[cfg(feature = "bls12381")]
1403 #[test]
1404 fn test_threshold_recovers_exact_certificate_with_verified_attestations() {
1405 threshold_success::<MinPk>();
1406 threshold_success::<MinSig>();
1407 }
1408
1409 #[cfg(feature = "bls12381")]
1410 fn multisig_optimistic_assemble_uses_one_certificate_check<V: Variant>() {
1411 let mut rng = test_rng();
1412 let schemes = multisig_signers::<V>(&mut rng, 5);
1413 let quorum = N3f1::quorum(schemes.len()) as usize;
1414 let attestations: Vec<_> = schemes
1415 .iter()
1416 .take(quorum)
1417 .map(|scheme| scheme.sign::<Sha256Digest>(SUBJECT).unwrap())
1418 .collect();
1419 let prior: Vec<_> = attestations[..1]
1420 .iter()
1421 .cloned()
1422 .map(Recording::outer)
1423 .collect();
1424 let calls = Arc::new(RecordingCalls::default());
1425 let verifier = Recording {
1426 inner: schemes[0].clone(),
1427 calls: Arc::clone(&calls),
1428 };
1429
1430 let certificate = verifier
1431 .optimistic_assemble::<_, Sha256Digest, _, _>(
1432 &mut rng,
1433 SUBJECT,
1434 attestations[1..].iter().cloned().map(Recording::outer),
1435 &prior,
1436 &Sequential,
1437 )
1438 .ok()
1439 .expect("valid mixed quorum must certify");
1440
1441 assert_eq!(calls.certificates.load(Ordering::Relaxed), 1);
1442 assert!(calls.attestations.lock().is_empty());
1443 assert!(schemes[0].verify_certificate::<_, Sha256Digest>(
1444 &mut rng,
1445 SUBJECT,
1446 &certificate,
1447 &Sequential,
1448 ));
1449 assert!(!schemes[0].verify_certificate::<_, Sha256Digest>(
1450 &mut rng,
1451 OTHER_SUBJECT,
1452 &certificate,
1453 &Sequential,
1454 ));
1455 }
1456
1457 #[cfg(feature = "bls12381")]
1458 #[test]
1459 fn test_multisig_optimistic_assemble_uses_one_certificate_check() {
1460 multisig_optimistic_assemble_uses_one_certificate_check::<MinPk>();
1461 multisig_optimistic_assemble_uses_one_certificate_check::<MinSig>();
1462 }
1463
1464 #[test]
1465 fn test_optimistic_assemble_individual_inputs() {
1466 let mut rng = test_rng();
1467 let (schemes, verifier) = setup_ed25519(5);
1468 let empty = verifier.optimistic_assemble::<_, Sha256Digest, _, _>(
1469 &mut rng,
1470 SUBJECT,
1471 Vec::new(),
1472 std::iter::empty(),
1473 &Sequential,
1474 );
1475 let verification = empty.unwrap_err();
1476 assert!(verification.verified.is_empty());
1477 assert!(verification.invalid.is_empty());
1478
1479 let pending: Vec<_> = schemes
1480 .iter()
1481 .map(|scheme| scheme.sign::<Sha256Digest>(SUBJECT).unwrap())
1482 .collect();
1483 let certificate = verifier
1484 .optimistic_assemble::<_, Sha256Digest, _, _>(
1485 &mut rng,
1486 SUBJECT,
1487 pending,
1488 std::iter::empty(),
1489 &Sequential,
1490 )
1491 .ok()
1492 .expect("valid quorum must certify");
1493 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1494 &mut rng,
1495 SUBJECT,
1496 &certificate,
1497 &Sequential,
1498 ));
1499
1500 let pending: Vec<_> = schemes
1501 .iter()
1502 .enumerate()
1503 .map(|(i, scheme)| {
1504 scheme
1505 .sign::<Sha256Digest>(if i == 0 { OTHER_SUBJECT } else { SUBJECT })
1506 .unwrap()
1507 })
1508 .collect();
1509 let signers: Vec<_> = pending.iter().map(|a| a.signer).collect();
1510 let result = verifier.optimistic_assemble::<_, Sha256Digest, _, _>(
1511 &mut rng,
1512 SUBJECT,
1513 pending,
1514 std::iter::empty(),
1515 &Sequential,
1516 );
1517 let verification = result.unwrap_err();
1518 assert_eq!(verification.invalid, vec![signers[0]]);
1519 assert_eq!(
1520 verification
1521 .verified
1522 .iter()
1523 .map(|a| a.signer)
1524 .collect::<Vec<_>>(),
1525 signers[1..]
1526 );
1527 let certificate = verifier
1528 .assemble(non_empty![@verification.verified], &Sequential)
1529 .unwrap();
1530 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1531 &mut rng,
1532 SUBJECT,
1533 &certificate,
1534 &Sequential,
1535 ));
1536 }
1537
1538 #[test]
1539 fn test_optimistic_assemble_individual_quorum() {
1540 let mut rng = test_rng();
1541 let (schemes, verifier) = setup_ed25519(5);
1542 let quorum = N3f1::quorum(schemes.len()) as usize;
1543 let attestations: Vec<_> = schemes
1544 .iter()
1545 .take(quorum)
1546 .map(|s| s.sign::<Sha256Digest>(SUBJECT).unwrap())
1547 .collect();
1548
1549 for count in [0, quorum - 1, quorum] {
1552 for pending in [0, count] {
1553 let result = verifier.optimistic_assemble::<_, Sha256Digest, _, _>(
1554 &mut rng,
1555 SUBJECT,
1556 attestations[..pending].iter().cloned(),
1557 &attestations[pending..count],
1558 &Sequential,
1559 );
1560 assert_eq!(result.is_ok(), count == quorum);
1561 match result {
1562 Ok(certificate) => assert!(verifier.verify_certificate::<_, Sha256Digest>(
1563 &mut rng,
1564 SUBJECT,
1565 &certificate,
1566 &Sequential,
1567 )),
1568 Err(verification) => {
1569 assert_eq!(verification.verified, attestations[..pending]);
1570 assert!(verification.invalid.is_empty());
1571 }
1572 }
1573 }
1574 }
1575 }
1576
1577 #[test]
1578 fn test_optimistic_assemble_empty_committee() {
1579 let mut rng = test_rng();
1580 let (_, verifier) = setup_ed25519(0);
1581 let (schemes, _) = setup_ed25519(1);
1582 let unknown = schemes[0].sign::<Sha256Digest>(SUBJECT).unwrap();
1583 for pending in [Vec::new(), vec![unknown]] {
1584 let expected: Vec<_> = pending.iter().map(|a| a.signer).collect();
1585 let result = verifier.optimistic_assemble::<_, Sha256Digest, _, _>(
1586 &mut rng,
1587 SUBJECT,
1588 pending,
1589 std::iter::empty(),
1590 &Sequential,
1591 );
1592 let verification = result.unwrap_err();
1593 assert!(verification.verified.is_empty());
1594 assert_eq!(verification.invalid, expected);
1595 }
1596 }
1597
1598 #[cfg(feature = "bls12381")]
1599 #[derive(Debug, Default)]
1600 struct RecordingCalls {
1601 attestations: Mutex<Vec<Vec<Participant>>>,
1602 assemblies: AtomicUsize,
1603 certificates: AtomicUsize,
1604 }
1605
1606 #[cfg(feature = "bls12381")]
1607 #[derive(Clone, Debug)]
1608 struct Recording<S> {
1609 inner: S,
1610 calls: Arc<RecordingCalls>,
1611 }
1612
1613 #[cfg(feature = "bls12381")]
1614 impl<S: Scheme> Recording<S> {
1615 fn inner(attestation: Attestation<Self>) -> Attestation<S> {
1616 Attestation {
1617 signer: attestation.signer,
1618 signature: attestation.signature,
1619 }
1620 }
1621
1622 fn outer(attestation: Attestation<S>) -> Attestation<Self> {
1623 Attestation {
1624 signer: attestation.signer,
1625 signature: attestation.signature,
1626 }
1627 }
1628 }
1629
1630 #[cfg(feature = "bls12381")]
1631 impl<S: Scheme> Verifier for Recording<S> {
1632 type Subject<'a, D: Digest> = S::Subject<'a, D>;
1633 type Faults = S::Faults;
1634 type PublicKey = S::PublicKey;
1635 type Certificate = S::Certificate;
1636
1637 fn verify_certificate<R, D>(
1638 &self,
1639 rng: &mut R,
1640 subject: Self::Subject<'_, D>,
1641 certificate: &Self::Certificate,
1642 strategy: &impl Strategy,
1643 ) -> bool
1644 where
1645 R: CryptoRng,
1646 D: Digest,
1647 {
1648 self.calls.certificates.fetch_add(1, Ordering::Relaxed);
1649 self.inner
1650 .verify_certificate(rng, subject, certificate, strategy)
1651 }
1652
1653 fn is_batchable() -> bool {
1654 S::is_batchable()
1655 }
1656
1657 fn max_certificate_size(participants: u32) -> usize {
1658 S::max_certificate_size(participants)
1659 }
1660
1661 fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg {
1662 self.inner.certificate_codec_config()
1663 }
1664
1665 fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg {
1666 S::certificate_codec_config_unbounded()
1667 }
1668 }
1669
1670 #[cfg(feature = "bls12381")]
1671 impl<S: Scheme> Scheme for Recording<S> {
1672 type Signature = S::Signature;
1673
1674 fn me(&self) -> Option<Membership> {
1675 self.inner.me()
1676 }
1677
1678 fn participants(&self) -> &Set<Self::PublicKey> {
1679 self.inner.participants()
1680 }
1681
1682 fn sign<D: Digest>(&self, subject: Self::Subject<'_, D>) -> Option<Attestation<Self>> {
1683 self.inner.sign(subject).map(Self::outer)
1684 }
1685
1686 fn verify_attestation<R, D>(
1687 &self,
1688 rng: &mut R,
1689 subject: Self::Subject<'_, D>,
1690 attestation: &Attestation<Self>,
1691 strategy: &impl Strategy,
1692 ) -> bool
1693 where
1694 R: CryptoRng,
1695 D: Digest,
1696 {
1697 self.calls
1698 .attestations
1699 .lock()
1700 .push(vec![attestation.signer]);
1701 self.inner
1702 .verify_attestation(rng, subject, &Self::inner(attestation.clone()), strategy)
1703 }
1704
1705 fn verify_attestations<R, D, I>(
1706 &self,
1707 rng: &mut R,
1708 subject: Self::Subject<'_, D>,
1709 attestations: I,
1710 strategy: &impl Strategy,
1711 ) -> Verification<Self>
1712 where
1713 R: CryptoRng,
1714 D: Digest,
1715 I: IntoIterator<Item = Attestation<Self>>,
1716 I::IntoIter: Send,
1717 {
1718 let attestations: Vec<_> = attestations.into_iter().collect();
1719 self.calls
1720 .attestations
1721 .lock()
1722 .push(attestations.iter().map(|a| a.signer).collect());
1723 let result = self.inner.verify_attestations(
1724 rng,
1725 subject,
1726 attestations.into_iter().map(Self::inner),
1727 strategy,
1728 );
1729 Verification::new(
1730 result.verified.into_iter().map(Self::outer).collect(),
1731 result.invalid,
1732 )
1733 }
1734
1735 fn optimistic_assemble<'a, R, D, I, J>(
1736 &self,
1737 rng: &mut R,
1738 subject: Self::Subject<'_, D>,
1739 pending: I,
1740 verified: J,
1741 strategy: &impl Strategy,
1742 ) -> Result<Self::Certificate, Verification<Self>>
1743 where
1744 R: CryptoRng,
1745 D: Digest,
1746 I: IntoIterator<Item = Attestation<Self>>,
1747 I::IntoIter: ExactSizeIterator + Send,
1748 J: IntoIterator<Item = &'a Attestation<Self>>,
1749 J::IntoIter: Send,
1750 {
1751 crate::certificate::optimistic_assemble::<Self, _, D, _, _>(
1752 self, rng, subject, pending, verified, strategy,
1753 )
1754 }
1755
1756 fn assemble<I>(
1757 &self,
1758 attestations: NonEmpty<I>,
1759 strategy: &impl Strategy,
1760 ) -> Result<Self::Certificate, AssemblyError>
1761 where
1762 I: Iterator<Item = Attestation<Self>> + Send,
1763 {
1764 self.calls.assemblies.fetch_add(1, Ordering::Relaxed);
1765 self.inner.assemble(
1766 non_empty![@attestations.into_iter().map(Self::inner)],
1767 strategy,
1768 )
1769 }
1770
1771 fn is_attributable() -> bool {
1772 S::is_attributable()
1773 }
1774 }
1775
1776 #[cfg(feature = "bls12381")]
1777 fn expected_halves(len: usize) -> Vec<Vec<Participant>> {
1778 let middle = len.div_ceil(2);
1779 let first = (0..middle).map(Participant::from_usize).collect();
1780 if middle == len {
1781 vec![first]
1782 } else {
1783 vec![first, (middle..len).map(Participant::from_usize).collect()]
1784 }
1785 }
1786
1787 #[cfg(feature = "bls12381")]
1788 #[test]
1789 fn test_rejected_certificate_verifies_pending_halves_directly() {
1790 for n in [1, 5, 7] {
1791 let mut rng = test_rng();
1792 let schemes = threshold_signers::<MinPk>(&mut rng, n);
1793 let calls = Arc::new(RecordingCalls::default());
1794 let verifier = Recording {
1795 inner: schemes[0].clone(),
1796 calls: Arc::clone(&calls),
1797 };
1798 let quorum = N3f1::quorum(n) as usize;
1799 let pending: Vec<_> = schemes
1800 .iter()
1801 .take(quorum)
1802 .map(|s| Recording::outer(s.sign::<Sha256Digest>(OTHER_SUBJECT).unwrap()))
1803 .collect();
1804 let result = verifier.optimistic_assemble::<_, Sha256Digest, _, _>(
1805 &mut rng,
1806 SUBJECT,
1807 pending,
1808 std::iter::empty(),
1809 &Sequential,
1810 );
1811 let verification = result.unwrap_err();
1812 assert!(verification.verified.is_empty());
1813 assert_eq!(
1814 verification.invalid,
1815 (0..quorum).map(Participant::from_usize).collect::<Vec<_>>()
1816 );
1817 assert_eq!(*calls.attestations.lock(), expected_halves(quorum));
1818 }
1819 }
1820
1821 #[cfg(feature = "bls12381")]
1822 #[test]
1823 fn test_malformed_assembly_falls_back_to_pending_halves() {
1824 let mut rng = test_rng();
1825 let schemes = threshold_signers::<MinPk>(&mut rng, 5);
1826 let quorum = N3f1::quorum(schemes.len()) as usize;
1827 for count in [quorum, schemes.len()] {
1828 let calls = Arc::new(RecordingCalls::default());
1829 let verifier = Recording {
1830 inner: schemes[0].clone(),
1831 calls: Arc::clone(&calls),
1832 };
1833 let mut pending: Vec<_> = schemes
1834 .iter()
1835 .take(count)
1836 .map(|s| Recording::outer(s.sign::<Sha256Digest>(SUBJECT).unwrap()))
1837 .collect();
1838 let mut malformed = Bytes::from_static(&[0]);
1839 pending[0].signature = Lazy::deferred(&mut malformed, ());
1840 let result = verifier.optimistic_assemble::<_, Sha256Digest, _, _>(
1841 &mut rng,
1842 SUBJECT,
1843 pending,
1844 std::iter::empty(),
1845 &Sequential,
1846 );
1847 let verification = result.unwrap_err();
1848 assert_eq!(verification.verified.len(), count - 1);
1849 assert_eq!(verification.invalid, vec![Participant::new(0)]);
1850 assert_eq!(*calls.attestations.lock(), expected_halves(count));
1851 if count > quorum {
1852 let certificate = verifier
1853 .assemble(non_empty![@verification.verified], &Sequential)
1854 .unwrap();
1855 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1856 &mut rng,
1857 SUBJECT,
1858 &certificate,
1859 &Sequential,
1860 ));
1861 }
1862 }
1863 }
1864
1865 #[cfg(feature = "bls12381")]
1866 #[test]
1867 fn test_optimistic_assemble_retains_verified_inputs() {
1868 let mut rng = test_rng();
1869 let schemes = threshold_signers::<MinPk>(&mut rng, 5);
1870 let mut prior: Vec<_> = schemes[..2]
1871 .iter()
1872 .map(|s| Recording::outer(s.sign::<Sha256Digest>(SUBJECT).unwrap()))
1873 .collect();
1874 let accepted = Recording::outer(schemes[2].sign::<Sha256Digest>(SUBJECT).unwrap());
1875 let rejected = Recording::outer(schemes[3].sign::<Sha256Digest>(OTHER_SUBJECT).unwrap());
1876 let invalid = rejected.signer;
1877 let calls = Arc::new(RecordingCalls::default());
1878 let verifier = Recording {
1879 inner: schemes[0].clone(),
1880 calls: Arc::clone(&calls),
1881 };
1882 let result = verifier.optimistic_assemble::<_, Sha256Digest, _, _>(
1883 &mut rng,
1884 SUBJECT,
1885 vec![accepted.clone(), rejected],
1886 &prior,
1887 &Sequential,
1888 );
1889 let verification = result.unwrap_err();
1890 assert_eq!(verification.verified, vec![accepted.clone()]);
1891 assert_eq!(verification.invalid, vec![invalid]);
1892 assert_eq!(
1893 *calls.attestations.lock(),
1894 vec![vec![accepted.signer], vec![invalid]]
1895 );
1896
1897 prior.extend(verification.verified);
1898 calls.attestations.lock().clear();
1899 let replacement = Recording::outer(schemes[4].sign::<Sha256Digest>(SUBJECT).unwrap());
1900 let signer = replacement.signer;
1901 let verification = verifier.verify_attestations::<_, Sha256Digest, _>(
1902 &mut rng,
1903 SUBJECT,
1904 vec![replacement.clone()],
1905 &Sequential,
1906 );
1907 assert_eq!(verification.verified, vec![replacement]);
1908 assert!(verification.invalid.is_empty());
1909 assert_eq!(*calls.attestations.lock(), vec![vec![signer]]);
1910 prior.extend(verification.verified);
1911 let certificate = verifier.assemble(non_empty![@prior], &Sequential).unwrap();
1912 assert!(verifier.verify_certificate::<_, Sha256Digest>(
1913 &mut rng,
1914 SUBJECT,
1915 &certificate,
1916 &Sequential,
1917 ));
1918 }
1919
1920 #[cfg(feature = "bls12381")]
1921 #[test]
1922 fn test_optimistic_assemble_with_verified_votes() {
1923 let mut rng = test_rng();
1924 let schemes = threshold_signers::<MinPk>(&mut rng, 5);
1925 let quorum = N3f1::quorum(schemes.len()) as usize;
1926 let attestations: Vec<_> = schemes[..quorum]
1927 .iter()
1928 .map(|scheme| scheme.sign::<Sha256Digest>(SUBJECT).unwrap())
1929 .collect();
1930
1931 for pending_len in [0, 1, 2] {
1932 let (prior, pending) = attestations.split_at(quorum - pending_len);
1935 let prior = schemes[0].verify_attestations::<_, Sha256Digest, _>(
1936 &mut rng,
1937 SUBJECT,
1938 prior.iter().cloned(),
1939 &Sequential,
1940 );
1941 assert!(prior.invalid.is_empty());
1942 let prior: Vec<_> = prior.verified.into_iter().map(Recording::outer).collect();
1943 let calls = Arc::new(RecordingCalls::default());
1944 let verifier = Recording {
1945 inner: schemes[0].clone(),
1946 calls: Arc::clone(&calls),
1947 };
1948 let certificate = verifier
1949 .optimistic_assemble::<_, Sha256Digest, _, _>(
1950 &mut rng,
1951 SUBJECT,
1952 pending.iter().cloned().map(Recording::outer),
1953 &prior,
1954 &Sequential,
1955 )
1956 .ok()
1957 .expect("verified quorum must certify");
1958
1959 assert_eq!(calls.assemblies.load(Ordering::Relaxed), 1);
1962 assert_eq!(
1963 calls.certificates.load(Ordering::Relaxed),
1964 usize::from(pending_len != 0)
1965 );
1966 assert!(calls.attestations.lock().is_empty());
1967 assert!(schemes[0].verify_certificate::<_, Sha256Digest>(
1968 &mut rng,
1969 SUBJECT,
1970 &certificate,
1971 &Sequential,
1972 ));
1973 }
1974 }
1975
1976 #[cfg(feature = "bls12381")]
1977 #[test]
1978 fn test_optimistic_assemble_retries_with_replacements() {
1979 let mut rng = test_rng();
1980 let schemes = threshold_signers::<MinPk>(&mut rng, 10);
1981 let quorum = N3f1::quorum(schemes.len()) as usize;
1982
1983 for failures in [1, 2] {
1984 let calls = Arc::new(RecordingCalls::default());
1985 let verifier = Recording {
1986 inner: schemes[0].clone(),
1987 calls: Arc::clone(&calls),
1988 };
1989
1990 let own = schemes[0].verify_attestations::<_, Sha256Digest, _>(
1993 &mut rng,
1994 SUBJECT,
1995 [schemes[0].sign::<Sha256Digest>(SUBJECT).unwrap()],
1996 &Sequential,
1997 );
1998 assert!(own.invalid.is_empty());
1999 let mut prior: Vec<_> = own.verified.into_iter().map(Recording::outer).collect();
2000 let pending: Vec<_> = schemes[1..quorum]
2001 .iter()
2002 .enumerate()
2003 .map(|(index, scheme)| {
2004 let subject = if index + 1 >= quorum - failures {
2005 OTHER_SUBJECT
2006 } else {
2007 SUBJECT
2008 };
2009 Recording::outer(scheme.sign::<Sha256Digest>(subject).unwrap())
2010 })
2011 .collect();
2012 let result = verifier
2013 .optimistic_assemble::<_, Sha256Digest, _, _>(
2014 &mut rng,
2015 SUBJECT,
2016 pending,
2017 &prior,
2018 &Sequential,
2019 )
2020 .unwrap_err();
2021 assert_eq!(result.invalid.len(), failures);
2022 prior.extend(result.verified);
2023 assert_eq!(prior.len(), quorum - failures);
2024
2025 let pending: Vec<_> = schemes[quorum..quorum + failures]
2028 .iter()
2029 .enumerate()
2030 .map(|(index, scheme)| {
2031 let subject = if index + 1 == failures {
2032 OTHER_SUBJECT
2033 } else {
2034 SUBJECT
2035 };
2036 Recording::outer(scheme.sign::<Sha256Digest>(subject).unwrap())
2037 })
2038 .collect();
2039 let signers: Vec<_> = pending.iter().map(|vote| vote.signer).collect();
2040 let invalid = *signers.last().unwrap();
2041 calls.attestations.lock().clear();
2042 calls.assemblies.store(0, Ordering::Relaxed);
2043 calls.certificates.store(0, Ordering::Relaxed);
2044 let result = verifier
2045 .optimistic_assemble::<_, Sha256Digest, _, _>(
2046 &mut rng,
2047 SUBJECT,
2048 pending,
2049 &prior,
2050 &Sequential,
2051 )
2052 .unwrap_err();
2053 assert_eq!(result.invalid, vec![invalid]);
2054 assert_eq!(result.verified.len(), failures - 1);
2055 assert_eq!(calls.assemblies.load(Ordering::Relaxed), 1);
2056 assert_eq!(calls.certificates.load(Ordering::Relaxed), 1);
2057 assert_eq!(
2058 *calls.attestations.lock(),
2059 signers
2060 .into_iter()
2061 .map(|signer| vec![signer])
2062 .collect::<Vec<_>>()
2063 );
2064 prior.extend(result.verified);
2065 assert_eq!(prior.len(), quorum - 1);
2066
2067 let replacement = Recording::outer(
2069 schemes[quorum + failures]
2070 .sign::<Sha256Digest>(SUBJECT)
2071 .unwrap(),
2072 );
2073 calls.attestations.lock().clear();
2074 calls.assemblies.store(0, Ordering::Relaxed);
2075 calls.certificates.store(0, Ordering::Relaxed);
2076 let certificate = verifier
2077 .optimistic_assemble::<_, Sha256Digest, _, _>(
2078 &mut rng,
2079 SUBJECT,
2080 [replacement],
2081 &prior,
2082 &Sequential,
2083 )
2084 .ok()
2085 .expect("valid replacement must certify");
2086 assert_eq!(calls.assemblies.load(Ordering::Relaxed), 1);
2087 assert_eq!(calls.certificates.load(Ordering::Relaxed), 1);
2088 assert!(calls.attestations.lock().is_empty());
2089 assert!(schemes[0].verify_certificate::<_, Sha256Digest>(
2090 &mut rng,
2091 SUBJECT,
2092 &certificate,
2093 &Sequential,
2094 ));
2095 assert!(!schemes[0].verify_certificate::<_, Sha256Digest>(
2096 &mut rng,
2097 OTHER_SUBJECT,
2098 &certificate,
2099 &Sequential,
2100 ));
2101 }
2102 }
2103
2104 #[cfg(feature = "bls12381")]
2105 #[test]
2106 fn test_optimistic_assemble_below_quorum() {
2107 let mut rng = test_rng();
2108 let schemes = threshold_signers::<MinPk>(&mut rng, 5);
2109 let pending: Vec<_> = schemes[..2]
2110 .iter()
2111 .map(|s| Recording::outer(s.sign::<Sha256Digest>(SUBJECT).unwrap()))
2112 .collect();
2113 let expected = pending.clone();
2114 let calls = Arc::new(RecordingCalls::default());
2115 let verifier = Recording {
2116 inner: schemes[0].clone(),
2117 calls: Arc::clone(&calls),
2118 };
2119 let result = verifier.optimistic_assemble::<_, Sha256Digest, _, _>(
2120 &mut rng,
2121 SUBJECT,
2122 pending,
2123 std::iter::empty(),
2124 &Sequential,
2125 );
2126 let verification = result.unwrap_err();
2127 assert_eq!(verification.verified, expected);
2128 assert!(verification.invalid.is_empty());
2129 assert_eq!(*calls.attestations.lock(), expected_halves(expected.len()));
2130 }
2131
2132 #[cfg(feature = "bls12381")]
2133 #[test]
2134 fn test_optimistic_assemble_unknown_pending_signer() {
2135 let mut rng = test_rng();
2136 let schemes = threshold_signers::<MinPk>(&mut rng, 5);
2137 let quorum = N3f1::quorum(schemes.len()) as usize;
2138 let mut pending: Vec<_> = schemes
2139 .iter()
2140 .take(quorum)
2141 .map(|s| Recording::outer(s.sign::<Sha256Digest>(SUBJECT).unwrap()))
2142 .collect();
2143 pending[0].signer = Participant::new(999);
2144 let calls = Arc::new(RecordingCalls::default());
2145 let verifier = Recording {
2146 inner: schemes[0].clone(),
2147 calls: Arc::clone(&calls),
2148 };
2149 let result = verifier.optimistic_assemble::<_, Sha256Digest, _, _>(
2150 &mut rng,
2151 SUBJECT,
2152 pending,
2153 std::iter::empty(),
2154 &Sequential,
2155 );
2156 let verification = result.unwrap_err();
2157 assert_eq!(verification.verified.len(), quorum - 1);
2158 assert_eq!(verification.invalid, vec![Participant::new(999)]);
2159 assert_eq!(
2160 *calls.attestations.lock(),
2161 vec![
2162 vec![Participant::new(999), Participant::new(1)],
2163 vec![Participant::new(2), Participant::new(3)],
2164 ]
2165 );
2166 }
2167
2168 #[cfg(feature = "arbitrary")]
2169 mod conformance {
2170 use super::{ed25519_fixture::Scheme, *};
2171 use commonware_codec::conformance::CodecConformance;
2172
2173 commonware_conformance::conformance_tests! {
2174 CodecConformance<Signers>,
2175 CodecConformance<Attestation<Scheme>>,
2176 }
2177 }
2178}