commonware_cryptography/ed25519/certificate/
mod.rs

1//! Ed25519 signing scheme implementation.
2//!
3//! This module provides both the generic Ed25519 implementation and a macro to generate
4//! protocol-specific wrappers.
5
6#[cfg(feature = "mocks")]
7pub mod mocks;
8
9#[cfg(feature = "std")]
10use super::Batch;
11use super::{PrivateKey, PublicKey, Signature as Ed25519Signature};
12#[cfg(feature = "std")]
13use crate::{certificate::Verification, BatchVerifier};
14use crate::{
15    certificate::{Attestation, Namespace, Scheme, Signers, Subject},
16    Digest, Signer as _, Verifier as _,
17};
18#[cfg(not(feature = "std"))]
19use alloc::vec::Vec;
20use bytes::{Buf, BufMut};
21use commonware_codec::{EncodeSize, Error, Read, ReadRangeExt, Write};
22use commonware_utils::{
23    ordered::{Quorum, Set},
24    Faults, Participant,
25};
26use rand_core::CryptoRngCore;
27#[cfg(feature = "std")]
28use std::collections::BTreeSet;
29
30/// Generic Ed25519 signing scheme 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<N: Namespace> {
37    /// Participants in the committee.
38    pub participants: Set<PublicKey>,
39    /// Key used for generating signatures.
40    pub signer: Option<(Participant, PrivateKey)>,
41    /// Pre-computed namespace(s) for this subject type.
42    pub namespace: N,
43}
44
45impl<N: Namespace> Generic<N> {
46    /// Creates a new generic Ed25519 scheme instance.
47    pub fn signer(
48        namespace: &[u8],
49        participants: Set<PublicKey>,
50        private_key: PrivateKey,
51    ) -> Option<Self> {
52        let signer = participants
53            .index(&private_key.public_key())
54            .map(|index| (index, private_key))?;
55
56        Some(Self {
57            participants,
58            signer: Some(signer),
59            namespace: N::derive(namespace),
60        })
61    }
62
63    /// Builds a verifier that can authenticate signatures without generating them.
64    pub fn verifier(namespace: &[u8], participants: Set<PublicKey>) -> Self {
65        Self {
66            participants,
67            signer: None,
68            namespace: N::derive(namespace),
69        }
70    }
71
72    /// Returns the index of "self" in the participant set, if available.
73    pub fn me(&self) -> Option<Participant> {
74        self.signer.as_ref().map(|(index, _)| *index)
75    }
76
77    /// Signs a subject and returns the signer index and signature.
78    pub fn sign<'a, S, D>(&self, subject: S::Subject<'a, D>) -> Option<Attestation<S>>
79    where
80        S: Scheme<Signature = Ed25519Signature>,
81        S::Subject<'a, D>: Subject<Namespace = N>,
82        D: Digest,
83    {
84        let (index, private_key) = self.signer.as_ref()?;
85
86        let signature = private_key.sign(subject.namespace(&self.namespace), &subject.message());
87
88        Some(Attestation {
89            signer: *index,
90            signature,
91        })
92    }
93
94    /// Verifies a single attestation from a signer.
95    pub fn verify_attestation<'a, S, D>(
96        &self,
97        subject: S::Subject<'a, D>,
98        attestation: &Attestation<S>,
99    ) -> bool
100    where
101        S: Scheme<Signature = Ed25519Signature>,
102        S::Subject<'a, D>: Subject<Namespace = N>,
103        D: Digest,
104    {
105        let Some(public_key) = self.participants.key(attestation.signer) else {
106            return false;
107        };
108
109        public_key.verify(
110            subject.namespace(&self.namespace),
111            &subject.message(),
112            &attestation.signature,
113        )
114    }
115
116    /// Batch-verifies attestations and returns verified attestations and invalid signers.
117    #[cfg(feature = "std")]
118    pub fn verify_attestations<'a, S, R, D, I>(
119        &self,
120        rng: &mut R,
121        subject: S::Subject<'a, D>,
122        attestations: I,
123    ) -> Verification<S>
124    where
125        S: Scheme<Signature = Ed25519Signature>,
126        S::Subject<'a, D>: Subject<Namespace = N>,
127        R: CryptoRngCore,
128        D: Digest,
129        I: IntoIterator<Item = Attestation<S>>,
130    {
131        let namespace = subject.namespace(&self.namespace);
132        let message = subject.message();
133
134        let mut invalid = BTreeSet::new();
135        let mut candidates = Vec::new();
136        let mut batch = Batch::new();
137
138        for attestation in attestations.into_iter() {
139            let Some(public_key) = self.participants.key(attestation.signer) else {
140                invalid.insert(attestation.signer);
141                continue;
142            };
143
144            batch.add(namespace, &message, public_key, &attestation.signature);
145            candidates.push((attestation, public_key));
146        }
147
148        if !candidates.is_empty() && !batch.verify(rng) {
149            // Batch failed: fall back to per-signer verification to isolate faulty attestations.
150            for (attestation, public_key) in &candidates {
151                if !public_key.verify(namespace, &message, &attestation.signature) {
152                    invalid.insert(attestation.signer);
153                }
154            }
155        }
156
157        let verified = candidates
158            .into_iter()
159            .filter_map(|(attestation, _)| {
160                if invalid.contains(&attestation.signer) {
161                    None
162                } else {
163                    Some(attestation)
164                }
165            })
166            .collect();
167
168        Verification::new(verified, invalid.into_iter().collect())
169    }
170
171    /// Verifies attestations one-by-one and returns verified attestations and invalid signers.
172    #[cfg(not(feature = "std"))]
173    pub fn verify_attestations<'a, S, R, D, I>(
174        &self,
175        _rng: &mut R,
176        subject: S::Subject<'a, D>,
177        attestations: I,
178    ) -> crate::certificate::Verification<S>
179    where
180        S: Scheme<Signature = Ed25519Signature>,
181        S::Subject<'a, D>: Subject<Namespace = N>,
182        R: CryptoRngCore,
183        D: Digest,
184        I: IntoIterator<Item = Attestation<S>>,
185    {
186        let namespace = subject.namespace(&self.namespace);
187        let message = subject.message();
188
189        let mut invalid = alloc::collections::BTreeSet::new();
190        let mut verified = Vec::new();
191
192        for attestation in attestations.into_iter() {
193            let Some(public_key) = self.participants.key(attestation.signer) else {
194                invalid.insert(attestation.signer);
195                continue;
196            };
197
198            if public_key.verify(namespace, &message, &attestation.signature) {
199                verified.push(attestation);
200            } else {
201                invalid.insert(attestation.signer);
202            }
203        }
204
205        crate::certificate::Verification::new(verified, invalid.into_iter().collect())
206    }
207
208    /// Assembles a certificate from a collection of attestations.
209    pub fn assemble<S, I, M>(&self, attestations: I) -> Option<Certificate>
210    where
211        S: Scheme<Signature = Ed25519Signature>,
212        I: IntoIterator<Item = Attestation<S>>,
213        M: Faults,
214    {
215        // Collect the signers and signatures.
216        let mut entries = Vec::new();
217        for Attestation { signer, signature } in attestations {
218            if usize::from(signer) >= self.participants.len() {
219                return None;
220            }
221
222            entries.push((signer, signature));
223        }
224        if entries.len() < self.participants.quorum::<M>() as usize {
225            return None;
226        }
227
228        // Sort the signatures by signer index.
229        entries.sort_by_key(|(signer, _)| *signer);
230        let (signer, signatures): (Vec<Participant>, Vec<_>) = entries.into_iter().unzip();
231        let signers = Signers::from(self.participants.len(), signer);
232
233        Some(Certificate {
234            signers,
235            signatures,
236        })
237    }
238
239    /// Stages a certificate for batch verification.
240    ///
241    /// Returns false if the certificate structure is invalid.
242    #[cfg(feature = "std")]
243    fn batch_verify_certificate<'a, S, D, M>(
244        &self,
245        batch: &mut Batch,
246        subject: S::Subject<'a, D>,
247        certificate: &Certificate,
248    ) -> bool
249    where
250        S: Scheme,
251        S::Subject<'a, D>: Subject<Namespace = N>,
252        D: Digest,
253        M: Faults,
254    {
255        // If the certificate signers length does not match the participant set, return false.
256        if certificate.signers.len() != self.participants.len() {
257            return false;
258        }
259
260        // If the certificate signers and signatures counts differ, return false.
261        if certificate.signers.count() != certificate.signatures.len() {
262            return false;
263        }
264
265        // If the certificate does not meet the quorum, return false.
266        if certificate.signers.count() < self.participants.quorum::<M>() as usize {
267            return false;
268        }
269
270        // Add the certificate to the batch.
271        let namespace = subject.namespace(&self.namespace);
272        let message = subject.message();
273        for (signer, signature) in certificate.signers.iter().zip(&certificate.signatures) {
274            let Some(public_key) = self.participants.key(signer) else {
275                return false;
276            };
277
278            batch.add(namespace, &message, public_key, signature);
279        }
280
281        true
282    }
283
284    /// Verifies a certificate using batch verification.
285    #[cfg(feature = "std")]
286    pub fn verify_certificate<'a, S, R, D, M>(
287        &self,
288        rng: &mut R,
289        subject: S::Subject<'a, D>,
290        certificate: &Certificate,
291    ) -> bool
292    where
293        S: Scheme,
294        S::Subject<'a, D>: Subject<Namespace = N>,
295        R: CryptoRngCore,
296        D: Digest,
297        M: Faults,
298    {
299        let mut batch = Batch::new();
300        if !self.batch_verify_certificate::<S, D, M>(&mut batch, subject, certificate) {
301            return false;
302        }
303
304        batch.verify(rng)
305    }
306
307    /// Verifies a certificate by checking each signature individually.
308    #[cfg(not(feature = "std"))]
309    pub fn verify_certificate<'a, S, R, D, M>(
310        &self,
311        _rng: &mut R,
312        subject: S::Subject<'a, D>,
313        certificate: &Certificate,
314    ) -> bool
315    where
316        S: Scheme,
317        S::Subject<'a, D>: Subject<Namespace = N>,
318        R: CryptoRngCore,
319        D: Digest,
320        M: Faults,
321    {
322        if certificate.signers.len() != self.participants.len() {
323            return false;
324        }
325        if certificate.signers.count() != certificate.signatures.len() {
326            return false;
327        }
328        if certificate.signers.count() < self.participants.quorum::<M>() as usize {
329            return false;
330        }
331
332        let namespace = subject.namespace(&self.namespace);
333        let message = subject.message();
334        for (signer, signature) in certificate.signers.iter().zip(&certificate.signatures) {
335            let Some(public_key) = self.participants.key(signer) else {
336                return false;
337            };
338            if !public_key.verify(namespace, &message, signature) {
339                return false;
340            }
341        }
342
343        true
344    }
345
346    /// Verifies multiple certificates in a batch.
347    #[cfg(feature = "std")]
348    pub fn verify_certificates<'a, S, R, D, I, M>(&self, rng: &mut R, certificates: I) -> bool
349    where
350        S: Scheme,
351        S::Subject<'a, D>: Subject<Namespace = N>,
352        R: CryptoRngCore,
353        D: Digest,
354        I: Iterator<Item = (S::Subject<'a, D>, &'a Certificate)>,
355        M: Faults,
356    {
357        let mut batch = Batch::new();
358        for (subject, certificate) in certificates {
359            if !self.batch_verify_certificate::<S, D, M>(&mut batch, subject, certificate) {
360                return false;
361            }
362        }
363
364        batch.verify(rng)
365    }
366
367    /// Verifies multiple certificates one-by-one.
368    #[cfg(not(feature = "std"))]
369    pub fn verify_certificates<'a, S, R, D, I, M>(&self, rng: &mut R, certificates: I) -> bool
370    where
371        S: Scheme,
372        S::Subject<'a, D>: Subject<Namespace = N>,
373        R: CryptoRngCore,
374        D: Digest,
375        I: Iterator<Item = (S::Subject<'a, D>, &'a Certificate)>,
376        M: Faults,
377    {
378        for (subject, certificate) in certificates {
379            if !self.verify_certificate::<S, _, D, M>(rng, subject, certificate) {
380                return false;
381            }
382        }
383
384        true
385    }
386
387    pub const fn is_attributable() -> bool {
388        true
389    }
390
391    pub const fn is_batchable() -> bool {
392        // Batch verification requires the `std` feature because it depends on
393        // `ed25519_consensus::batch::Verifier`.
394        cfg!(feature = "std")
395    }
396
397    pub const fn certificate_codec_config(&self) -> <Certificate as commonware_codec::Read>::Cfg {
398        self.participants.len()
399    }
400
401    pub const fn certificate_codec_config_unbounded() -> <Certificate as commonware_codec::Read>::Cfg
402    {
403        u32::MAX as usize
404    }
405}
406
407#[derive(Clone, Debug, PartialEq, Eq, Hash)]
408pub struct Certificate {
409    /// Bitmap of participant indices that contributed signatures.
410    pub signers: Signers,
411    /// Ed25519 signatures emitted by the respective participants ordered by signer index.
412    pub signatures: Vec<Ed25519Signature>,
413}
414
415#[cfg(feature = "arbitrary")]
416impl arbitrary::Arbitrary<'_> for Certificate {
417    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
418        let signers = Signers::arbitrary(u)?;
419        let signatures = (0..signers.count())
420            .map(|_| u.arbitrary::<Ed25519Signature>())
421            .collect::<arbitrary::Result<Vec<_>>>()?;
422        Ok(Self {
423            signers,
424            signatures,
425        })
426    }
427}
428
429impl Write for Certificate {
430    fn write(&self, writer: &mut impl BufMut) {
431        self.signers.write(writer);
432        self.signatures.write(writer);
433    }
434}
435
436impl EncodeSize for Certificate {
437    fn encode_size(&self) -> usize {
438        self.signers.encode_size() + self.signatures.encode_size()
439    }
440}
441
442impl Read for Certificate {
443    type Cfg = usize;
444
445    fn read_cfg(reader: &mut impl Buf, participants: &usize) -> Result<Self, Error> {
446        let signers = Signers::read_cfg(reader, participants)?;
447        if signers.count() == 0 {
448            return Err(Error::Invalid(
449                "cryptography::ed25519::certificate::Certificate",
450                "Certificate contains no signers",
451            ));
452        }
453
454        let signatures = Vec::<Ed25519Signature>::read_range(reader, ..=*participants)?;
455        if signers.count() != signatures.len() {
456            return Err(Error::Invalid(
457                "cryptography::ed25519::certificate::Certificate",
458                "Signers and signatures counts differ",
459            ));
460        }
461
462        Ok(Self {
463            signers,
464            signatures,
465        })
466    }
467}
468
469mod macros {
470    /// Generates an Ed25519 signing scheme wrapper for a specific protocol.
471    ///
472    /// This macro creates a complete wrapper struct with constructors, `Scheme` trait
473    /// implementation, and a `fixture` function for testing.
474    ///
475    /// # Parameters
476    ///
477    /// - `$subject`: The subject type used as `Scheme::Subject<'a, D>`. Use `'a` and `D`
478    ///   in the subject type to bind to the GAT lifetime and digest type parameters.
479    ///
480    /// - `$namespace`: The namespace type that implements [`Namespace`](crate::certificate::Namespace).
481    ///   This type pre-computes and stores any protocol-specific namespace bytes derived from
482    ///   a base namespace. The scheme calls `$namespace::derive(base)` at construction time
483    ///   to create the namespace, then passes it to `Subject::namespace()` during signing
484    ///   and verification. For simple protocols with only a base namespace, `Vec<u8>` can be used directly.
485    ///   For protocols with multiple message types, a custom struct can pre-compute all variants.
486    ///
487    /// # Example
488    /// ```ignore
489    /// // For non-generic subject types with a single namespace:
490    /// impl_certificate_ed25519!(MySubject, Vec<u8>);
491    ///
492    /// // For protocols with generic subject types:
493    /// impl_certificate_ed25519!(Subject<'a, D>, Namespace);
494    /// ```
495    #[macro_export]
496    macro_rules! impl_certificate_ed25519 {
497        ($subject:ty, $namespace:ty) => {
498            /// Generates a test fixture with Ed25519 identities and signing schemes.
499            ///
500            /// Returns a [`commonware_cryptography::certificate::mocks::Fixture`] whose keys and
501            /// scheme instances share a consistent ordering.
502            #[cfg(feature = "mocks")]
503            #[allow(dead_code)]
504            pub fn fixture<R>(
505                rng: &mut R,
506                namespace: &[u8],
507                n: u32,
508            ) -> $crate::certificate::mocks::Fixture<Scheme>
509            where
510                R: rand::RngCore + rand::CryptoRng,
511            {
512                $crate::ed25519::certificate::mocks::fixture(
513                    rng,
514                    namespace,
515                    n,
516                    Scheme::signer,
517                    Scheme::verifier,
518                )
519            }
520
521            /// Ed25519 signing scheme wrapper.
522            #[derive(Clone, Debug)]
523            pub struct Scheme {
524                generic: $crate::ed25519::certificate::Generic<$namespace>,
525            }
526
527            impl Scheme {
528                /// Creates a new scheme instance with the provided key material.
529                ///
530                /// Participants use the same key for both identity and signing.
531                ///
532                /// If the provided private key does not match any signing key in the participant set,
533                /// the instance will act as a verifier (unable to generate signatures).
534                ///
535                /// Returns `None` if the provided private key does not match any participant
536                /// in the participant set.
537                pub fn signer(
538                    namespace: &[u8],
539                    participants: commonware_utils::ordered::Set<$crate::ed25519::PublicKey>,
540                    private_key: $crate::ed25519::PrivateKey,
541                ) -> Option<Self> {
542                    Some(Self {
543                        generic: $crate::ed25519::certificate::Generic::signer(
544                            namespace,
545                            participants,
546                            private_key,
547                        )?,
548                    })
549                }
550
551                /// Builds a verifier that can authenticate signatures without generating them.
552                ///
553                /// Participants use the same key for both identity and signing.
554                pub fn verifier(
555                    namespace: &[u8],
556                    participants: commonware_utils::ordered::Set<$crate::ed25519::PublicKey>,
557                ) -> Self {
558                    Self {
559                        generic: $crate::ed25519::certificate::Generic::verifier(
560                            namespace,
561                            participants,
562                        ),
563                    }
564                }
565            }
566
567            impl $crate::certificate::Scheme for Scheme {
568                type Subject<'a, D: $crate::Digest> = $subject;
569                type PublicKey = $crate::ed25519::PublicKey;
570                type Signature = $crate::ed25519::Signature;
571                type Certificate = $crate::ed25519::certificate::Certificate;
572
573                fn me(&self) -> Option<commonware_utils::Participant> {
574                    self.generic.me()
575                }
576
577                fn participants(&self) -> &commonware_utils::ordered::Set<Self::PublicKey> {
578                    &self.generic.participants
579                }
580
581                fn sign<D: $crate::Digest>(
582                    &self,
583                    subject: Self::Subject<'_, D>,
584                ) -> Option<$crate::certificate::Attestation<Self>> {
585                    self.generic.sign::<_, D>(subject)
586                }
587
588                fn verify_attestation<R, D>(
589                    &self,
590                    _rng: &mut R,
591                    subject: Self::Subject<'_, D>,
592                    attestation: &$crate::certificate::Attestation<Self>,
593                    _strategy: &impl commonware_parallel::Strategy,
594                ) -> bool
595                where
596                    R: rand_core::CryptoRngCore,
597                    D: $crate::Digest,
598                {
599                    self.generic
600                        .verify_attestation::<_, D>(subject, attestation)
601                }
602
603                fn verify_attestations<R, D, I>(
604                    &self,
605                    rng: &mut R,
606                    subject: Self::Subject<'_, D>,
607                    attestations: I,
608                    _strategy: &impl commonware_parallel::Strategy,
609                ) -> $crate::certificate::Verification<Self>
610                where
611                    R: rand_core::CryptoRngCore,
612                    D: $crate::Digest,
613                    I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
614                {
615                    self.generic
616                        .verify_attestations::<_, _, D, _>(rng, subject, attestations)
617                }
618
619                fn assemble<I, M>(
620                    &self,
621                    attestations: I,
622                    _strategy: &impl commonware_parallel::Strategy,
623                ) -> Option<Self::Certificate>
624                where
625                    I: IntoIterator<Item = $crate::certificate::Attestation<Self>>,
626                    M: commonware_utils::Faults,
627                {
628                    self.generic.assemble::<Self, _, M>(attestations)
629                }
630
631                fn verify_certificate<R, D, M>(
632                    &self,
633                    rng: &mut R,
634                    subject: Self::Subject<'_, D>,
635                    certificate: &Self::Certificate,
636                    _strategy: &impl commonware_parallel::Strategy,
637                ) -> bool
638                where
639                    R: rand_core::CryptoRngCore,
640                    D: $crate::Digest,
641                    M: commonware_utils::Faults,
642                {
643                    self.generic
644                        .verify_certificate::<Self, _, D, M>(rng, subject, certificate)
645                }
646
647                fn verify_certificates<'a, R, D, I, M>(
648                    &self,
649                    rng: &mut R,
650                    certificates: I,
651                    _strategy: &impl commonware_parallel::Strategy,
652                ) -> bool
653                where
654                    R: rand::Rng + rand::CryptoRng,
655                    D: $crate::Digest,
656                    I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
657                    M: commonware_utils::Faults,
658                {
659                    self.generic
660                        .verify_certificates::<Self, _, D, _, M>(rng, certificates)
661                }
662
663                fn is_attributable() -> bool {
664                    $crate::ed25519::certificate::Generic::<$namespace>::is_attributable()
665                }
666
667                fn is_batchable() -> bool {
668                    $crate::ed25519::certificate::Generic::<$namespace>::is_batchable()
669                }
670
671                fn certificate_codec_config(
672                    &self,
673                ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
674                    self.generic.certificate_codec_config()
675                }
676
677                fn certificate_codec_config_unbounded(
678                ) -> <Self::Certificate as commonware_codec::Read>::Cfg {
679                    $crate::ed25519::certificate::Generic::<$namespace>::certificate_codec_config_unbounded()
680                }
681            }
682        };
683    }
684}
685
686#[cfg(test)]
687mod tests {
688    use super::*;
689    use crate::{
690        certificate::Scheme as _, impl_certificate_ed25519, sha256::Digest as Sha256Digest,
691    };
692    use bytes::Bytes;
693    use commonware_codec::{Decode, Encode};
694    use commonware_math::algebra::Random;
695    use commonware_parallel::Sequential;
696    use commonware_utils::{ordered::Set, test_rng, Faults, N3f1, Participant, TryCollect};
697
698    const NAMESPACE: &[u8] = b"test-ed25519";
699    const MESSAGE: &[u8] = b"test message";
700
701    /// Test context type for generic scheme tests.
702    #[derive(Clone, Debug)]
703    pub struct TestSubject {
704        pub message: Bytes,
705    }
706
707    impl Subject for TestSubject {
708        type Namespace = Vec<u8>;
709
710        fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] {
711            derived.as_ref()
712        }
713
714        fn message(&self) -> Bytes {
715            self.message.clone()
716        }
717    }
718
719    // Use the macro to generate the test scheme
720    impl_certificate_ed25519!(TestSubject, Vec<u8>);
721
722    fn setup_signers(rng: &mut impl CryptoRngCore, n: u32) -> (Vec<Scheme>, Scheme) {
723        let private_keys: Vec<_> = (0..n).map(|_| PrivateKey::random(&mut *rng)).collect();
724        let participants: Set<PublicKey> = private_keys
725            .iter()
726            .map(|sk| sk.public_key())
727            .try_collect()
728            .unwrap();
729
730        let signers = private_keys
731            .into_iter()
732            .map(|sk| Scheme::signer(NAMESPACE, participants.clone(), sk).unwrap())
733            .collect();
734
735        let verifier = Scheme::verifier(NAMESPACE, participants);
736
737        (signers, verifier)
738    }
739
740    #[test]
741    fn test_is_attributable() {
742        assert!(Generic::<Vec<u8>>::is_attributable());
743        assert!(Scheme::is_attributable());
744    }
745
746    #[test]
747    #[cfg(feature = "std")]
748    fn test_is_batchable() {
749        assert!(Generic::<Vec<u8>>::is_batchable());
750        assert!(Scheme::is_batchable());
751    }
752
753    #[test]
754    #[cfg(not(feature = "std"))]
755    fn test_is_not_batchable() {
756        assert!(!Generic::is_batchable());
757        assert!(!Scheme::is_batchable());
758    }
759
760    #[test]
761    fn test_sign_vote_roundtrip() {
762        let mut rng = test_rng();
763        let (schemes, _) = setup_signers(&mut rng, 4);
764        let scheme = &schemes[0];
765
766        let attestation = scheme
767            .sign::<Sha256Digest>(TestSubject {
768                message: Bytes::from_static(MESSAGE),
769            })
770            .unwrap();
771        assert!(scheme.verify_attestation::<_, Sha256Digest>(
772            &mut rng,
773            TestSubject {
774                message: Bytes::from_static(MESSAGE),
775            },
776            &attestation,
777            &Sequential,
778        ));
779    }
780
781    #[test]
782    fn test_verifier_cannot_sign() {
783        let mut rng = test_rng();
784        let (_, verifier) = setup_signers(&mut rng, 4);
785        assert!(verifier
786            .sign::<Sha256Digest>(TestSubject {
787                message: Bytes::from_static(MESSAGE)
788            })
789            .is_none());
790    }
791
792    #[test]
793    fn test_verify_attestations_filters_invalid() {
794        let mut rng = test_rng();
795        let (schemes, _) = setup_signers(&mut rng, 5);
796        let quorum = N3f1::quorum(schemes.len()) as usize;
797
798        let attestations: Vec<_> = schemes
799            .iter()
800            .take(quorum)
801            .map(|s| {
802                s.sign::<Sha256Digest>(TestSubject {
803                    message: Bytes::from_static(MESSAGE),
804                })
805                .unwrap()
806            })
807            .collect();
808
809        let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
810            &mut rng,
811            TestSubject {
812                message: Bytes::from_static(MESSAGE),
813            },
814            attestations.clone(),
815            &Sequential,
816        );
817        assert!(result.invalid.is_empty());
818        assert_eq!(result.verified.len(), quorum);
819
820        // Test 1: Corrupt one attestation - invalid signer index
821        let mut attestations_corrupted = attestations.clone();
822        attestations_corrupted[0].signer = Participant::new(999);
823        let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
824            &mut rng,
825            TestSubject {
826                message: Bytes::from_static(MESSAGE),
827            },
828            attestations_corrupted,
829            &Sequential,
830        );
831        assert_eq!(result.invalid, vec![Participant::new(999)]);
832        assert_eq!(result.verified.len(), quorum - 1);
833
834        // Test 2: Corrupt one attestation - invalid signature
835        let mut attestations_corrupted = attestations;
836        attestations_corrupted[0].signature = attestations_corrupted[1].signature.clone();
837        let result = schemes[0].verify_attestations::<_, Sha256Digest, _>(
838            &mut rng,
839            TestSubject {
840                message: Bytes::from_static(MESSAGE),
841            },
842            attestations_corrupted,
843            &Sequential,
844        );
845        // Batch verification may detect either signer 0 (wrong sig) or signer 1 (duplicate sig)
846        assert_eq!(result.invalid.len(), 1);
847        assert_eq!(result.verified.len(), quorum - 1);
848    }
849
850    #[test]
851    fn test_assemble_certificate() {
852        let mut rng = test_rng();
853        let (schemes, _) = setup_signers(&mut rng, 4);
854        let quorum = N3f1::quorum(schemes.len()) as usize;
855
856        let attestations: Vec<_> = schemes
857            .iter()
858            .take(quorum)
859            .map(|s| {
860                s.sign::<Sha256Digest>(TestSubject {
861                    message: Bytes::from_static(MESSAGE),
862                })
863                .unwrap()
864            })
865            .collect();
866
867        let certificate = schemes[0]
868            .assemble::<_, N3f1>(attestations, &Sequential)
869            .unwrap();
870
871        // Verify certificate has correct number of signers
872        assert_eq!(certificate.signers.count(), quorum);
873        assert_eq!(certificate.signatures.len(), quorum);
874    }
875
876    #[test]
877    fn test_assemble_certificate_sorts_signers() {
878        let mut rng = test_rng();
879        let (schemes, _) = setup_signers(&mut rng, 4);
880
881        // Get indices and sort them to create attestations in guaranteed reverse order
882        let mut indexed: Vec<_> = (0..3).map(|i| (schemes[i].me().unwrap(), i)).collect();
883        indexed.sort_by_key(|(idx, _)| *idx);
884
885        // Create attestations in reverse sorted order (guaranteed non-sorted)
886        let attestations = vec![
887            schemes[indexed[2].1]
888                .sign::<Sha256Digest>(TestSubject {
889                    message: Bytes::from_static(MESSAGE),
890                })
891                .unwrap(),
892            schemes[indexed[1].1]
893                .sign::<Sha256Digest>(TestSubject {
894                    message: Bytes::from_static(MESSAGE),
895                })
896                .unwrap(),
897            schemes[indexed[0].1]
898                .sign::<Sha256Digest>(TestSubject {
899                    message: Bytes::from_static(MESSAGE),
900                })
901                .unwrap(),
902        ];
903
904        let certificate = schemes[0]
905            .assemble::<_, N3f1>(attestations, &Sequential)
906            .unwrap();
907
908        // Verify signers are sorted by signer index
909        let expected: Vec<_> = indexed.iter().map(|(idx, _)| *idx).collect();
910        assert_eq!(certificate.signers.iter().collect::<Vec<_>>(), expected);
911    }
912
913    #[test]
914    fn test_verify_certificate() {
915        let mut rng = test_rng();
916        let (schemes, verifier) = setup_signers(&mut rng, 4);
917        let quorum = N3f1::quorum(schemes.len()) as usize;
918
919        let attestations: Vec<_> = schemes
920            .iter()
921            .take(quorum)
922            .map(|s| {
923                s.sign::<Sha256Digest>(TestSubject {
924                    message: Bytes::from_static(MESSAGE),
925                })
926                .unwrap()
927            })
928            .collect();
929
930        let certificate = schemes[0]
931            .assemble::<_, N3f1>(attestations, &Sequential)
932            .unwrap();
933
934        assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
935            &mut rng,
936            TestSubject {
937                message: Bytes::from_static(MESSAGE)
938            },
939            &certificate,
940            &Sequential,
941        ));
942    }
943
944    #[test]
945    fn test_verify_certificate_detects_corruption() {
946        let mut rng = test_rng();
947        let (schemes, verifier) = setup_signers(&mut rng, 4);
948        let quorum = N3f1::quorum(schemes.len()) as usize;
949
950        let attestations: Vec<_> = schemes
951            .iter()
952            .take(quorum)
953            .map(|s| {
954                s.sign::<Sha256Digest>(TestSubject {
955                    message: Bytes::from_static(MESSAGE),
956                })
957                .unwrap()
958            })
959            .collect();
960
961        let certificate = schemes[0]
962            .assemble::<_, N3f1>(attestations, &Sequential)
963            .unwrap();
964
965        // Valid certificate passes
966        assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
967            &mut rng,
968            TestSubject {
969                message: Bytes::from_static(MESSAGE),
970            },
971            &certificate,
972            &Sequential,
973        ));
974
975        // Corrupted certificate fails
976        let mut corrupted = certificate;
977        corrupted.signatures[0] = corrupted.signatures[1].clone();
978        assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
979            &mut rng,
980            TestSubject {
981                message: Bytes::from_static(MESSAGE),
982            },
983            &corrupted,
984            &Sequential,
985        ));
986    }
987
988    #[test]
989    fn test_certificate_codec_roundtrip() {
990        let mut rng = test_rng();
991        let (schemes, _) = setup_signers(&mut rng, 4);
992        let quorum = N3f1::quorum(schemes.len()) as usize;
993
994        let attestations: Vec<_> = schemes
995            .iter()
996            .take(quorum)
997            .map(|s| {
998                s.sign::<Sha256Digest>(TestSubject {
999                    message: Bytes::from_static(MESSAGE),
1000                })
1001                .unwrap()
1002            })
1003            .collect();
1004
1005        let certificate = schemes[0]
1006            .assemble::<_, N3f1>(attestations, &Sequential)
1007            .unwrap();
1008        let encoded = certificate.encode();
1009        let decoded = Certificate::decode_cfg(encoded, &schemes.len()).expect("decode certificate");
1010        assert_eq!(decoded, certificate);
1011    }
1012
1013    #[test]
1014    fn test_certificate_rejects_sub_quorum() {
1015        let mut rng = test_rng();
1016        let (schemes, _) = setup_signers(&mut rng, 4);
1017        let sub_quorum = 2; // Less than quorum (3)
1018
1019        let attestations: Vec<_> = schemes
1020            .iter()
1021            .take(sub_quorum)
1022            .map(|s| {
1023                s.sign::<Sha256Digest>(TestSubject {
1024                    message: Bytes::from_static(MESSAGE),
1025                })
1026                .unwrap()
1027            })
1028            .collect();
1029
1030        assert!(schemes[0]
1031            .assemble::<_, N3f1>(attestations, &Sequential)
1032            .is_none());
1033    }
1034
1035    #[test]
1036    fn test_certificate_rejects_invalid_signer() {
1037        let mut rng = test_rng();
1038        let (schemes, _) = setup_signers(&mut rng, 4);
1039        let quorum = N3f1::quorum(schemes.len()) as usize;
1040
1041        let mut attestations: Vec<_> = schemes
1042            .iter()
1043            .take(quorum)
1044            .map(|s| {
1045                s.sign::<Sha256Digest>(TestSubject {
1046                    message: Bytes::from_static(MESSAGE),
1047                })
1048                .unwrap()
1049            })
1050            .collect();
1051
1052        // Corrupt signer index to be out of range
1053        attestations[0].signer = Participant::new(999);
1054
1055        assert!(schemes[0]
1056            .assemble::<_, N3f1>(attestations, &Sequential)
1057            .is_none());
1058    }
1059
1060    #[test]
1061    fn test_verify_certificate_rejects_sub_quorum() {
1062        let mut rng = test_rng();
1063        let (schemes, verifier) = setup_signers(&mut rng, 4);
1064        let participants_len = schemes.len();
1065
1066        let attestations: Vec<_> = schemes
1067            .iter()
1068            .take(3)
1069            .map(|s| {
1070                s.sign::<Sha256Digest>(TestSubject {
1071                    message: Bytes::from_static(MESSAGE),
1072                })
1073                .unwrap()
1074            })
1075            .collect();
1076
1077        let mut certificate = schemes[0]
1078            .assemble::<_, N3f1>(attestations, &Sequential)
1079            .unwrap();
1080
1081        // Artificially truncate to below quorum
1082        let mut signers: Vec<Participant> = certificate.signers.iter().collect();
1083        signers.pop();
1084        certificate.signers = Signers::from(participants_len, signers);
1085        certificate.signatures.pop();
1086
1087        assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1088            &mut rng,
1089            TestSubject {
1090                message: Bytes::from_static(MESSAGE),
1091            },
1092            &certificate,
1093            &Sequential,
1094        ));
1095    }
1096
1097    #[test]
1098    fn test_verify_certificate_rejects_mismatched_signature_count() {
1099        let mut rng = test_rng();
1100        let (schemes, verifier) = setup_signers(&mut rng, 4);
1101
1102        let attestations: Vec<_> = schemes
1103            .iter()
1104            .take(3)
1105            .map(|s| {
1106                s.sign::<Sha256Digest>(TestSubject {
1107                    message: Bytes::from_static(MESSAGE),
1108                })
1109                .unwrap()
1110            })
1111            .collect();
1112
1113        let mut certificate = schemes[0]
1114            .assemble::<_, N3f1>(attestations, &Sequential)
1115            .unwrap();
1116
1117        // Remove one signature but keep signers bitmap unchanged
1118        certificate.signatures.pop();
1119
1120        assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1121            &mut rng,
1122            TestSubject {
1123                message: Bytes::from_static(MESSAGE),
1124            },
1125            &certificate,
1126            &Sequential,
1127        ));
1128    }
1129
1130    #[test]
1131    fn test_verify_certificates_batch() {
1132        let mut rng = test_rng();
1133        let (schemes, verifier) = setup_signers(&mut rng, 4);
1134        let quorum = N3f1::quorum(schemes.len()) as usize;
1135
1136        let messages: Vec<Bytes> = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()]
1137            .into_iter()
1138            .map(Bytes::copy_from_slice)
1139            .collect();
1140        let mut certificates = Vec::new();
1141
1142        for msg in &messages {
1143            let attestations: Vec<_> = schemes
1144                .iter()
1145                .take(quorum)
1146                .map(|s| {
1147                    s.sign::<Sha256Digest>(TestSubject {
1148                        message: msg.clone(),
1149                    })
1150                    .unwrap()
1151                })
1152                .collect();
1153            certificates.push(
1154                schemes[0]
1155                    .assemble::<_, N3f1>(attestations, &Sequential)
1156                    .unwrap(),
1157            );
1158        }
1159
1160        let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
1161            (
1162                TestSubject {
1163                    message: msg.clone(),
1164                },
1165                cert,
1166            )
1167        });
1168
1169        assert!(verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1170            &mut rng,
1171            certs_iter,
1172            &Sequential
1173        ));
1174    }
1175
1176    #[test]
1177    fn test_verify_certificates_batch_detects_failure() {
1178        let mut rng = test_rng();
1179        let (schemes, verifier) = setup_signers(&mut rng, 4);
1180        let quorum = N3f1::quorum(schemes.len()) as usize;
1181
1182        let messages: Vec<Bytes> = [b"msg1".as_slice(), b"msg2".as_slice()]
1183            .into_iter()
1184            .map(Bytes::copy_from_slice)
1185            .collect();
1186        let mut certificates = Vec::new();
1187
1188        for msg in &messages {
1189            let attestations: Vec<_> = schemes
1190                .iter()
1191                .take(quorum)
1192                .map(|s| {
1193                    s.sign::<Sha256Digest>(TestSubject {
1194                        message: msg.clone(),
1195                    })
1196                    .unwrap()
1197                })
1198                .collect();
1199            certificates.push(
1200                schemes[0]
1201                    .assemble::<_, N3f1>(attestations, &Sequential)
1202                    .unwrap(),
1203            );
1204        }
1205
1206        // Corrupt second certificate
1207        certificates[1].signatures[0] = certificates[1].signatures[1].clone();
1208
1209        let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| {
1210            (
1211                TestSubject {
1212                    message: msg.clone(),
1213                },
1214                cert,
1215            )
1216        });
1217
1218        assert!(!verifier.verify_certificates::<_, Sha256Digest, _, N3f1>(
1219            &mut rng,
1220            certs_iter,
1221            &Sequential
1222        ));
1223    }
1224
1225    #[test]
1226    #[should_panic(expected = "duplicate signer index")]
1227    fn test_assemble_certificate_rejects_duplicate_signers() {
1228        let mut rng = test_rng();
1229        let (schemes, _) = setup_signers(&mut rng, 4);
1230
1231        let mut attestations: Vec<_> = schemes
1232            .iter()
1233            .take(3)
1234            .map(|s| {
1235                s.sign::<Sha256Digest>(TestSubject {
1236                    message: Bytes::from_static(MESSAGE),
1237                })
1238                .unwrap()
1239            })
1240            .collect();
1241
1242        // Add a duplicate of the last vote
1243        attestations.push(attestations.last().unwrap().clone());
1244
1245        // This should panic due to duplicate signer
1246        schemes[0].assemble::<_, N3f1>(attestations, &Sequential);
1247    }
1248
1249    #[test]
1250    fn test_scheme_clone_and_verifier() {
1251        let mut rng = test_rng();
1252        let (schemes, _) = setup_signers(&mut rng, 4);
1253        let participants = schemes[0].participants().clone();
1254
1255        // Clone a signer
1256        let signer = schemes[0].clone();
1257        assert!(
1258            signer
1259                .sign::<Sha256Digest>(TestSubject {
1260                    message: Bytes::from_static(MESSAGE),
1261                })
1262                .is_some(),
1263            "signer should produce votes"
1264        );
1265
1266        // A verifier cannot produce votes
1267        let verifier = Scheme::verifier(NAMESPACE, participants);
1268        assert!(
1269            verifier
1270                .sign::<Sha256Digest>(TestSubject {
1271                    message: Bytes::from_static(MESSAGE),
1272                })
1273                .is_none(),
1274            "verifier should not produce votes"
1275        );
1276    }
1277
1278    #[test]
1279    fn test_certificate_decode_validation() {
1280        let mut rng = test_rng();
1281        let (schemes, _) = setup_signers(&mut rng, 4);
1282        let participants_len = schemes.len();
1283
1284        let attestations: Vec<_> = schemes
1285            .iter()
1286            .take(3)
1287            .map(|s| {
1288                s.sign::<Sha256Digest>(TestSubject {
1289                    message: Bytes::from_static(MESSAGE),
1290                })
1291                .unwrap()
1292            })
1293            .collect();
1294
1295        let certificate = schemes[0]
1296            .assemble::<_, N3f1>(attestations, &Sequential)
1297            .unwrap();
1298
1299        // Well-formed certificate decodes successfully
1300        let encoded = certificate.encode();
1301        let decoded =
1302            Certificate::decode_cfg(encoded, &participants_len).expect("decode certificate");
1303        assert_eq!(decoded, certificate);
1304
1305        // Certificate with no signers is rejected
1306        let empty = Certificate {
1307            signers: Signers::from(participants_len, std::iter::empty::<Participant>()),
1308            signatures: Vec::new(),
1309        };
1310        assert!(Certificate::decode_cfg(empty.encode(), &participants_len).is_err());
1311
1312        // Certificate with mismatched signature count is rejected
1313        let mismatched = Certificate {
1314            signers: Signers::from(participants_len, [0u32, 1].map(Participant::new)),
1315            signatures: vec![certificate.signatures[0].clone()],
1316        };
1317        assert!(Certificate::decode_cfg(mismatched.encode(), &participants_len).is_err());
1318
1319        // Certificate containing more signers than the participant set is rejected
1320        let mut signers = certificate.signers.iter().collect::<Vec<_>>();
1321        signers.push(Participant::from_usize(participants_len));
1322        let mut sigs = certificate.signatures.clone();
1323        sigs.push(certificate.signatures[0].clone());
1324        let extended = Certificate {
1325            signers: Signers::from(participants_len + 1, signers),
1326            signatures: sigs,
1327        };
1328        assert!(Certificate::decode_cfg(extended.encode(), &participants_len).is_err());
1329    }
1330
1331    #[test]
1332    fn test_verify_certificate_rejects_unknown_signer() {
1333        let mut rng = test_rng();
1334        let (schemes, verifier) = setup_signers(&mut rng, 4);
1335        let participants_len = schemes.len();
1336
1337        let attestations: Vec<_> = schemes
1338            .iter()
1339            .take(3)
1340            .map(|s| {
1341                s.sign::<Sha256Digest>(TestSubject {
1342                    message: Bytes::from_static(MESSAGE),
1343                })
1344                .unwrap()
1345            })
1346            .collect();
1347
1348        let mut certificate = schemes[0]
1349            .assemble::<_, N3f1>(attestations, &Sequential)
1350            .unwrap();
1351
1352        // Add an unknown signer (out of range)
1353        let mut signers: Vec<Participant> = certificate.signers.iter().collect();
1354        signers.push(Participant::from_usize(participants_len));
1355        certificate.signers = Signers::from(participants_len + 1, signers);
1356        certificate
1357            .signatures
1358            .push(certificate.signatures[0].clone());
1359
1360        assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1361            &mut rng,
1362            TestSubject {
1363                message: Bytes::from_static(MESSAGE),
1364            },
1365            &certificate,
1366            &Sequential,
1367        ));
1368    }
1369
1370    #[test]
1371    fn test_verify_certificate_rejects_invalid_certificate_signers_size() {
1372        let mut rng = test_rng();
1373        let (schemes, verifier) = setup_signers(&mut rng, 4);
1374        let participants_len = schemes.len();
1375
1376        let attestations: Vec<_> = schemes
1377            .iter()
1378            .take(3)
1379            .map(|s| {
1380                s.sign::<Sha256Digest>(TestSubject {
1381                    message: Bytes::from_static(MESSAGE),
1382                })
1383                .unwrap()
1384            })
1385            .collect();
1386
1387        let mut certificate = schemes[0]
1388            .assemble::<_, N3f1>(attestations, &Sequential)
1389            .unwrap();
1390
1391        // Valid certificate passes
1392        assert!(verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1393            &mut rng,
1394            TestSubject {
1395                message: Bytes::from_static(MESSAGE),
1396            },
1397            &certificate,
1398            &Sequential,
1399        ));
1400
1401        // Make the signers bitmap size larger (mismatched with participants)
1402        let signers: Vec<Participant> = certificate.signers.iter().collect();
1403        certificate.signers = Signers::from(participants_len + 1, signers);
1404
1405        // Certificate verification should fail due to size mismatch
1406        assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1407            &mut rng,
1408            TestSubject {
1409                message: Bytes::from_static(MESSAGE),
1410            },
1411            &certificate,
1412            &Sequential,
1413        ));
1414    }
1415
1416    #[test]
1417    fn test_verify_certificate_rejects_signers_size_mismatch() {
1418        let mut rng = test_rng();
1419        let (schemes, verifier) = setup_signers(&mut rng, 4);
1420        let participants_len = schemes.len();
1421
1422        let attestations: Vec<_> = schemes
1423            .iter()
1424            .take(3)
1425            .map(|s| {
1426                s.sign::<Sha256Digest>(TestSubject {
1427                    message: Bytes::from_static(MESSAGE),
1428                })
1429                .unwrap()
1430            })
1431            .collect();
1432
1433        let mut certificate = schemes[0]
1434            .assemble::<_, N3f1>(attestations, &Sequential)
1435            .unwrap();
1436
1437        // Make the signers bitmap size larger than participants
1438        let signers: Vec<Participant> = certificate.signers.iter().collect();
1439        certificate.signers = Signers::from(participants_len + 1, signers);
1440        certificate
1441            .signatures
1442            .push(certificate.signatures[0].clone());
1443
1444        assert!(!verifier.verify_certificate::<_, Sha256Digest, N3f1>(
1445            &mut rng,
1446            TestSubject {
1447                message: Bytes::from_static(MESSAGE),
1448            },
1449            &certificate,
1450            &Sequential,
1451        ));
1452    }
1453
1454    #[cfg(feature = "arbitrary")]
1455    mod conformance {
1456        use super::*;
1457        use commonware_codec::conformance::CodecConformance;
1458
1459        commonware_conformance::conformance_tests! {
1460            CodecConformance<Certificate>,
1461        }
1462    }
1463}