Skip to main content

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