#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::borrow::Cow;
#[cfg(feature = "std")]
use std::fmt;
#[cfg(not(feature = "std"))]
use core::fmt;
use dcrypt_api::{Error as CoreError, Result as CoreResult};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
Parameter {
name: Cow<'static, str>, reason: Cow<'static, str>, },
Length {
context: &'static str,
expected: usize,
actual: usize,
},
Authentication {
algorithm: &'static str,
},
Randomness,
NotImplemented {
feature: &'static str,
},
Processing {
operation: &'static str,
details: &'static str,
},
MacError {
algorithm: &'static str,
details: &'static str,
},
#[cfg(feature = "std")]
External {
source: &'static str,
details: String,
},
#[cfg(not(feature = "std"))]
External {
source: &'static str,
},
Other(&'static str),
}
impl Error {
pub fn param<N: Into<Cow<'static, str>>, R: Into<Cow<'static, str>>>(
name: N,
reason: R,
) -> Self {
Error::Parameter {
name: name.into(),
reason: reason.into(),
}
}
}
pub type Result<T> = core::result::Result<T, Error>;
pub type CipherResult<T> = Result<T>;
pub type HashResult<T> = Result<T>;
pub type MacResult<T> = Result<T>;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Parameter { name, reason } => {
write!(f, "Invalid parameter '{}': {}", name, reason)
}
Error::Length {
context,
expected,
actual,
} => {
write!(
f,
"Invalid length for {}: expected {}, got {}",
context, expected, actual
)
}
Error::Authentication { algorithm } => {
write!(f, "Authentication failed for {}", algorithm)
}
Error::Randomness => f.write_str("Caller-provided randomness source failed"),
Error::NotImplemented { feature } => {
write!(f, "Feature not implemented: {}", feature)
}
Error::Processing { operation, details } => {
write!(f, "Processing error in {}: {}", operation, details)
}
Error::MacError { algorithm, details } => {
write!(f, "MAC error in {}: {}", algorithm, details)
}
#[cfg(feature = "std")]
Error::External { source, details } => {
write!(f, "External error from {}: {}", source, details)
}
#[cfg(not(feature = "std"))]
Error::External { source } => {
write!(f, "External error from {}", source)
}
Error::Other(msg) => write!(f, "{}", msg),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl From<Error> for CoreError {
fn from(err: Error) -> Self {
match err {
Error::Parameter { name, reason } => {
let context = match &name {
Cow::Borrowed(name) => *name,
Cow::Owned(_) => "algorithm parameter",
};
#[cfg(not(feature = "std"))]
let _ = reason;
CoreError::InvalidParameter {
context,
#[cfg(feature = "std")]
message: match name {
Cow::Borrowed(_) => reason.into_owned(),
Cow::Owned(name) => format!("{name}: {reason}"),
},
}
}
Error::Length {
context,
expected,
actual,
} => CoreError::InvalidLength {
context,
expected,
actual,
},
Error::Authentication { algorithm } => CoreError::AuthenticationFailed {
context: algorithm,
#[cfg(feature = "std")]
message: "authentication failed".to_string(),
},
Error::Randomness => CoreError::RandomGenerationError {
context: "caller-provided RNG",
#[cfg(feature = "std")]
message: "caller-provided randomness source failed".to_string(),
},
Error::NotImplemented { feature } => CoreError::NotImplemented { feature },
Error::Processing { operation, details } => {
#[cfg(not(feature = "std"))]
let _ = details;
CoreError::Other {
context: operation,
#[cfg(feature = "std")]
message: details.to_string(),
}
}
Error::MacError { algorithm, details } => {
#[cfg(not(feature = "std"))]
let _ = details;
CoreError::Other {
context: algorithm,
#[cfg(feature = "std")]
message: details.to_string(),
}
}
#[cfg(feature = "std")]
Error::External { source, details } => CoreError::Other {
context: source,
message: details,
},
#[cfg(not(feature = "std"))]
Error::External { source } => CoreError::Other {
context: source,
#[cfg(feature = "std")]
message: "external error".to_string(),
},
Error::Other(msg) => {
#[cfg(not(feature = "std"))]
let _ = msg;
CoreError::Other {
context: "primitives",
#[cfg(feature = "std")]
message: msg.to_string(),
}
}
}
}
}
impl From<dcrypt_internal::random::Error> for Error {
fn from(_: dcrypt_internal::random::Error) -> Self {
Self::Randomness
}
}
#[inline]
pub fn to_core_result<T>(r: Result<T>, ctx: &'static str) -> CoreResult<T> {
r.map_err(|e| {
let mut core = CoreError::from(e);
core = core.with_context(ctx);
core
})
}
pub mod validate;
#[cfg(test)]
mod conversion_tests {
use super::*;
#[test]
fn randomness_preserves_its_public_error_category() {
let error = CoreError::from(Error::Randomness);
assert!(matches!(
error,
CoreError::RandomGenerationError {
context: "caller-provided RNG",
..
}
));
}
#[test]
fn owned_parameter_names_do_not_become_static_allocations() {
let error = CoreError::from(Error::param(
String::from("attacker-controlled name"),
"invalid value",
));
assert!(matches!(
error,
CoreError::InvalidParameter {
context: "algorithm parameter",
..
}
));
}
}