use crate::{simplex::elector, types::Round}; use commonware_codec::{types::lazy::Lazy, Encode}; use commonware_cryptography::{ certificate::{Attestation, Verification}, sha256::Sha256, Hasher as _, }; use commonware_parallel::Sequential; use commonware_utils::{modulo, test_rng, Faults, Participant}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Behavior { Honest, CorruptSignature, } #[derive(Clone, Debug)] pub struct Scheme { inner: S, behavior: Behavior, } #[derive(Clone, Debug)] pub struct Elector { inner: E, _phantom: std::marker::PhantomData, } #[derive(Clone, Debug, Default)] pub struct Config(pub L); impl Scheme { pub const fn new(inner: S, behavior: Behavior) -> Self { Self { inner, behavior } } fn to_inner_attestation(attestation: &Attestation) -> Attestation where S: commonware_cryptography::certificate::Scheme, { Attestation { signer: attestation.signer, signature: attestation.signature.clone(), } } fn from_inner_attestation(attestation: Attestation) -> Attestation where S: commonware_cryptography::certificate::Scheme, { Attestation { signer: attestation.signer, signature: attestation.signature, } } fn corrupt_signature( inner: &S, subject: ::Subject<'_, D>, signer: Participant, signature: &::Signature, ) -> Lazy<::Signature> where S: commonware_cryptography::certificate::Scheme, D: commonware_cryptography::Digest, { let encoded = signature.encode().to_vec(); let bit_len = encoded.len() * 8; // Hash the signer and signature bytes to derive a deterministic starting // point for the single-bit flip search. let mut hasher = Sha256::default(); hasher.update(&signer.encode()); hasher.update(&encoded); let digest = hasher.finalize(); // Start from a deterministic but non-trivial bit so tests do not always // mutate the same low-order bit first. let start = modulo(digest.as_ref(), bit_len as u64) as usize; // Search all single-bit mutations, wrapping around from `start`, until // we find one that still decodes as a signature but fails verification. (0..bit_len) .find_map(|offset| { let flip = (start + offset) % bit_len; let byte = flip / 8; let bit = flip % 8; let mut corrupted = encoded.clone(); corrupted[byte] ^= 1 << bit; // `Lazy` lets us reject undecodable byte patterns before asking // the wrapped scheme whether the mutated attestation verifies. let lazy = Lazy::deferred(&mut corrupted.as_slice(), ()); let attestation = Attestation { signer, signature: lazy.clone(), }; (lazy.get().is_some() && !inner.verify_attestation( &mut test_rng(), subject.clone(), &attestation, &Sequential, )) .then_some(lazy) }) .expect("expected at least one invalid but decodable signature mutation") } } impl elector::Config> for Config where S: commonware_cryptography::certificate::Scheme, L: elector::Config, { type Elector = Elector; fn build( self, participants: &commonware_utils::ordered::Set< as commonware_cryptography::certificate::Scheme>::PublicKey, >, ) -> Self::Elector { Elector { inner: self.0.build(participants), _phantom: std::marker::PhantomData, } } } impl elector::Elector> for Elector where S: commonware_cryptography::certificate::Scheme, E: elector::Elector, { fn elect( &self, round: Round, certificate: Option< & as commonware_cryptography::certificate::Scheme>::Certificate, >, ) -> Participant { self.inner.elect(round, certificate) } } impl commonware_cryptography::certificate::Scheme for Scheme where S: commonware_cryptography::certificate::Scheme, { type Subject<'a, D: commonware_cryptography::Digest> = S::Subject<'a, D>; type PublicKey = S::PublicKey; type Signature = S::Signature; type Certificate = S::Certificate; fn me(&self) -> Option { self.inner.me() } fn participants(&self) -> &commonware_utils::ordered::Set { self.inner.participants() } fn sign( &self, subject: Self::Subject<'_, D>, ) -> Option> { let attestation = self.inner.sign(subject.clone())?; let signature = match self.behavior { Behavior::Honest => attestation.signature, Behavior::CorruptSignature => { let signature = attestation .signature .get() .expect("fresh signature should decode"); Self::corrupt_signature(&self.inner, subject, attestation.signer, signature) } }; Some(Attestation { signer: attestation.signer, signature, }) } fn verify_attestation( &self, rng: &mut R, subject: Self::Subject<'_, D>, attestation: &Attestation, strategy: &impl commonware_parallel::Strategy, ) -> bool where R: rand_core::CryptoRngCore, D: commonware_cryptography::Digest, { self.inner.verify_attestation( rng, subject, &Self::to_inner_attestation(attestation), strategy, ) } fn verify_attestations( &self, rng: &mut R, subject: Self::Subject<'_, D>, attestations: I, strategy: &impl commonware_parallel::Strategy, ) -> Verification where R: rand_core::CryptoRngCore, D: commonware_cryptography::Digest, I: IntoIterator>, I::IntoIter: Send, { let verification = self.inner.verify_attestations( rng, subject, attestations.into_iter().map(|attestation| Attestation { signer: attestation.signer, signature: attestation.signature, }), strategy, ); Verification::new( verification .verified .into_iter() .map(Self::from_inner_attestation) .collect(), verification.invalid, ) } fn assemble( &self, attestations: I, strategy: &impl commonware_parallel::Strategy, ) -> Option where I: IntoIterator>, I::IntoIter: Send, M: Faults, { self.inner.assemble::<_, M>( attestations.into_iter().map(|attestation| Attestation { signer: attestation.signer, signature: attestation.signature, }), strategy, ) } fn verify_certificate( &self, rng: &mut R, subject: Self::Subject<'_, D>, certificate: &Self::Certificate, strategy: &impl commonware_parallel::Strategy, ) -> bool where R: rand_core::CryptoRngCore, D: commonware_cryptography::Digest, M: Faults, { self.inner .verify_certificate::<_, _, M>(rng, subject, certificate, strategy) } fn is_attributable() -> bool { S::is_attributable() } fn is_batchable() -> bool { S::is_batchable() } fn certificate_codec_config(&self) -> ::Cfg { self.inner.certificate_codec_config() } fn certificate_codec_config_unbounded() -> ::Cfg { S::certificate_codec_config_unbounded() } }