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