use crate::VerificationError;
use bcx_core::Digest;
use bcx_wire::WireLimits;
use core::fmt;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SignatureAlgorithm {
Ed25519,
MlDsa65,
SlhDsaSha2_128s,
HybridEd25519MlDsa65,
}
impl SignatureAlgorithm {
pub const ED25519_SIGNATURE_LEN: usize = 64;
pub const ML_DSA_65_SIGNATURE_LEN: usize = 3_293;
pub const SLH_DSA_SHA2_128S_SIGNATURE_LEN: usize = 7_856;
pub const HYBRID_ED25519_ML_DSA_65_SIGNATURE_LEN: usize =
Self::ED25519_SIGNATURE_LEN + Self::ML_DSA_65_SIGNATURE_LEN;
#[must_use]
pub const fn expected_signature_len(self) -> usize {
match self {
Self::Ed25519 => Self::ED25519_SIGNATURE_LEN,
Self::MlDsa65 => Self::ML_DSA_65_SIGNATURE_LEN,
Self::SlhDsaSha2_128s => Self::SLH_DSA_SHA2_128S_SIGNATURE_LEN,
Self::HybridEd25519MlDsa65 => Self::HYBRID_ED25519_ML_DSA_65_SIGNATURE_LEN,
}
}
#[must_use]
pub fn split_hybrid(self, signature: &[u8]) -> Option<(&[u8], &[u8])> {
match self {
Self::HybridEd25519MlDsa65
if signature.len() == Self::HYBRID_ED25519_ML_DSA_65_SIGNATURE_LEN =>
{
Some(signature.split_at(Self::ED25519_SIGNATURE_LEN))
}
Self::Ed25519 | Self::MlDsa65 | Self::SlhDsaSha2_128s | Self::HybridEd25519MlDsa65 => {
None
}
}
}
}
#[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 {
match (self, other) {
(Self::Ed25519, Self::Ed25519) => true,
(Self::Ed25519, Self::MlDsa65) => false,
(Self::Ed25519, Self::SlhDsaSha2_128s) => false,
(Self::Ed25519, Self::HybridEd25519MlDsa65) => false,
(Self::MlDsa65, Self::Ed25519) => false,
(Self::MlDsa65, Self::MlDsa65) => true,
(Self::MlDsa65, Self::SlhDsaSha2_128s) => false,
(Self::MlDsa65, Self::HybridEd25519MlDsa65) => false,
(Self::SlhDsaSha2_128s, Self::Ed25519) => false,
(Self::SlhDsaSha2_128s, Self::MlDsa65) => false,
(Self::SlhDsaSha2_128s, Self::SlhDsaSha2_128s) => true,
(Self::SlhDsaSha2_128s, Self::HybridEd25519MlDsa65) => false,
(Self::HybridEd25519MlDsa65, Self::Ed25519) => false,
(Self::HybridEd25519MlDsa65, Self::MlDsa65) => false,
(Self::HybridEd25519MlDsa65, Self::SlhDsaSha2_128s) => false,
(Self::HybridEd25519MlDsa65, Self::HybridEd25519MlDsa65) => true,
}
}
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct SignatureEnvelope<'a> {
key_id: Digest,
algorithm: SignatureAlgorithm,
signature: &'a [u8],
}
impl<'a> SignatureEnvelope<'a> {
pub fn new(
key_id: Digest,
algorithm: SignatureAlgorithm,
signature: &'a [u8],
limits: WireLimits,
) -> Result<Self, VerificationError> {
let envelope = Self {
key_id,
algorithm,
signature,
};
match envelope.validate(limits) {
Ok(()) => Ok(envelope),
Err(error) => Err(error),
}
}
pub fn validate(&self, limits: WireLimits) -> 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() > limits.maximum_message_len() {
return Err(VerificationError::SignatureTooLarge);
}
if self.signature.len() != self.algorithm.expected_signature_len() {
return Err(VerificationError::InvalidSignature);
}
Ok(())
}
#[must_use]
pub const fn key_id(&self) -> Digest {
self.key_id
}
#[must_use]
pub const fn algorithm(&self) -> SignatureAlgorithm {
self.algorithm
}
#[must_use]
pub const fn signature(&self) -> &'a [u8] {
self.signature
}
}
impl<'a> fmt::Debug for SignatureEnvelope<'a> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("SignatureEnvelope")
.field("key_id", &self.key_id)
.field("algorithm", &self.algorithm)
.field(
"signature",
&format_args!("[{} bytes]", self.signature.len()),
)
.finish()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SignedEnvelope<'a, T> {
payload: T,
signature: SignatureEnvelope<'a>,
}
impl<'a, T> SignedEnvelope<'a, T> {
#[must_use]
pub const fn new(payload: T, signature: SignatureEnvelope<'a>) -> Self {
Self { payload, signature }
}
pub fn verify_detached_bytes<V: Verifier>(
&self,
verifier: &V,
algorithm_policy: &AlgorithmPolicy<'_>,
canonical_payload: &[u8],
limits: WireLimits,
) -> Result<(), VerificationError> {
if !algorithm_policy.admits(self.signature.algorithm) {
return Err(VerificationError::AlgorithmNotAdmitted);
}
self.signature.validate(limits)?;
if canonical_payload.len() > limits.maximum_message_len() {
return Err(VerificationError::PayloadTooLarge);
}
match self.signature.algorithm {
SignatureAlgorithm::HybridEd25519MlDsa65 => verifier
.verify_hybrid(&self.signature, canonical_payload)
.map(|_| ()),
SignatureAlgorithm::Ed25519
| SignatureAlgorithm::MlDsa65
| SignatureAlgorithm::SlhDsaSha2_128s => {
verifier.verify(&self.signature, canonical_payload)
}
}
}
#[must_use]
pub const fn payload(&self) -> &T {
&self.payload
}
#[must_use]
pub const fn signature(&self) -> SignatureEnvelope<'a> {
self.signature
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct HybridVerified(());
pub trait HybridVerifier {
fn verify_ed25519(
&self,
ed25519_signature: &[u8],
canonical_payload: &[u8],
) -> Result<(), VerificationError>;
fn verify_ml_dsa_65(
&self,
ml_dsa_65_signature: &[u8],
canonical_payload: &[u8],
) -> Result<(), VerificationError>;
fn verify_hybrid(
&self,
envelope: &SignatureEnvelope<'_>,
canonical_payload: &[u8],
) -> Result<HybridVerified, VerificationError> {
let (ed25519, ml_dsa_65) = envelope
.algorithm()
.split_hybrid(envelope.signature())
.ok_or(VerificationError::InvalidSignature)?;
self.verify_ed25519(ed25519, canonical_payload)?;
self.verify_ml_dsa_65(ml_dsa_65, canonical_payload)?;
Ok(HybridVerified(()))
}
}
pub trait Verifier: HybridVerifier {
fn verify(
&self,
envelope: &SignatureEnvelope<'_>,
canonical_payload: &[u8],
) -> Result<(), VerificationError>;
}