Skip to main content

commonware_cryptography/
certificate.rs

1//! Cryptographic primitives for generating and verifying certificates.
2//!
3//! This module provides the [`Verifier`] and [`Scheme`] traits and implementations for
4//! producing signatures, validating them (individually or in batches), assembling certificates, and
5//! verifying recovered certificates.
6//!
7//! # Pluggable Cryptography
8//!
9//! Certificates are generic over the signing scheme, allowing users to choose
10//! the scheme best suited for their requirements:
11//!
12//! - [`ed25519`]: Attributable signatures with individual verification. HSM-friendly, no trusted
13//!   setup required, and widely supported. Certificates contain individual signatures from each
14//!   signer.
15//!
16//! - [`secp256r1`]: Attributable signatures with individual verification. HSM-friendly, no trusted
17//!   setup required, and widely supported by hardware security modules. Unlike ed25519, does not
18//!   benefit from batch verification. Certificates contain individual signatures from each signer.
19#![cfg_attr(
20    feature = "bls12381",
21    doc = "
22- [`bls12381_multisig`]: Attributable signatures with aggregated verification. Signatures
23  can be aggregated into a single multi-signature for compact certificates while preserving
24  attribution (signer indices are stored alongside the aggregated signature). Callers must verify
25  a proof of possession for every BLS signing key before constructing the scheme.
26
27- [`bls12381_threshold`]: Non-attributable threshold signatures. Produces succinct
28  certificates that are constant-size regardless of committee size. Requires a trusted
29  setup (distributed key generation) and cannot attribute signatures to individual signers.
30"
31)]
32//! # Attributable Schemes and Fault Evidence
33//!
34//! Signing schemes differ in whether per-participant activities can be used as evidence of
35//! either liveness or of committing a fault:
36//!
37//! - **Attributable Schemes**: Individual signatures can be presented to some third party as
38//!   evidence of either liveness or of committing a fault. Certificates contain signer indices
39//!   alongside individual signatures, enabling secure per-participant activity tracking and
40//!   conflict detection.
41//!
42//! - **Non-Attributable Schemes**: Individual signatures cannot be presented to some third party
43//!   as evidence because they can be forged after collecting a quorum of partial signatures.
44//!   Authenticated peer connections allow this evidence to be used locally, but not by an external
45//!   observer.
46//!
47//! The [`Scheme::is_attributable()`] associated function signals whether evidence can be safely
48//! exposed to third parties.
49//!
50//! # Identity Keys vs Signing Keys
51//!
52//! A participant may supply both an identity key and a signing key. The identity key
53//! is used for assigning a unique order to the participant set and authenticating connections
54//! whereas the signing key is used for producing and verifying signatures/certificates.
55//!
56#![cfg_attr(
57    feature = "bls12381",
58    doc = "Some cryptographic schemes are only performant when used in batch verification (like
59[`bls12381_multisig`]) and/or are refreshed frequently (like [`bls12381_threshold`])."
60)]
61//! Refer to [ed25519] for an example of a scheme that uses the same key for both purposes.
62
63#[cfg(feature = "bls12381")]
64pub use crate::bls12381::certificate::{
65    multisig as bls12381_multisig, threshold as bls12381_threshold,
66};
67pub use crate::ed25519::certificate as ed25519;
68#[commonware_macros::stability(ALPHA)]
69pub use crate::secp256r1::certificate as secp256r1;
70use crate::{Digest, PublicKey};
71#[cfg(not(feature = "std"))]
72use alloc::{collections::BTreeSet, sync::Arc, vec, vec::Vec};
73use bytes::{Buf, BufMut, Bytes};
74use commonware_codec::{
75    Codec, CodecFixed, EncodeSize, Error as CodecError, Read, ReadExt, Write, types::lazy::Lazy,
76};
77use commonware_parallel::Strategy;
78use commonware_utils::{Faults, Participant, bitmap::BitMap, iter::NonEmpty, ordered::Set};
79use core::{fmt::Debug, hash::Hash};
80use rand_core::CryptoRng;
81#[cfg(feature = "std")]
82use std::{collections::BTreeSet, sync::Arc, vec::Vec};
83use thiserror::Error;
84
85/// A participant's attestation for a certificate.
86#[derive(Clone, Debug)]
87pub struct Attestation<S: Scheme> {
88    /// Index of the signer inside the participant set.
89    pub signer: Participant,
90    /// Scheme-specific signature or share produced for a given subject.
91    pub signature: Lazy<S::Signature>,
92}
93
94impl<S: Scheme> PartialEq for Attestation<S> {
95    fn eq(&self, other: &Self) -> bool {
96        self.signer == other.signer && self.signature == other.signature
97    }
98}
99
100impl<S: Scheme> Eq for Attestation<S> {}
101
102impl<S: Scheme> Hash for Attestation<S> {
103    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
104        self.signer.hash(state);
105        self.signature.hash(state);
106    }
107}
108
109impl<S: Scheme> Write for Attestation<S> {
110    fn write(&self, writer: &mut impl BufMut) {
111        self.signer.write(writer);
112        self.signature.write(writer);
113    }
114}
115
116impl<S: Scheme> EncodeSize for Attestation<S> {
117    fn encode_size(&self) -> usize {
118        self.signer.encode_size() + self.signature.encode_size()
119    }
120}
121
122impl<S: Scheme> Read for Attestation<S> {
123    type Cfg = ();
124
125    fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
126        let signer = Participant::read(reader)?;
127        let signature = ReadExt::read(reader)?;
128
129        Ok(Self { signer, signature })
130    }
131}
132
133#[cfg(feature = "arbitrary")]
134impl<S: Scheme> arbitrary::Arbitrary<'_> for Attestation<S>
135where
136    S::Signature: for<'a> arbitrary::Arbitrary<'a>,
137{
138    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
139        let signer = Participant::arbitrary(u)?;
140        let signature = S::Signature::arbitrary(u)?;
141        Ok(Self {
142            signer,
143            signature: signature.into(),
144        })
145    }
146}
147
148/// Result of batch-verifying attestations.
149pub struct Verification<S: Scheme> {
150    /// Contains the attestations accepted by the scheme.
151    pub verified: Vec<Attestation<S>>,
152    /// Identifies the participant indices rejected during batch verification.
153    pub invalid: Vec<Participant>,
154}
155
156impl<S: Scheme> Verification<S> {
157    /// Creates a new `Verification` result.
158    pub const fn new(verified: Vec<Attestation<S>>, invalid: Vec<Participant>) -> Self {
159        Self { verified, invalid }
160    }
161}
162
163/// Errors returned while assembling attestations into a certificate.
164#[derive(Debug, Error, PartialEq, Eq)]
165pub enum AssemblyError {
166    /// Structurally valid attestations do not meet the scheme's quorum.
167    #[error("insufficient attestations: expected={0}, found={1}")]
168    InsufficientAttestations(u32, u32),
169    /// An attestation references an index outside the participant set.
170    #[error("unknown signer: {0}")]
171    UnknownSigner(Participant),
172    /// More than one attestation references the same signer.
173    #[error("duplicate signer: {0}")]
174    DuplicateSigner(Participant),
175    /// An attestation contains a malformed encoded signature.
176    #[error("malformed signature from signer: {0}")]
177    MalformedSignature(Participant),
178    /// The scheme could not recover a certificate from structurally valid attestations.
179    #[error("certificate recovery failed")]
180    RecoveryFailed,
181}
182
183/// Trait for namespace types that can derive themselves from a base namespace.
184///
185/// This trait is implemented by namespace types to define how they are computed
186/// from a base namespace string.
187pub trait Namespace: Clone + Send + Sync {
188    /// Derive a namespace from the given base.
189    fn derive(namespace: &[u8]) -> Self;
190}
191
192impl Namespace for Vec<u8> {
193    fn derive(namespace: &[u8]) -> Self {
194        namespace.to_vec()
195    }
196}
197
198/// Identifies the subject of a signature or certificate.
199pub trait Subject: Clone + Debug + Send + Sync {
200    /// Pre-computed namespace(s) for this subject type.
201    type Namespace: Namespace;
202
203    /// Get the namespace bytes for this subject instance.
204    fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8];
205
206    /// Get the message bytes for this subject instance.
207    fn message(&self) -> Bytes;
208}
209
210/// Cryptographic surface for recovered certificate verification.
211///
212/// A `Verifier` verifies recovered certificates, but does not expose participant
213/// metadata or signing operations.
214pub trait Verifier: Clone + Debug + Send + Sync + 'static {
215    /// Subject type for certificate verification.
216    type Subject<'a, D: Digest>: Subject;
217
218    /// Fault model used to compute certificate quorums.
219    type Faults: Faults;
220
221    /// Public key type for participant identity used to order and index the participant set.
222    type PublicKey: PublicKey;
223
224    /// Certificate assembled from a set of attestations.
225    type Certificate: Clone + Debug + PartialEq + Eq + Hash + Send + Sync + Codec;
226
227    /// Verifies a certificate that was recovered or received from the network.
228    fn verify_certificate<R, D>(
229        &self,
230        rng: &mut R,
231        subject: Self::Subject<'_, D>,
232        certificate: &Self::Certificate,
233        strategy: &impl Strategy,
234    ) -> bool
235    where
236        R: CryptoRng,
237        D: Digest;
238
239    /// Verifies a non-empty stream of certificates, returning `false` at the first verification
240    /// failure.
241    fn verify_certificates<'a, R, D, I>(
242        &self,
243        rng: &mut R,
244        certificates: NonEmpty<I>,
245        strategy: &impl Strategy,
246    ) -> bool
247    where
248        R: CryptoRng,
249        D: Digest,
250        I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
251    {
252        for (subject, certificate) in certificates {
253            if !self.verify_certificate(rng, subject, certificate, strategy) {
254                return false;
255            }
256        }
257
258        true
259    }
260
261    /// Batch-verifies certificates and returns a per-item result.
262    ///
263    /// For batchable schemes, attempts batch verification first and bisects
264    /// on failure to efficiently identify invalid certificates. For
265    /// non-batchable schemes, verifies each certificate individually.
266    /// Empty input returns an empty result.
267    fn verify_certificates_bisect<'a, R, D>(
268        &self,
269        rng: &mut R,
270        certificates: &[(Self::Subject<'a, D>, &'a Self::Certificate)],
271        strategy: &impl Strategy,
272    ) -> Vec<bool>
273    where
274        R: CryptoRng,
275        D: Digest,
276        Self::Subject<'a, D>: Copy,
277        Self::Certificate: 'a,
278    {
279        let len = certificates.len();
280        let mut verified = vec![false; len];
281        if len == 0 {
282            return verified;
283        }
284
285        // Non-batchable schemes (e.g. secp256r1) gain nothing from bisection
286        // since verify_certificates already checks one-by-one.
287        if !Self::is_batchable() {
288            for (i, (subject, certificate)) in certificates.iter().enumerate() {
289                verified[i] = self.verify_certificate(rng, *subject, certificate, strategy);
290            }
291            return verified;
292        }
293
294        // Iterative bisection: try the full range first. If batch verification
295        // passes, mark the entire range valid. If it fails, split in half and
296        // retry each half. Singletons that fail remain false.
297        //
298        //       [0..8) fail
299        //      /            \
300        //   [0..4) pass   [4..8) fail
301        //                /          \
302        //            [4..6) pass  [6..8) fail
303        //                        /        \
304        //                    [6..7) pass  [7..8) fail
305        let mut stack = vec![(0, len)];
306        while let Some((start, end)) = stack.pop() {
307            let (first, rest) = certificates[start..end]
308                .split_first()
309                .expect("bisection ranges are non-empty");
310            let certificates = NonEmpty::new(*first, rest.iter().copied());
311            if self.verify_certificates(rng, certificates, strategy) {
312                verified[start..end].fill(true);
313            } else if end - start > 1 {
314                let mid = start + (end - start) / 2;
315                stack.push((mid, end));
316                stack.push((start, mid));
317            }
318        }
319
320        verified
321    }
322
323    /// Returns whether this scheme benefits from batch verification.
324    ///
325    /// Schemes that benefit from batch verification should return `true`, allowing callers to
326    /// optimize by deferring verification until multiple signatures are available.
327    ///
328    /// Schemes that don't benefit from batch verification (like [`secp256r1`]) should
329    /// return `false`, indicating that eager per-signature verification is preferred.
330    fn is_batchable() -> bool;
331
332    /// Encoding configuration for bounded-size certificate decoding used in network payloads.
333    fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg;
334
335    /// Encoding configuration that allows unbounded certificate decoding.
336    ///
337    /// Only use this when decoding data from trusted local storage, it must not be exposed to
338    /// adversarial inputs or network payloads.
339    fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg;
340}
341
342/// Cryptographic surface for multi-party certificate schemes.
343///
344/// A `Scheme` extends [`Verifier`] with the signing surface: it produces attestations, validates
345/// them (individually or in batches), and assembles certificates. Implementations may override the
346/// provided defaults to take advantage of scheme-specific batching strategies.
347pub trait Scheme: Verifier {
348    /// Signature emitted by individual participants.
349    type Signature: Clone + Debug + PartialEq + Eq + Hash + Send + Sync + CodecFixed<Cfg = ()>;
350
351    /// Returns the index of "self" in the participant set, if available.
352    /// Returns `None` if the scheme is a verifier-only instance.
353    fn me(&self) -> Option<Participant>;
354
355    /// Returns the ordered set of participant public identity keys managed by the scheme.
356    fn participants(&self) -> &Set<Self::PublicKey>;
357
358    /// Signs a subject.
359    /// Returns `None` if the scheme cannot sign (e.g. it's a verifier-only instance).
360    fn sign<D: Digest>(&self, subject: Self::Subject<'_, D>) -> Option<Attestation<Self>>;
361
362    /// Verifies a single attestation against the participant material managed by the scheme.
363    fn verify_attestation<R, D>(
364        &self,
365        rng: &mut R,
366        subject: Self::Subject<'_, D>,
367        attestation: &Attestation<Self>,
368        strategy: &impl Strategy,
369    ) -> bool
370    where
371        R: CryptoRng,
372        D: Digest;
373
374    /// Batch-verifies attestations and separates valid attestations from signer indices that failed
375    /// verification. Empty input produces an empty result in both sets.
376    ///
377    /// Callers must not include duplicate attestations from the same signer: duplicates may
378    /// produce incorrect results.
379    fn verify_attestations<R, D, I>(
380        &self,
381        rng: &mut R,
382        subject: Self::Subject<'_, D>,
383        attestations: I,
384        strategy: &impl Strategy,
385    ) -> Verification<Self>
386    where
387        R: CryptoRng,
388        D: Digest,
389        I: IntoIterator<Item = Attestation<Self>>,
390        I::IntoIter: Send,
391    {
392        let mut invalid = BTreeSet::new();
393
394        let verified = attestations.into_iter().filter_map(|attestation| {
395            if self.verify_attestation(&mut *rng, subject.clone(), &attestation, strategy) {
396                Some(attestation)
397            } else {
398                invalid.insert(attestation.signer);
399                None
400            }
401        });
402
403        Verification::new(verified.collect(), invalid.into_iter().collect())
404    }
405
406    /// Assembles a non-empty stream of attestations into a certificate.
407    ///
408    /// Insufficient input returns [`AssemblyError::InsufficientAttestations`].
409    /// A signer-unique quorum already verified for one subject must assemble successfully.
410    fn assemble<I>(
411        &self,
412        attestations: NonEmpty<I>,
413        strategy: &impl Strategy,
414    ) -> Result<Self::Certificate, AssemblyError>
415    where
416        I: Iterator<Item = Attestation<Self>> + Send;
417
418    /// Returns whether per-participant fault evidence can be safely exposed.
419    ///
420    /// Schemes where individual signatures can be safely reported as fault evidence should
421    /// return `true`.
422    fn is_attributable() -> bool;
423}
424
425/// A scheme handle returned by a [`Provider`] for one scope.
426///
427/// Always usable as a [`Verifier`] for certificate verification. The full signing scheme is
428/// recoverable with [`Scoped::into_scheme`] only when the scope was built with [`Scoped::scheme`].
429/// A scope built with [`Scoped::verifier`] yields `None`.
430#[derive(Clone, Debug)]
431pub struct Scoped<S: Scheme> {
432    scheme: Arc<S>,
433    can_sign: bool,
434}
435
436impl<S: Scheme> Scoped<S> {
437    /// Builds a verify-only scope.
438    pub const fn verifier(scheme: Arc<S>) -> Self {
439        Self {
440            scheme,
441            can_sign: false,
442        }
443    }
444
445    /// Builds a full signing scope.
446    pub const fn scheme(scheme: Arc<S>) -> Self {
447        Self {
448            scheme,
449            can_sign: true,
450        }
451    }
452
453    /// Returns the full signing scheme, or `None` for a verify-only scope.
454    pub fn into_scheme(self) -> Option<Arc<S>> {
455        self.can_sign.then_some(self.scheme)
456    }
457}
458
459impl<S: Scheme> Verifier for Scoped<S> {
460    type Subject<'a, D: Digest> = S::Subject<'a, D>;
461    type Faults = S::Faults;
462    type PublicKey = S::PublicKey;
463    type Certificate = S::Certificate;
464
465    fn verify_certificate<R, D>(
466        &self,
467        rng: &mut R,
468        subject: Self::Subject<'_, D>,
469        certificate: &Self::Certificate,
470        strategy: &impl Strategy,
471    ) -> bool
472    where
473        R: CryptoRng,
474        D: Digest,
475    {
476        self.scheme
477            .verify_certificate(rng, subject, certificate, strategy)
478    }
479
480    fn verify_certificates<'a, R, D, I>(
481        &self,
482        rng: &mut R,
483        certificates: NonEmpty<I>,
484        strategy: &impl Strategy,
485    ) -> bool
486    where
487        R: CryptoRng,
488        D: Digest,
489        I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
490    {
491        self.scheme.verify_certificates(rng, certificates, strategy)
492    }
493
494    fn is_batchable() -> bool {
495        S::is_batchable()
496    }
497
498    fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg {
499        self.scheme.certificate_codec_config()
500    }
501
502    fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg {
503        S::certificate_codec_config_unbounded()
504    }
505}
506
507/// Supplies the signing scheme for a given scope.
508///
509/// This trait uses an associated `Scope` type, allowing implementations to work
510/// with any scope representation (e.g., epoch numbers, block heights, etc.).
511pub trait Provider: Clone + Send + Sync + 'static {
512    /// The scope type used to look up schemes.
513    type Scope: Clone + Send + Sync + 'static;
514    /// The signing scheme to provide.
515    type Scheme: Scheme;
516
517    /// Return a [`Scoped`] for `scope` capable of verifying certificates produced under it.
518    ///
519    /// A scheme that can verify certificates from any scope without scope-specific state should
520    /// return a verify-only [`Scoped`] (via [`Scoped::verifier`]) for every scope. A fixed group
521    /// public key that survives committee rotation is one such case. A scheme that needs
522    /// scope-specific verification state should return `None` once that state is unavailable.
523    fn scoped(&self, scope: Self::Scope) -> Option<Scoped<Self::Scheme>>;
524
525    /// Return the full signing scheme that corresponds to `scope`, if available.
526    ///
527    /// The default returns a scheme only when [`Provider::scoped`] yields a signing scope, so a
528    /// verify-only scope produces `None`. Override this when the signing scheme is available even
529    /// for scopes that [`Provider::scoped`] serves with a verify-only result.
530    fn scheme(&self, scope: Self::Scope) -> Option<Arc<Self::Scheme>> {
531        self.scoped(scope).and_then(Scoped::into_scheme)
532    }
533}
534
535/// Bitmap wrapper that tracks which participants signed a certificate.
536///
537/// Internally, it stores bits in 1-byte chunks for compact encoding.
538#[derive(Clone, Debug, PartialEq, Eq, Hash)]
539pub struct Signers {
540    bitmap: BitMap<1>,
541}
542
543impl Signers {
544    /// Builds [`Signers`] from an iterator of signer indices.
545    ///
546    /// Indices need not be sorted: some signing schemes aggregate commutatively, so ordering
547    /// is left to the caller.
548    pub fn new(
549        participants: u32,
550        signers: impl IntoIterator<Item = Participant>,
551    ) -> Result<Self, AssemblyError> {
552        let mut bitmap = BitMap::zeroes(u64::from(participants));
553        for signer in signers.into_iter() {
554            let index = u64::from(signer.get());
555            if index >= bitmap.len() {
556                return Err(AssemblyError::UnknownSigner(signer));
557            }
558            if bitmap.get(index) {
559                return Err(AssemblyError::DuplicateSigner(signer));
560            }
561            bitmap.set(index, true);
562        }
563
564        Ok(Self { bitmap })
565    }
566
567    /// Requires at least `required` signers in an already validated set.
568    pub(crate) fn require(self, required: u32) -> Result<Self, AssemblyError> {
569        let found = u32::try_from(self.count()).expect("signer count exceeds u32::MAX");
570        if found < required {
571            return Err(AssemblyError::InsufficientAttestations(required, found));
572        }
573
574        Ok(self)
575    }
576
577    /// Returns the length of the bitmap (the size of the participant set).
578    #[allow(clippy::len_without_is_empty)]
579    pub const fn len(&self) -> usize {
580        self.bitmap.len() as usize
581    }
582
583    /// Returns how many participants are marked as signers.
584    pub fn count(&self) -> usize {
585        self.bitmap.count_ones() as usize
586    }
587
588    /// Iterates over signer indices in ascending order.
589    pub fn iter(&self) -> impl Iterator<Item = Participant> + '_ {
590        self.bitmap
591            .ones_iter()
592            .map(|index| Participant::from_usize(index as usize))
593    }
594}
595
596/// Builds [`Signers`] using the participant set as the valid signer-index range.
597///
598/// # Panics
599///
600/// Panics if the participant count exceeds `u32::MAX`.
601impl<'a, P, I> TryFrom<(&'a Set<P>, I)> for Signers
602where
603    I: IntoIterator<Item = Participant>,
604{
605    type Error = AssemblyError;
606
607    fn try_from((participants, signers): (&'a Set<P>, I)) -> Result<Self, Self::Error> {
608        let total = u32::try_from(participants.len()).expect("participant count exceeds u32::MAX");
609        Self::new(total, signers)
610    }
611}
612
613impl Write for Signers {
614    fn write(&self, writer: &mut impl BufMut) {
615        self.bitmap.write(writer);
616    }
617}
618
619impl EncodeSize for Signers {
620    fn encode_size(&self) -> usize {
621        self.bitmap.encode_size()
622    }
623}
624
625impl Read for Signers {
626    type Cfg = usize;
627
628    fn read_cfg(reader: &mut impl Buf, max_participants: &usize) -> Result<Self, CodecError> {
629        let bitmap = BitMap::read_cfg(reader, &(*max_participants as u64))?;
630        // The participant count is treated as an upper bound for decoding flexibility, e.g. one
631        // might use `Scheme::certificate_codec_config_unbounded` for decoding certificates from
632        // local storage.
633        //
634        // Exact length validation **must** be enforced at verification time by the signing schemes
635        // against the actual participant set size.
636        Ok(Self { bitmap })
637    }
638}
639
640#[cfg(feature = "arbitrary")]
641impl arbitrary::Arbitrary<'_> for Signers {
642    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
643        let participants = u.arbitrary_len::<u8>()? % 10;
644        let signer_count = u.arbitrary_len::<u8>()?.min(participants);
645        let signers = (0..u32::try_from(signer_count).expect("signer count exceeds u32::MAX"))
646            .map(Participant::new)
647            .collect::<Vec<_>>();
648        Ok(Self::new(participants.try_into().unwrap(), signers)
649            .expect("indices are unique and in range"))
650    }
651}
652
653/// A scheme provider that always returns the same scheme regardless of scope.
654#[derive(Clone, Debug)]
655pub struct ConstantProvider<S: Scheme, Sc = ()> {
656    scheme: Arc<S>,
657    _scope: core::marker::PhantomData<Sc>,
658}
659
660impl<S: Scheme, Sc> ConstantProvider<S, Sc> {
661    /// Creates a new provider that always returns the given scheme.
662    pub fn new(scheme: S) -> Self {
663        Self {
664            scheme: Arc::new(scheme),
665            _scope: core::marker::PhantomData,
666        }
667    }
668}
669
670impl<S: Scheme, Sc: Clone + Send + Sync + 'static> crate::certificate::Provider
671    for ConstantProvider<S, Sc>
672{
673    type Scope = Sc;
674    type Scheme = S;
675
676    fn scoped(&self, _: Sc) -> Option<Scoped<S>> {
677        Some(Scoped::scheme(self.scheme.clone()))
678    }
679}
680
681#[cfg(feature = "mocks")]
682pub mod mocks;
683
684#[cfg(test)]
685mod tests {
686    use super::*;
687    use crate::{Signer as _, ed25519::PrivateKey, sha256::Digest as Sha256Digest};
688    use commonware_codec::{Decode, Encode};
689    use commonware_math::algebra::Random;
690    use commonware_parallel::Sequential;
691    use commonware_utils::{TryCollect, non_empty, ordered::Set, test_rng};
692    use ed25519_fixture::{Scheme as Ed25519Scheme, TestSubject};
693
694    #[test]
695    fn test_new_signers() {
696        let signers = Signers::new(6, [0, 3, 5].map(Participant::new)).unwrap();
697        let collected: Vec<_> = signers.iter().collect();
698        assert_eq!(
699            collected,
700            vec![0, 3, 5]
701                .into_iter()
702                .map(Participant::new)
703                .collect::<Vec<_>>()
704        );
705        assert_eq!(signers.count(), 3);
706    }
707
708    #[test]
709    fn test_new_out_of_bounds() {
710        assert_eq!(
711            Signers::new(4, [0, 4].map(Participant::new)),
712            Err(AssemblyError::UnknownSigner(Participant::new(4)))
713        );
714    }
715
716    #[test]
717    fn test_new_duplicate() {
718        assert_eq!(
719            Signers::new(4, [0, 0, 1].map(Participant::new)),
720            Err(AssemblyError::DuplicateSigner(Participant::new(0)))
721        );
722    }
723
724    #[test]
725    fn test_new_not_increasing() {
726        assert!(Signers::new(4, [2, 1].map(Participant::new)).is_ok());
727    }
728
729    #[test]
730    fn test_try_from_set_and_require() {
731        let participants = Set::from_iter_dedup(0..4);
732        let signers = Signers::try_from((&participants, [0, 2, 3].map(Participant::new)))
733            .unwrap()
734            .require(3)
735            .unwrap();
736        assert_eq!(signers.count(), 3);
737        assert_eq!(
738            Signers::try_from((&participants, [0, 2].map(Participant::new)))
739                .unwrap()
740                .require(3),
741            Err(AssemblyError::InsufficientAttestations(3, 2))
742        );
743    }
744
745    #[test]
746    fn test_try_from_set_checks_signer_bounds() {
747        let participants = Set::<u8>::default();
748        let signer = Participant::new(0);
749        assert_eq!(
750            Signers::try_from((&participants, [signer])),
751            Err(AssemblyError::UnknownSigner(signer))
752        );
753    }
754
755    #[test]
756    fn test_codec_round_trip() {
757        let signers = Signers::new(9, [1, 6].map(Participant::new)).unwrap();
758        let encoded = signers.encode();
759        let decoded = Signers::decode_cfg(encoded, &9).unwrap();
760        assert_eq!(decoded, signers);
761    }
762
763    #[test]
764    fn test_decode_respects_participant_limit() {
765        let signers = Signers::new(8, [0, 3, 7].map(Participant::new)).unwrap();
766        let encoded = signers.encode();
767        // More participants than expected should fail.
768        assert!(Signers::decode_cfg(encoded.clone(), &2).is_err());
769        // Exact participant bound succeeds.
770        assert!(Signers::decode_cfg(encoded.clone(), &8).is_ok());
771        // Less participants than expected succeeds (upper bound).
772        assert!(Signers::decode_cfg(encoded, &10).is_ok());
773    }
774
775    mod ed25519_fixture {
776        use crate::{certificate::Subject, impl_certificate_ed25519};
777        use commonware_utils::N3f1;
778
779        /// Test subject for certificate verification tests.
780        #[derive(Copy, Clone, Debug)]
781        pub struct TestSubject {
782            pub message: &'static [u8],
783        }
784
785        impl Subject for TestSubject {
786            type Namespace = Vec<u8>;
787
788            fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] {
789                derived
790            }
791
792            fn message(&self) -> bytes::Bytes {
793                bytes::Bytes::from_static(self.message)
794            }
795        }
796
797        // Use the macro to generate the test scheme
798        impl_certificate_ed25519!(TestSubject, Vec<u8>, N3f1);
799    }
800
801    const NAMESPACE: &[u8] = b"test-bisect";
802    const MESSAGE: &[u8] = b"good message";
803    const BAD_MESSAGE: &[u8] = b"bad message";
804
805    fn make_certificate(
806        schemes: &[Ed25519Scheme],
807        message: &'static [u8],
808    ) -> <Ed25519Scheme as Verifier>::Certificate {
809        let attestations: Vec<_> = schemes
810            .iter()
811            .filter_map(|s| s.sign::<Sha256Digest>(TestSubject { message }))
812            .collect();
813        schemes[0]
814            .assemble(non_empty![@attestations], &Sequential)
815            .expect("assembly failed")
816    }
817
818    fn setup_ed25519(n: u32) -> (Vec<Ed25519Scheme>, Ed25519Scheme) {
819        let mut rng = test_rng();
820        let private_keys: Vec<_> = (0..n).map(|_| PrivateKey::random(&mut rng)).collect();
821        let participants: Set<crate::ed25519::PublicKey> = private_keys
822            .iter()
823            .map(|sk| sk.public_key())
824            .try_collect()
825            .unwrap();
826        let signers: Vec<_> = private_keys
827            .into_iter()
828            .map(|sk| Ed25519Scheme::signer(NAMESPACE, participants.clone(), sk).unwrap())
829            .collect();
830        let verifier = Ed25519Scheme::verifier(NAMESPACE, participants);
831        (signers, verifier)
832    }
833
834    #[test]
835    fn test_bisect_empty() {
836        let mut rng = test_rng();
837        let (_, verifier) = setup_ed25519(4);
838        let result =
839            verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &[], &Sequential);
840        assert!(result.is_empty());
841    }
842
843    #[test]
844    fn test_bisect_all_valid() {
845        let mut rng = test_rng();
846        let (schemes, verifier) = setup_ed25519(4);
847        let cert = make_certificate(&schemes, MESSAGE);
848        let good = TestSubject { message: MESSAGE };
849        let pairs: Vec<_> = (0..5).map(|_| (good, &cert)).collect();
850        let result =
851            verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &pairs, &Sequential);
852        assert_eq!(result, vec![true; 5]);
853    }
854
855    #[test]
856    fn test_bisect_mixed() {
857        let mut rng = test_rng();
858        let (schemes, verifier) = setup_ed25519(4);
859        let cert = make_certificate(&schemes, MESSAGE);
860        let good = TestSubject { message: MESSAGE };
861        let bad = TestSubject {
862            message: BAD_MESSAGE,
863        };
864        let pairs = vec![
865            (good, &cert),
866            (bad, &cert),
867            (good, &cert),
868            (bad, &cert),
869            (good, &cert),
870            (good, &cert),
871            (bad, &cert),
872            (bad, &cert),
873        ];
874        let expected = vec![true, false, true, false, true, true, false, false];
875        let result =
876            verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &pairs, &Sequential);
877        assert_eq!(result, expected);
878    }
879
880    #[test]
881    fn test_bisect_all_invalid() {
882        let mut rng = test_rng();
883        let (schemes, verifier) = setup_ed25519(4);
884        let cert = make_certificate(&schemes, MESSAGE);
885        let bad = TestSubject {
886            message: BAD_MESSAGE,
887        };
888        let pairs: Vec<_> = (0..4).map(|_| (bad, &cert)).collect();
889        let result =
890            verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &pairs, &Sequential);
891        assert_eq!(result, vec![false; 4]);
892    }
893
894    #[test]
895    fn test_bisect_single_valid() {
896        let mut rng = test_rng();
897        let (schemes, verifier) = setup_ed25519(4);
898        let cert = make_certificate(&schemes, MESSAGE);
899        let pairs = vec![(TestSubject { message: MESSAGE }, &cert)];
900        let result =
901            verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &pairs, &Sequential);
902        assert_eq!(result, vec![true]);
903    }
904
905    #[test]
906    fn test_bisect_single_invalid() {
907        let mut rng = test_rng();
908        let (schemes, verifier) = setup_ed25519(4);
909        let cert = make_certificate(&schemes, MESSAGE);
910        let pairs = vec![(
911            TestSubject {
912                message: BAD_MESSAGE,
913            },
914            &cert,
915        )];
916        let result =
917            verifier.verify_certificates_bisect::<_, Sha256Digest>(&mut rng, &pairs, &Sequential);
918        assert_eq!(result, vec![false]);
919    }
920
921    #[test]
922    fn test_scoped_verifies_and_into_scheme() {
923        let mut rng = test_rng();
924        let (schemes, verifier) = setup_ed25519(4);
925        let cert = make_certificate(&schemes, MESSAGE);
926        let subject = TestSubject { message: MESSAGE };
927
928        let as_verifier = Scoped::verifier(Arc::new(verifier));
929        let as_scheme = Scoped::scheme(Arc::new(schemes[0].clone()));
930
931        // Both scopes verify a valid certificate through the `Verifier` impl.
932        assert!(as_verifier.verify_certificate::<_, Sha256Digest>(
933            &mut rng,
934            subject,
935            &cert,
936            &Sequential,
937        ));
938        assert!(as_scheme.verify_certificate::<_, Sha256Digest>(
939            &mut rng,
940            subject,
941            &cert,
942            &Sequential,
943        ));
944        let pairs = [(subject, &cert)];
945        assert!(as_verifier.verify_certificates::<_, Sha256Digest, _>(
946            &mut rng,
947            non_empty![@pairs.iter().copied()],
948            &Sequential,
949        ));
950        assert_eq!(
951            <Scoped<Ed25519Scheme> as Verifier>::is_batchable(),
952            <Ed25519Scheme as Verifier>::is_batchable(),
953        );
954        assert_eq!(
955            as_verifier.certificate_codec_config(),
956            schemes[0].certificate_codec_config(),
957        );
958        let _ = <Scoped<Ed25519Scheme> as Verifier>::certificate_codec_config_unbounded();
959
960        // A verify-only scope never yields its scheme; a full scope always does.
961        assert!(as_verifier.into_scheme().is_none());
962        assert!(as_scheme.into_scheme().is_some());
963
964        let provider = ConstantProvider::<_, ()>::new(schemes[0].clone());
965        assert!(provider.scoped(()).is_some());
966        assert!(provider.scheme(()).is_some());
967    }
968
969    #[cfg(feature = "arbitrary")]
970    mod conformance {
971        use super::{ed25519_fixture::Scheme, *};
972        use commonware_codec::conformance::CodecConformance;
973
974        commonware_conformance::conformance_tests! {
975            CodecConformance<Signers>,
976            CodecConformance<Attestation<Scheme>>,
977        }
978    }
979}