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 {
#[must_use]
pub fn is_verified(&self) -> bool {
self.signers.iter().any(|s| s.verified)
}
#[must_use]
pub fn all_verified(&self) -> bool {
!self.signers.is_empty() && self.signers.iter().all(|s| s.verified)
}
pub fn verified_signers(&self) -> impl Iterator<Item = &SignerResult> {
self.signers.iter().filter(|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,
},
#[deprecated(since = "0.2.0", note = "match CertificateExpired instead")]
AllTrustAnchorsExpired {
issuer: String,
},
SignatureVerification {
subject: String,
},
#[deprecated = "never produced by the current pkix-chain-based validator; match TooDeep instead"]
PathLenViolated {
intermediate_count: usize,
path_len: u8,
},
NotACa {
subject: String,
},
Cycle {
subject: String,
},
NoMatchingIssuer {
issuer: String,
},
TooDeep,
Other(String),
}
#[allow(deprecated)]
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 the maximum allowed depth")
}
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),
BoundaryCollision,
}
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, "{msg}"),
SmimeError::WrongContentType(msg) => write!(f, "wrong content type: {msg}"),
SmimeError::BoundaryCollision => write!(
f,
"could not generate a unique MIME boundary after 8 retries"
),
SmimeError::AllSignersFailed(signers) => {
write!(f, "{} signer(s) failed: ", signers.len())?;
for (i, signer) in signers.iter().enumerate() {
if i > 0 {
write!(f, "; ")?;
}
let reason = signer.error.as_deref().unwrap_or("unknown");
write!(f, "{reason}")?;
}
Ok(())
}
}
}
}
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)
}
}
#[derive(Serialize, Deserialize)]
#[serde(tag = "type", content = "detail")]
enum SmimeErrorRepr {
Der(String),
UnsupportedAlgorithm(String),
NoMatchingRecipient,
SignatureVerification,
CertChain(CertChainError),
MalformedInput(String),
NoRecipients,
RngFailure(String),
DecryptionFailed(String),
Other(String),
AllSignersFailed(Vec<SignerResult>),
WrongContentType(String),
BoundaryCollision,
}
impl From<&SmimeError> for SmimeErrorRepr {
fn from(e: &SmimeError) -> Self {
match e {
SmimeError::Der(de) => SmimeErrorRepr::Der(de.to_string()),
SmimeError::UnsupportedAlgorithm(s) => SmimeErrorRepr::UnsupportedAlgorithm(s.clone()),
SmimeError::NoMatchingRecipient => SmimeErrorRepr::NoMatchingRecipient,
SmimeError::SignatureVerification => SmimeErrorRepr::SignatureVerification,
SmimeError::CertChain(c) => SmimeErrorRepr::CertChain(c.clone()),
SmimeError::MalformedInput(s) => SmimeErrorRepr::MalformedInput(s.clone()),
SmimeError::NoRecipients => SmimeErrorRepr::NoRecipients,
SmimeError::RngFailure(s) => SmimeErrorRepr::RngFailure(s.clone()),
SmimeError::DecryptionFailed(s) => SmimeErrorRepr::DecryptionFailed(s.clone()),
SmimeError::Other(s) => SmimeErrorRepr::Other(s.clone()),
SmimeError::AllSignersFailed(v) => SmimeErrorRepr::AllSignersFailed(v.clone()),
SmimeError::WrongContentType(s) => SmimeErrorRepr::WrongContentType(s.clone()),
SmimeError::BoundaryCollision => SmimeErrorRepr::BoundaryCollision,
}
}
}
impl From<SmimeErrorRepr> for SmimeError {
fn from(r: SmimeErrorRepr) -> Self {
match r {
SmimeErrorRepr::Der(s) => SmimeError::Other(format!("DER error: {s}")),
SmimeErrorRepr::UnsupportedAlgorithm(s) => SmimeError::UnsupportedAlgorithm(s),
SmimeErrorRepr::NoMatchingRecipient => SmimeError::NoMatchingRecipient,
SmimeErrorRepr::SignatureVerification => SmimeError::SignatureVerification,
SmimeErrorRepr::CertChain(c) => SmimeError::CertChain(c),
SmimeErrorRepr::MalformedInput(s) => SmimeError::MalformedInput(s),
SmimeErrorRepr::NoRecipients => SmimeError::NoRecipients,
SmimeErrorRepr::RngFailure(s) => SmimeError::RngFailure(s),
SmimeErrorRepr::DecryptionFailed(s) => SmimeError::DecryptionFailed(s),
SmimeErrorRepr::Other(s) => SmimeError::Other(s),
SmimeErrorRepr::AllSignersFailed(v) => SmimeError::AllSignersFailed(v),
SmimeErrorRepr::WrongContentType(s) => SmimeError::WrongContentType(s),
SmimeErrorRepr::BoundaryCollision => SmimeError::BoundaryCollision,
}
}
}
impl Serialize for SmimeError {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
SmimeErrorRepr::from(self).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for SmimeError {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
SmimeErrorRepr::deserialize(deserializer).map(SmimeError::from)
}
}