Skip to main content

commonware_cryptography/secp256r1/certificate/
mod.rs

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