commonware_cryptography/bls12381/certificate/multisig/
mod.rs

1//! BLS12-381 multi-signature signing scheme implementation.
2//!
3//! This module provides both the generic BLS12-381 multisig implementation and a macro to generate
4//! protocol-specific wrappers.
5
6#[cfg(feature = "mocks")]
7pub mod mocks;
8
9use crate::{
10    bls12381::primitives::{
11        group::Private,
12        ops::{
13            aggregate_signatures, aggregate_verify_multiple_public_keys, compute_public,
14            sign_message, verify_message,
15        },
16        variant::Variant,
17    },
18    certificate::{Attestation, Scheme, Signers, Subject, Verification},
19    Digest, PublicKey,
20};
21#[cfg(not(feature = "std"))]
22use alloc::{collections::BTreeSet, vec::Vec};
23use bytes::{Buf, BufMut};
24use commonware_codec::{EncodeSize, Error, Read, ReadExt, Write};
25use commonware_utils::ordered::{BiMap, Quorum, Set};
26use rand::{CryptoRng, Rng};
27#[cfg(feature = "std")]
28use std::collections::BTreeSet;
29
30/// Generic BLS12-381 multi-signature implementation.
31///
32/// This struct contains the core cryptographic operations without protocol-specific
33/// context types. It can be reused across different protocols (simplex, aggregation, etc.)
34/// by wrapping it with protocol-specific trait implementations via the macro.
35#[derive(Clone, Debug)]
36pub struct Generic<P: PublicKey, V: Variant> {
37    /// Participants in the committee.
38    pub participants: BiMap<P, V::Public>,
39    /// Key used for generating signatures.
40    pub signer: Option<(u32, Private)>,
41}
42
43impl<P: PublicKey, V: Variant> Generic<P, V> {
44    /// Creates a new scheme instance with the provided key material.
45    ///
46    /// Participants have both an identity key and a signing key. The identity key
47    /// is used for participant set ordering and indexing, while the signing key is used for
48    /// signing and verification.
49    ///
50    /// Returns `None` if the provided private key does not match any signing key
51    /// in the participant set.
52    pub fn signer(participants: BiMap<P, V::Public>, private_key: Private) -> Option<Self> {
53        let public_key = compute_public::<V>(&private_key);
54        let signer = participants
55            .values()
56            .iter()
57            .position(|p| p == &public_key)
58            .map(|index| (index as u32, private_key))?;
59
60        Some(Self {
61            participants,
62            signer: Some(signer),
63        })
64    }
65
66    /// Builds a verifier that can authenticate signatures and certificates.
67    ///
68    /// Participants have both an identity key and a signing key. The identity key
69    /// is used for participant set ordering and indexing, while the signing key is used for
70    /// verification.
71    pub const fn verifier(participants: BiMap<P, V::Public>) -> Self {
72        Self {
73            participants,
74            signer: None,
75        }
76    }
77
78    /// Returns the ordered set of identity keys.
79    pub const fn participants(&self) -> &Set<P> {
80        self.participants.keys()
81    }
82
83    /// Returns the index of "self" in the participant set, if available.
84    pub fn me(&self) -> Option<u32> {
85        self.signer.as_ref().map(|(index, _)| *index)
86    }
87
88    /// Signs a subject and returns the attestation.
89    pub fn sign<S, D>(&self, namespace: &[u8], subject: S::Subject<'_, D>) -> Option<Attestation<S>>
90    where
91        S: Scheme<Signature = V::Signature>,
92        D: Digest,
93    {
94        let (index, private_key) = self.signer.as_ref()?;
95
96        let (namespace, message) = subject.namespace_and_message(namespace);
97        let signature = sign_message::<V>(private_key, Some(namespace.as_ref()), message.as_ref());
98
99        Some(Attestation {
100            signer: *index,
101            signature,
102        })
103    }
104
105    /// Verifies a single attestation from a signer.
106    pub fn verify_attestation<S, D>(
107        &self,
108        namespace: &[u8],
109        subject: S::Subject<'_, D>,
110        attestation: &Attestation<S>,
111    ) -> bool
112    where
113        S: Scheme<Signature = V::Signature>,
114        D: Digest,
115    {
116        let Some(public_key) = self.participants.value(attestation.signer as usize) else {
117            return false;
118        };
119
120        let (namespace, message) = subject.namespace_and_message(namespace);
121        verify_message::<V>(
122            public_key,
123            Some(namespace.as_ref()),
124            message.as_ref(),
125            &attestation.signature,
126        )
127        .is_ok()
128    }
129
130    /// Batch-verifies attestations and returns verified attestations and invalid signers.
131    pub fn verify_attestations<S, R, D, I>(
132        &self,
133        _rng: &mut R,
134        namespace: &[u8],
135        subject: S::Subject<'_, D>,
136        attestations: I,
137    ) -> Verification<S>
138    where
139        S: Scheme<Signature = V::Signature>,
140        R: Rng + CryptoRng,
141        D: Digest,
142        I: IntoIterator<Item = Attestation<S>>,
143    {
144        let mut invalid = BTreeSet::new();
145        let mut candidates = Vec::new();
146        let mut publics = Vec::new();
147        let mut sigs = Vec::new();
148        for attestation in attestations.into_iter() {
149            let Some(public_key) = self.participants.value(attestation.signer as usize) else {
150                invalid.insert(attestation.signer);
151                continue;
152            };
153
154            publics.push(*public_key);
155            sigs.push(attestation.signature);
156            candidates.push(attestation);
157        }
158
159        // If there are no candidates to verify, return before doing any work.
160        if candidates.is_empty() {
161            return Verification::new(candidates, invalid.into_iter().collect());
162        }
163
164        // Verify the aggregate signature.
165        let (namespace, message) = subject.namespace_and_message(namespace);
166        if aggregate_verify_multiple_public_keys::<V, _>(
167            publics.iter(),
168            Some(namespace.as_ref()),
169            message.as_ref(),
170            &aggregate_signatures::<V, _>(sigs.iter()),
171        )
172        .is_err()
173        {
174            for (attestation, public_key) in candidates.iter().zip(publics.iter()) {
175                if verify_message::<V>(
176                    public_key,
177                    Some(namespace.as_ref()),
178                    message.as_ref(),
179                    &attestation.signature,
180                )
181                .is_err()
182                {
183                    invalid.insert(attestation.signer);
184                }
185            }
186        }
187
188        // Collect the verified attestations.
189        let verified = candidates
190            .into_iter()
191            .filter(|attestation| !invalid.contains(&attestation.signer))
192            .collect();
193
194        Verification::new(verified, invalid.into_iter().collect())
195    }
196
197    /// Assembles a certificate from a collection of attestations.
198    pub fn assemble<S, I>(&self, attestations: I) -> Option<Certificate<V>>
199    where
200        S: Scheme<Signature = V::Signature>,
201        I: IntoIterator<Item = Attestation<S>>,
202    {
203        // Collect the signers and signatures.
204        let mut entries = Vec::new();
205        for Attestation { signer, signature } in attestations {
206            if signer as usize >= self.participants.len() {
207                return None;
208            }
209
210            entries.push((signer, signature));
211        }
212        if entries.len() < self.participants.quorum() as usize {
213            return None;
214        }
215
216        // Produce signers and aggregate signature.
217        let (signers, signatures): (Vec<_>, Vec<_>) = entries.into_iter().unzip();
218        let signers = Signers::from(self.participants.len(), signers);
219        let signature = aggregate_signatures::<V, _>(signatures.iter());
220
221        Some(Certificate { signers, signature })
222    }
223
224    /// Verifies a certificate.
225    pub fn verify_certificate<S, R, D>(
226        &self,
227        _rng: &mut R,
228        namespace: &[u8],
229        subject: S::Subject<'_, D>,
230        certificate: &Certificate<V>,
231    ) -> bool
232    where
233        S: Scheme,
234        R: Rng + CryptoRng,
235        D: Digest,
236    {
237        // If the certificate signers length does not match the participant set, return false.
238        if certificate.signers.len() != self.participants.len() {
239            return false;
240        }
241
242        // If the certificate does not meet the quorum, return false.
243        if certificate.signers.count() < self.participants.quorum() as usize {
244            return false;
245        }
246
247        // Collect the public keys.
248        let mut publics = Vec::with_capacity(certificate.signers.count());
249        for signer in certificate.signers.iter() {
250            let Some(public_key) = self.participants.value(signer as usize) else {
251                return false;
252            };
253
254            publics.push(*public_key);
255        }
256
257        // Verify the aggregate signature.
258        let (namespace, message) = subject.namespace_and_message(namespace);
259        aggregate_verify_multiple_public_keys::<V, _>(
260            publics.iter(),
261            Some(namespace.as_ref()),
262            message.as_ref(),
263            &certificate.signature,
264        )
265        .is_ok()
266    }
267
268    /// Verifies multiple certificates (no batch optimization for BLS multisig).
269    pub fn verify_certificates<'a, S, R, D, I>(
270        &self,
271        rng: &mut R,
272        namespace: &[u8],
273        certificates: I,
274    ) -> bool
275    where
276        S: Scheme,
277        R: Rng + CryptoRng,
278        D: Digest,
279        I: Iterator<Item = (S::Subject<'a, D>, &'a Certificate<V>)>,
280    {
281        for (subject, certificate) in certificates {
282            if !self.verify_certificate::<S, R, D>(rng, namespace, subject, certificate) {
283                return false;
284            }
285        }
286        true
287    }
288
289    pub const fn is_attributable(&self) -> bool {
290        true
291    }
292
293    pub const fn certificate_codec_config(&self) -> <Certificate<V> as Read>::Cfg {
294        self.participants.len()
295    }
296
297    pub const fn certificate_codec_config_unbounded() -> <Certificate<V> as Read>::Cfg {
298        u32::MAX as usize
299    }
300}
301
302/// Certificate formed by an aggregated BLS12-381 signature plus the signers that
303/// contributed to it.
304#[derive(Clone, Debug, PartialEq, Eq, Hash)]
305pub struct Certificate<V: Variant> {
306    /// Bitmap of participant indices that contributed signatures.
307    pub signers: Signers,
308    /// Aggregated BLS signature covering all signatures in this certificate.
309    pub signature: V::Signature,
310}
311
312impl<V: Variant> Write for Certificate<V> {
313    fn write(&self, writer: &mut impl BufMut) {
314        self.signers.write(writer);
315        self.signature.write(writer);
316    }
317}
318
319impl<V: Variant> EncodeSize for Certificate<V> {
320    fn encode_size(&self) -> usize {
321        self.signers.encode_size() + self.signature.encode_size()
322    }
323}
324
325impl<V: Variant> Read for Certificate<V> {
326    type Cfg = usize;
327
328    fn read_cfg(reader: &mut impl Buf, participants: &usize) -> Result<Self, Error> {
329        let signers = Signers::read_cfg(reader, participants)?;
330        if signers.count() == 0 {
331            return Err(Error::Invalid(
332                "cryptography::bls12381::certificate::multisig::Certificate",
333                "Certificate contains no signers",
334            ));
335        }
336
337        let signature = V::Signature::read(reader)?;
338
339        Ok(Self { signers, signature })
340    }
341}
342
343#[cfg(feature = "arbitrary")]
344impl<V: Variant> arbitrary::Arbitrary<'_> for Certificate<V>
345where
346    V::Signature: for<'a> arbitrary::Arbitrary<'a>,
347{
348    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
349        let signers = Signers::arbitrary(u)?;
350        let signature = V::Signature::arbitrary(u)?;
351        Ok(Self { signers, signature })
352    }
353}
354
355mod macros {
356    /// Generates a BLS12-381 multisig signing scheme wrapper for a specific protocol.
357    ///
358    /// This macro creates a complete wrapper struct with constructors, `Scheme` trait
359    /// implementation, and a `fixture` function for testing.
360    /// The only required parameter is the `Subject` type, which varies per protocol.
361    ///
362    /// # Example
363    /// ```ignore
364    /// impl_certificate_bls12381_multisig!(VoteSubject<'a, D>);
365    /// ```
366    #[macro_export]
367    macro_rules! impl_certificate_bls12381_multisig {
368        ($subject:ty) => {
369            /// Generates a test fixture with Ed25519 identities and BLS12-381 multisig schemes.
370            ///
371            /// Returns a [`commonware_cryptography::certificate::mocks::Fixture`] whose keys and
372            /// scheme instances share a consistent ordering.
373            #[cfg(feature = "mocks")]
374            #[allow(dead_code)]
375            pub fn fixture<V, R>(
376                rng: &mut R,
377                n: u32,
378            ) -> $crate::certificate::mocks::Fixture<Scheme<$crate::ed25519::PublicKey, V>>
379            where
380                V: $crate::bls12381::primitives::variant::Variant,
381                R: rand::RngCore + rand::CryptoRng,
382            {
383                $crate::bls12381::certificate::multisig::mocks::fixture::<_, V, _>(
384                    rng,
385                    n,
386                    Scheme::signer,
387                    Scheme::verifier,
388                )
389            }
390
391            /// BLS12-381 multi-signature signing scheme wrapper.
392            #[derive(Clone, Debug)]
393            pub struct Scheme<
394                P: $crate::PublicKey,
395                V: $crate::bls12381::primitives::variant::Variant,
396            > {
397                generic: $crate::bls12381::certificate::multisig::Generic<P, V>,
398            }
399
400            impl<
401                P: $crate::PublicKey,
402                V: $crate::bls12381::primitives::variant::Variant,
403            > Scheme<P, V> {
404                /// Creates a new scheme instance with the provided key material.
405                pub fn signer(
406                    participants: commonware_utils::ordered::BiMap<P, V::Public>,
407                    private_key: $crate::bls12381::primitives::group::Private,
408                ) -> Option<Self> {
409                    Some(Self {
410                        generic: $crate::bls12381::certificate::multisig::Generic::signer(
411                            participants,
412                            private_key,
413                        )?,
414                    })
415                }
416
417                /// Builds a verifier that can authenticate signatures and certificates.
418                pub const fn verifier(
419                    participants: commonware_utils::ordered::BiMap<P, V::Public>,
420                ) -> Self {
421                    Self {
422                        generic: $crate::bls12381::certificate::multisig::Generic::verifier(
423                            participants,
424                        ),
425                    }
426                }
427            }
428
429            impl<
430                P: $crate::PublicKey,
431                V: $crate::bls12381::primitives::variant::Variant + Send + Sync,
432            > $crate::certificate::Scheme for Scheme<P, V> {
433                type Subject<'a, D: $crate::Digest> = $subject;
434                type PublicKey = P;
435                type Signature = V::Signature;
436                type Certificate = $crate::bls12381::certificate::multisig::Certificate<V>;
437
438                fn me(&self) -> Option<u32> {
439                    self.generic.me()
440                }
441
442                fn participants(&self) -> &commonware_utils::ordered::Set<Self::PublicKey> {
443                    self.generic.participants()
444                }
445
446                fn sign<D: $crate::Digest>(
447                    &self,
448                    namespace: &[u8],
449                    subject: Self::Subject<'_, D>,
450                ) -> Option<$crate::certificate::Attestation<Self>> {
451                    self.generic.sign::<_, D>(namespace, subject)
452                }
453
454                fn verify_attestation<D: $crate::Digest>(
455                    &self,
456                    namespace: &[u8],
457                    subject: Self::Subject<'_, D>,
458                    attestation: &$crate::certificate::Attestation<Self>,
459                ) -> bool {
460                    self.generic.verify_attestation::<_, D>(namespace, subject, attestation)
461                }
462
463                fn verify_attestations<R, D, I>(
464                    &self,
465                    rng: &mut R,
466                    namespace: &[u8],
467                    subject: Self::Subject<'_, D>,
468                    attestations: I,
469                ) -> $crate::certificate::Verification<Self>
470                where
471                    R: rand::Rng + rand::CryptoRng,
472                    D: $crate::Digest,
473                    I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
474                {
475                    self.generic.verify_attestations::<_, _, D, _>(rng, namespace, subject, attestations)
476                }
477
478                fn assemble<I>(&self, attestations: I) -> Option<Self::Certificate>
479                where
480                    I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
481                {
482                    self.generic.assemble(attestations)
483                }
484
485                fn verify_certificate<
486                    R: rand::Rng + rand::CryptoRng,
487                    D: $crate::Digest,
488                >(
489                    &self,
490                    rng: &mut R,
491                    namespace: &[u8],
492                    subject: Self::Subject<'_, D>,
493                    certificate: &Self::Certificate,
494                ) -> bool {
495                    self.generic.verify_certificate::<Self, _, D>(rng, namespace, subject, certificate)
496                }
497
498                fn verify_certificates<'a, R, D, I>(
499                    &self,
500                    rng: &mut R,
501                    namespace: &[u8],
502                    certificates: I,
503                ) -> bool
504                where
505                    R: rand::Rng + rand::CryptoRng,
506                    D: $crate::Digest,
507                    I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
508                {
509                    self.generic.verify_certificates::<Self, _, D, _>(rng, namespace, certificates)
510                }
511
512                fn is_attributable(&self) -> bool {
513                    self.generic.is_attributable()
514                }
515
516                fn certificate_codec_config(
517                    &self,
518                ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
519                    self.generic.certificate_codec_config()
520                }
521
522                fn certificate_codec_config_unbounded() -> <Self::Certificate as commonware_codec::Read>::Cfg {
523                    $crate::bls12381::certificate::multisig::Generic::<P, V>::certificate_codec_config_unbounded()
524                }
525            }
526        };
527    }
528}
529
530#[cfg(test)]
531mod tests {
532    use super::*;
533    use crate::{
534        bls12381::primitives::{
535            group::Private,
536            ops::compute_public,
537            variant::{MinPk, MinSig, Variant},
538        },
539        certificate::Scheme as _,
540        ed25519::{self, PrivateKey as Ed25519PrivateKey},
541        impl_certificate_bls12381_multisig,
542        sha256::Digest as Sha256Digest,
543        Signer as _,
544    };
545    use bytes::Bytes;
546    use commonware_codec::{Decode, Encode};
547    use commonware_math::algebra::{Additive, Random};
548    use commonware_utils::{ordered::BiMap, quorum, TryCollect};
549    use rand::{rngs::StdRng, thread_rng, SeedableRng};
550
551    const NAMESPACE: &[u8] = b"test-bls12381-multisig";
552    const MESSAGE: &[u8] = b"test message";
553
554    /// Test context type for generic scheme tests.
555    #[derive(Clone, Debug)]
556    pub struct TestSubject<'a> {
557        pub message: &'a [u8],
558    }
559
560    impl<'a> Subject for TestSubject<'a> {
561        fn namespace_and_message(&self, namespace: &[u8]) -> (Bytes, Bytes) {
562            (namespace.to_vec().into(), self.message.to_vec().into())
563        }
564    }
565
566    // Use the macro to generate the test scheme
567    impl_certificate_bls12381_multisig!(TestSubject<'a>);
568
569    fn setup_signers<V: Variant>(
570        n: u32,
571        seed: u64,
572    ) -> (
573        Vec<Scheme<ed25519::PublicKey, V>>,
574        Scheme<ed25519::PublicKey, V>,
575    ) {
576        let mut rng = StdRng::seed_from_u64(seed);
577
578        // Generate identity keys (ed25519) and consensus keys (BLS)
579        let identity_keys: Vec<_> = (0..n)
580            .map(|_| Ed25519PrivateKey::random(&mut rng))
581            .collect();
582        let consensus_keys: Vec<Private> = (0..n).map(|_| Private::random(&mut rng)).collect();
583
584        // Build BiMap of identity public keys -> consensus public keys
585        let participants: BiMap<ed25519::PublicKey, V::Public> = identity_keys
586            .iter()
587            .zip(consensus_keys.iter())
588            .map(|(id_sk, cons_sk)| (id_sk.public_key(), compute_public::<V>(cons_sk)))
589            .try_collect()
590            .unwrap();
591
592        let signers = consensus_keys
593            .into_iter()
594            .map(|sk| Scheme::signer(participants.clone(), sk).unwrap())
595            .collect();
596
597        let verifier = Scheme::verifier(participants);
598
599        (signers, verifier)
600    }
601
602    fn test_sign_vote_roundtrip<V: Variant + Send + Sync>() {
603        let (schemes, _) = setup_signers::<V>(4, 42);
604        let scheme = &schemes[0];
605
606        let attestation = scheme
607            .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
608            .unwrap();
609        assert!(scheme.verify_attestation::<Sha256Digest>(
610            NAMESPACE,
611            TestSubject { message: MESSAGE },
612            &attestation
613        ));
614    }
615
616    #[test]
617    fn test_sign_vote_roundtrip_variants() {
618        test_sign_vote_roundtrip::<MinPk>();
619        test_sign_vote_roundtrip::<MinSig>();
620    }
621
622    fn test_verifier_cannot_sign<V: Variant + Send + Sync>() {
623        let (_, verifier) = setup_signers::<V>(4, 43);
624        assert!(verifier
625            .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
626            .is_none());
627    }
628
629    #[test]
630    fn test_verifier_cannot_sign_variants() {
631        test_verifier_cannot_sign::<MinPk>();
632        test_verifier_cannot_sign::<MinSig>();
633    }
634
635    fn test_verify_attestations_filters_invalid<V: Variant + Send + Sync>() {
636        let (schemes, _) = setup_signers::<V>(5, 44);
637        let quorum = quorum(schemes.len() as u32) as usize;
638
639        let attestations: Vec<_> = schemes
640            .iter()
641            .take(quorum)
642            .map(|s| {
643                s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
644                    .unwrap()
645            })
646            .collect();
647
648        let mut rng = StdRng::seed_from_u64(45);
649        let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
650            &mut rng,
651            NAMESPACE,
652            TestSubject { message: MESSAGE },
653            attestations.clone(),
654        );
655        assert!(result.invalid.is_empty());
656        assert_eq!(result.verified.len(), quorum);
657
658        // Test: Corrupt one attestation - invalid signer index
659        let mut attestations_corrupted = attestations.clone();
660        attestations_corrupted[0].signer = 999;
661        let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
662            &mut rng,
663            NAMESPACE,
664            TestSubject { message: MESSAGE },
665            attestations_corrupted,
666        );
667        assert_eq!(result.invalid, vec![999]);
668        assert_eq!(result.verified.len(), quorum - 1);
669
670        // Test: Corrupt one attestation - invalid signature
671        let mut attestations_corrupted = attestations;
672        attestations_corrupted[0].signature = attestations_corrupted[1].signature;
673        let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
674            &mut rng,
675            NAMESPACE,
676            TestSubject { message: MESSAGE },
677            attestations_corrupted,
678        );
679        assert_eq!(result.invalid.len(), 1);
680        assert_eq!(result.verified.len(), quorum - 1);
681    }
682
683    #[test]
684    fn test_verify_attestations_filters_invalid_variants() {
685        test_verify_attestations_filters_invalid::<MinPk>();
686        test_verify_attestations_filters_invalid::<MinSig>();
687    }
688
689    fn test_assemble_certificate<V: Variant + Send + Sync>() {
690        let (schemes, _) = setup_signers::<V>(4, 46);
691        let quorum = quorum(schemes.len() as u32) as usize;
692
693        let attestations: Vec<_> = schemes
694            .iter()
695            .take(quorum)
696            .map(|s| {
697                s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
698                    .unwrap()
699            })
700            .collect();
701
702        let certificate = schemes[0].assemble(attestations).unwrap();
703        assert_eq!(certificate.signers.count(), quorum);
704    }
705
706    #[test]
707    fn test_assemble_certificate_variants() {
708        test_assemble_certificate::<MinPk>();
709        test_assemble_certificate::<MinSig>();
710    }
711
712    fn test_assemble_certificate_sorts_signers<V: Variant + Send + Sync>() {
713        let (schemes, _) = setup_signers::<V>(4, 47);
714
715        // Create votes in non-sorted order (indices 2, 0, 1)
716        let attestations = vec![
717            schemes[2]
718                .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
719                .unwrap(),
720            schemes[0]
721                .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
722                .unwrap(),
723            schemes[1]
724                .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
725                .unwrap(),
726        ];
727
728        let certificate = schemes[0].assemble(attestations).unwrap();
729        assert_eq!(
730            certificate.signers.iter().collect::<Vec<_>>(),
731            vec![0, 1, 2]
732        );
733    }
734
735    #[test]
736    fn test_assemble_certificate_sorts_signers_variants() {
737        test_assemble_certificate_sorts_signers::<MinPk>();
738        test_assemble_certificate_sorts_signers::<MinSig>();
739    }
740
741    fn test_verify_certificate<V: Variant + Send + Sync>() {
742        let (schemes, verifier) = setup_signers::<V>(4, 48);
743        let quorum = quorum(schemes.len() as u32) as usize;
744
745        let attestations: Vec<_> = schemes
746            .iter()
747            .take(quorum)
748            .map(|s| {
749                s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
750                    .unwrap()
751            })
752            .collect();
753
754        let certificate = schemes[0].assemble(attestations).unwrap();
755
756        let mut rng = StdRng::seed_from_u64(49);
757        assert!(verifier.verify_certificate::<_, Sha256Digest>(
758            &mut rng,
759            NAMESPACE,
760            TestSubject { message: MESSAGE },
761            &certificate
762        ));
763    }
764
765    #[test]
766    fn test_verify_certificate_variants() {
767        test_verify_certificate::<MinPk>();
768        test_verify_certificate::<MinSig>();
769    }
770
771    fn test_verify_certificate_detects_corruption<V: Variant + Send + Sync>() {
772        let (schemes, verifier) = setup_signers::<V>(4, 50);
773        let quorum = quorum(schemes.len() as u32) as usize;
774
775        let attestations: Vec<_> = schemes
776            .iter()
777            .take(quorum)
778            .map(|s| {
779                s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
780                    .unwrap()
781            })
782            .collect();
783
784        let certificate = schemes[0].assemble(attestations).unwrap();
785
786        // Valid certificate passes
787        assert!(verifier.verify_certificate::<_, Sha256Digest>(
788            &mut thread_rng(),
789            NAMESPACE,
790            TestSubject { message: MESSAGE },
791            &certificate
792        ));
793
794        // Corrupted certificate fails
795        let mut corrupted = certificate;
796        corrupted.signature = V::Signature::zero();
797        assert!(!verifier.verify_certificate::<_, Sha256Digest>(
798            &mut thread_rng(),
799            NAMESPACE,
800            TestSubject { message: MESSAGE },
801            &corrupted
802        ));
803    }
804
805    #[test]
806    fn test_verify_certificate_detects_corruption_variants() {
807        test_verify_certificate_detects_corruption::<MinPk>();
808        test_verify_certificate_detects_corruption::<MinSig>();
809    }
810
811    fn test_certificate_codec_roundtrip<V: Variant + Send + Sync>() {
812        let (schemes, _) = setup_signers::<V>(4, 51);
813        let quorum = quorum(schemes.len() as u32) as usize;
814
815        let attestations: Vec<_> = schemes
816            .iter()
817            .take(quorum)
818            .map(|s| {
819                s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
820                    .unwrap()
821            })
822            .collect();
823
824        let certificate = schemes[0].assemble(attestations).unwrap();
825        let encoded = certificate.encode();
826        let decoded =
827            Certificate::<V>::decode_cfg(encoded, &schemes.len()).expect("decode certificate");
828        assert_eq!(decoded, certificate);
829    }
830
831    #[test]
832    fn test_certificate_codec_roundtrip_variants() {
833        test_certificate_codec_roundtrip::<MinPk>();
834        test_certificate_codec_roundtrip::<MinSig>();
835    }
836
837    fn test_certificate_rejects_sub_quorum<V: Variant + Send + Sync>() {
838        let (schemes, _) = setup_signers::<V>(4, 52);
839        let sub_quorum = 2; // Less than quorum (3)
840
841        let attestations: Vec<_> = schemes
842            .iter()
843            .take(sub_quorum)
844            .map(|s| {
845                s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
846                    .unwrap()
847            })
848            .collect();
849
850        assert!(schemes[0].assemble(attestations).is_none());
851    }
852
853    #[test]
854    fn test_certificate_rejects_sub_quorum_variants() {
855        test_certificate_rejects_sub_quorum::<MinPk>();
856        test_certificate_rejects_sub_quorum::<MinSig>();
857    }
858
859    fn test_certificate_rejects_invalid_signer<V: Variant + Send + Sync>() {
860        let (schemes, _) = setup_signers::<V>(4, 53);
861        let quorum = quorum(schemes.len() as u32) as usize;
862
863        let mut attestations: Vec<_> = schemes
864            .iter()
865            .take(quorum)
866            .map(|s| {
867                s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
868                    .unwrap()
869            })
870            .collect();
871
872        // Corrupt signer index to be out of range
873        attestations[0].signer = 999;
874
875        assert!(schemes[0].assemble(attestations).is_none());
876    }
877
878    #[test]
879    fn test_certificate_rejects_invalid_signer_variants() {
880        test_certificate_rejects_invalid_signer::<MinPk>();
881        test_certificate_rejects_invalid_signer::<MinSig>();
882    }
883
884    fn test_verify_certificate_rejects_sub_quorum<V: Variant + Send + Sync>() {
885        let (schemes, verifier) = setup_signers::<V>(4, 54);
886        let participants_len = schemes.len();
887
888        let attestations: Vec<_> = schemes
889            .iter()
890            .take(3)
891            .map(|s| {
892                s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
893                    .unwrap()
894            })
895            .collect();
896
897        let mut certificate = schemes[0].assemble(attestations).unwrap();
898
899        // Artificially truncate to below quorum
900        let mut signers: Vec<u32> = certificate.signers.iter().collect();
901        signers.pop();
902        certificate.signers = Signers::from(participants_len, signers);
903
904        assert!(!verifier.verify_certificate::<_, Sha256Digest>(
905            &mut thread_rng(),
906            NAMESPACE,
907            TestSubject { message: MESSAGE },
908            &certificate
909        ));
910    }
911
912    #[test]
913    fn test_verify_certificate_rejects_sub_quorum_variants() {
914        test_verify_certificate_rejects_sub_quorum::<MinPk>();
915        test_verify_certificate_rejects_sub_quorum::<MinSig>();
916    }
917
918    fn test_verify_certificate_rejects_signers_size_mismatch<V: Variant + Send + Sync>() {
919        let (schemes, verifier) = setup_signers::<V>(4, 55);
920        let participants_len = schemes.len();
921
922        let attestations: Vec<_> = schemes
923            .iter()
924            .take(3)
925            .map(|s| {
926                s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
927                    .unwrap()
928            })
929            .collect();
930
931        let mut certificate = schemes[0].assemble(attestations).unwrap();
932
933        // Make the signers bitmap size larger than participants
934        let signers: Vec<u32> = certificate.signers.iter().collect();
935        certificate.signers = Signers::from(participants_len + 1, signers);
936
937        assert!(!verifier.verify_certificate::<_, Sha256Digest>(
938            &mut thread_rng(),
939            NAMESPACE,
940            TestSubject { message: MESSAGE },
941            &certificate
942        ));
943    }
944
945    #[test]
946    fn test_verify_certificate_rejects_signers_size_mismatch_variants() {
947        test_verify_certificate_rejects_signers_size_mismatch::<MinPk>();
948        test_verify_certificate_rejects_signers_size_mismatch::<MinSig>();
949    }
950
951    fn test_verify_certificates_batch<V: Variant + Send + Sync>() {
952        let (schemes, verifier) = setup_signers::<V>(4, 56);
953        let quorum = quorum(schemes.len() as u32) as usize;
954
955        let messages = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()];
956        let mut certificates = Vec::new();
957
958        for msg in &messages {
959            let attestations: Vec<_> = schemes
960                .iter()
961                .take(quorum)
962                .map(|s| {
963                    s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: msg })
964                        .unwrap()
965                })
966                .collect();
967            certificates.push(schemes[0].assemble(attestations).unwrap());
968        }
969
970        let certs_iter = messages
971            .iter()
972            .zip(&certificates)
973            .map(|(msg, cert)| (TestSubject { message: msg }, cert));
974
975        let mut rng = StdRng::seed_from_u64(57);
976        assert!(verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter));
977    }
978
979    #[test]
980    fn test_verify_certificates_batch_variants() {
981        test_verify_certificates_batch::<MinPk>();
982        test_verify_certificates_batch::<MinSig>();
983    }
984
985    fn test_verify_certificates_batch_detects_failure<V: Variant + Send + Sync>() {
986        let (schemes, verifier) = setup_signers::<V>(4, 58);
987        let quorum = quorum(schemes.len() as u32) as usize;
988
989        let messages = [b"msg1".as_slice(), b"msg2".as_slice()];
990        let mut certificates = Vec::new();
991
992        for msg in &messages {
993            let attestations: Vec<_> = schemes
994                .iter()
995                .take(quorum)
996                .map(|s| {
997                    s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: msg })
998                        .unwrap()
999                })
1000                .collect();
1001            certificates.push(schemes[0].assemble(attestations).unwrap());
1002        }
1003
1004        // Corrupt second certificate
1005        certificates[1].signature = V::Signature::zero();
1006
1007        let certs_iter = messages
1008            .iter()
1009            .zip(&certificates)
1010            .map(|(msg, cert)| (TestSubject { message: msg }, cert));
1011
1012        let mut rng = StdRng::seed_from_u64(59);
1013        assert!(
1014            !verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter)
1015        );
1016    }
1017
1018    #[test]
1019    fn test_verify_certificates_batch_detects_failure_variants() {
1020        test_verify_certificates_batch_detects_failure::<MinPk>();
1021        test_verify_certificates_batch_detects_failure::<MinSig>();
1022    }
1023
1024    fn test_scheme_clone_and_verifier<V: Variant + Send + Sync>() {
1025        let (schemes, verifier) = setup_signers::<V>(4, 60);
1026
1027        // Clone a signer
1028        let signer = schemes[0].clone();
1029        assert!(
1030            signer
1031                .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
1032                .is_some(),
1033            "cloned signer should retain signing capability"
1034        );
1035
1036        // A verifier cannot produce votes
1037        assert!(
1038            verifier
1039                .sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
1040                .is_none(),
1041            "verifier must not sign votes"
1042        );
1043    }
1044
1045    #[test]
1046    fn test_scheme_clone_and_verifier_variants() {
1047        test_scheme_clone_and_verifier::<MinPk>();
1048        test_scheme_clone_and_verifier::<MinSig>();
1049    }
1050
1051    fn test_certificate_decode_validation<V: Variant + Send + Sync>() {
1052        let (schemes, _) = setup_signers::<V>(4, 61);
1053        let participants_len = schemes.len();
1054
1055        let attestations: Vec<_> = schemes
1056            .iter()
1057            .take(3)
1058            .map(|s| {
1059                s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
1060                    .unwrap()
1061            })
1062            .collect();
1063
1064        let certificate = schemes[0].assemble(attestations).unwrap();
1065
1066        // Well-formed certificate decodes successfully
1067        let encoded = certificate.encode();
1068        let decoded =
1069            Certificate::<V>::decode_cfg(encoded, &participants_len).expect("decode certificate");
1070        assert_eq!(decoded, certificate);
1071
1072        // Certificate with no signers is rejected
1073        let empty = Certificate::<V> {
1074            signers: Signers::from(participants_len, std::iter::empty::<u32>()),
1075            signature: certificate.signature,
1076        };
1077        assert!(Certificate::<V>::decode_cfg(empty.encode(), &participants_len).is_err());
1078
1079        // Certificate containing more signers than the participant set is rejected
1080        let mut signers = certificate.signers.iter().collect::<Vec<_>>();
1081        signers.push(participants_len as u32);
1082        let extended = Certificate::<V> {
1083            signers: Signers::from(participants_len + 1, signers),
1084            signature: certificate.signature,
1085        };
1086        assert!(Certificate::<V>::decode_cfg(extended.encode(), &participants_len).is_err());
1087    }
1088
1089    #[test]
1090    fn test_certificate_decode_validation_variants() {
1091        test_certificate_decode_validation::<MinPk>();
1092        test_certificate_decode_validation::<MinSig>();
1093    }
1094
1095    fn test_verify_certificate_rejects_unknown_signer<V: Variant + Send + Sync>() {
1096        let (schemes, verifier) = setup_signers::<V>(4, 62);
1097        let participants_len = schemes.len();
1098
1099        let attestations: Vec<_> = schemes
1100            .iter()
1101            .take(3)
1102            .map(|s| {
1103                s.sign::<Sha256Digest>(NAMESPACE, TestSubject { message: MESSAGE })
1104                    .unwrap()
1105            })
1106            .collect();
1107
1108        let mut certificate = schemes[0].assemble(attestations).unwrap();
1109
1110        // Add an unknown signer (out of range)
1111        let mut signers: Vec<u32> = certificate.signers.iter().collect();
1112        signers.push(participants_len as u32);
1113        certificate.signers = Signers::from(participants_len + 1, signers);
1114
1115        assert!(!verifier.verify_certificate::<_, Sha256Digest>(
1116            &mut thread_rng(),
1117            NAMESPACE,
1118            TestSubject { message: MESSAGE },
1119            &certificate,
1120        ));
1121    }
1122
1123    #[test]
1124    fn test_verify_certificate_rejects_unknown_signer_variants() {
1125        test_verify_certificate_rejects_unknown_signer::<MinPk>();
1126        test_verify_certificate_rejects_unknown_signer::<MinSig>();
1127    }
1128
1129    #[cfg(feature = "arbitrary")]
1130    mod conformance {
1131        use super::*;
1132        use crate::bls12381::primitives::variant::MinSig;
1133        use commonware_codec::conformance::CodecConformance;
1134
1135        commonware_conformance::conformance_tests! {
1136            CodecConformance<Certificate<MinSig>>,
1137        }
1138    }
1139}