use crate::VerificationError;
use bcx_core::Digest;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SignatureAlgorithm {
Ed25519,
MlDsa65,
SlhDsaSha2_128s,
HybridEd25519MlDsa65,
}
impl SignatureAlgorithm {
#[must_use]
pub const fn expected_signature_len(self) -> usize {
match self {
Self::Ed25519 => 64,
Self::MlDsa65 => 3_293,
Self::SlhDsaSha2_128s => 7_856,
Self::HybridEd25519MlDsa65 => 3_357,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct AlgorithmPolicy<'a> {
admitted: &'a [SignatureAlgorithm],
}
impl<'a> AlgorithmPolicy<'a> {
#[must_use]
pub const fn new(admitted: &'a [SignatureAlgorithm]) -> Self {
Self { admitted }
}
#[must_use]
pub const fn admits(&self, algorithm: SignatureAlgorithm) -> bool {
let mut index = 0;
while index < self.admitted.len() {
if self.admitted[index].eq_const(algorithm) {
return true;
}
index += 1;
}
false
}
}
impl SignatureAlgorithm {
const fn eq_const(self, other: Self) -> bool {
matches!(
(self, other),
(Self::Ed25519, Self::Ed25519)
| (Self::MlDsa65, Self::MlDsa65)
| (Self::SlhDsaSha2_128s, Self::SlhDsaSha2_128s)
| (Self::HybridEd25519MlDsa65, Self::HybridEd25519MlDsa65)
)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SignatureEnvelope<'a> {
pub key_id: Digest,
pub algorithm: SignatureAlgorithm,
pub signature: &'a [u8],
}
impl SignatureEnvelope<'_> {
pub const fn validate(&self, maximum_signature_len: usize) -> Result<(), VerificationError> {
if self.key_id.is_zero() {
return Err(VerificationError::EmptyKeyId);
}
if self.signature.is_empty() {
return Err(VerificationError::EmptySignature);
}
if self.signature.len() > maximum_signature_len {
return Err(VerificationError::SignatureTooLarge);
}
if self.signature.len() != self.algorithm.expected_signature_len() {
return Err(VerificationError::InvalidSignature);
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SignedEnvelope<'a, T> {
pub payload: T,
pub signature: SignatureEnvelope<'a>,
}
impl<'a, T> SignedEnvelope<'a, T> {
pub fn verify_bytes<V: Verifier>(
&self,
verifier: &V,
algorithm_policy: &AlgorithmPolicy<'_>,
canonical_payload: &[u8],
maximum_signature_len: usize,
) -> Result<(), VerificationError> {
if !algorithm_policy.admits(self.signature.algorithm) {
return Err(VerificationError::AlgorithmNotAdmitted);
}
self.signature.validate(maximum_signature_len)?;
verifier.verify(&self.signature, canonical_payload)
}
}
pub trait Verifier {
fn verify(
&self,
envelope: &SignatureEnvelope<'_>,
canonical_payload: &[u8],
) -> Result<(), VerificationError>;
}