use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct VerificationResult {
pub signers: Vec<SignerResult>,
}
impl VerificationResult {
pub fn is_verified(&self) -> bool {
self.signers.iter().any(|s| s.verified)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SignerResult {
pub verified: bool,
pub subject: Option<String>,
pub error: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum CertChainError {
NoTrustAnchors,
CertificateExpired {
subject: String,
not_after: String,
},
AllTrustAnchorsExpired {
issuer: String,
},
SignatureVerification {
subject: String,
},
PathLenViolated {
intermediate_count: usize,
path_len: u8,
},
NotACa {
subject: String,
},
Cycle {
subject: String,
},
NoMatchingIssuer {
issuer: String,
},
TooDeep,
Other(String),
}
impl fmt::Display for CertChainError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CertChainError::NoTrustAnchors => write!(f, "no trust anchors provided"),
CertChainError::CertificateExpired { subject, not_after } => write!(
f,
"certificate '{subject}' expired or not yet valid (not_after={not_after})"
),
CertChainError::AllTrustAnchorsExpired { issuer } => write!(
f,
"all trust anchors matching issuer '{issuer}' are expired or not yet valid"
),
CertChainError::SignatureVerification { subject } => {
write!(f, "issuer signature on '{subject}' does not match")
}
CertChainError::PathLenViolated {
intermediate_count,
path_len,
} => write!(
f,
"pathLen constraint violated: {intermediate_count} intermediate CA(s) \
but pathLen is {path_len}"
),
CertChainError::NotACa { subject } => {
write!(f, "certificate '{subject}' is not a CA")
}
CertChainError::Cycle { subject } => {
write!(f, "certificate chain cycle at '{subject}'")
}
CertChainError::NoMatchingIssuer { issuer } => write!(
f,
"no trust anchor or intermediate matches issuer '{issuer}' \
(add the CA root cert to trust_anchors)"
),
CertChainError::TooDeep => {
write!(f, "certificate chain exceeds maximum depth of 10")
}
CertChainError::Other(msg) => write!(f, "{msg}"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum SmimeError {
Der(der::Error),
UnsupportedAlgorithm(String),
NoMatchingRecipient,
SignatureVerification,
CertChain(CertChainError),
MalformedInput(String),
NoRecipients,
RngFailure(String),
DecryptionFailed(String),
Other(String),
AllSignersFailed(Vec<SignerResult>),
WrongContentType(String),
}
impl fmt::Display for SmimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SmimeError::Der(e) => write!(f, "DER error: {e}"),
SmimeError::UnsupportedAlgorithm(alg) => {
write!(f, "unsupported algorithm: {alg}")
}
SmimeError::NoMatchingRecipient => {
write!(f, "no decryption key matches any recipient")
}
SmimeError::SignatureVerification => write!(f, "signature verification failed"),
SmimeError::CertChain(e) => write!(f, "certificate chain error: {e}"),
SmimeError::MalformedInput(msg) => write!(f, "malformed CMS input: {msg}"),
SmimeError::NoRecipients => write!(f, "encrypt() called with no recipients"),
SmimeError::RngFailure(msg) => write!(f, "RNG failure: {msg}"),
SmimeError::DecryptionFailed(msg) => write!(f, "decryption failed: {msg}"),
SmimeError::Other(msg) => write!(f, "error: {msg}"),
SmimeError::WrongContentType(msg) => write!(f, "wrong content type: {msg}"),
SmimeError::AllSignersFailed(signers) => {
let first_error = signers
.first()
.and_then(|s| s.error.as_deref())
.unwrap_or("unknown");
write!(
f,
"signature verification failed: {} signer(s) all failed — first error: {}",
signers.len(),
first_error
)
}
}
}
}
impl std::error::Error for SmimeError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
SmimeError::Der(e) => Some(e),
_ => None,
}
}
}
impl From<der::Error> for SmimeError {
fn from(e: der::Error) -> Self {
SmimeError::Der(e)
}
}