1#[cfg(feature = "alloc")]
4extern crate alloc;
5
6#[cfg(feature = "alloc")]
7use alloc::borrow::Cow;
8
9#[cfg(feature = "std")]
10use std::fmt;
11
12#[cfg(not(feature = "std"))]
13use core::fmt;
14
15use dcrypt_api::{Error as CoreError, Result as CoreResult};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum Error {
20 Parameter {
22 name: Cow<'static, str>, reason: Cow<'static, str>, },
27
28 Length {
30 context: &'static str,
32 expected: usize,
34 actual: usize,
36 },
37
38 Authentication {
40 algorithm: &'static str,
42 },
43
44 Randomness,
46
47 NotImplemented {
49 feature: &'static str,
51 },
52
53 Processing {
55 operation: &'static str,
57 details: &'static str,
59 },
60
61 MacError {
63 algorithm: &'static str,
65 details: &'static str,
67 },
68
69 #[cfg(feature = "std")]
71 External {
72 source: &'static str,
74 details: String,
76 },
77
78 #[cfg(not(feature = "std"))]
79 External {
81 source: &'static str,
83 },
84
85 Other(&'static str),
87}
88
89impl Error {
91 pub fn param<N: Into<Cow<'static, str>>, R: Into<Cow<'static, str>>>(
93 name: N,
94 reason: R,
95 ) -> Self {
96 Error::Parameter {
97 name: name.into(),
98 reason: reason.into(),
99 }
100 }
101}
102
103pub type Result<T> = core::result::Result<T, Error>;
105
106pub type CipherResult<T> = Result<T>;
109pub type HashResult<T> = Result<T>;
111pub type MacResult<T> = Result<T>;
113
114impl fmt::Display for Error {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 match self {
118 Error::Parameter { name, reason } => {
119 write!(f, "Invalid parameter '{}': {}", name, reason)
120 }
121 Error::Length {
122 context,
123 expected,
124 actual,
125 } => {
126 write!(
127 f,
128 "Invalid length for {}: expected {}, got {}",
129 context, expected, actual
130 )
131 }
132 Error::Authentication { algorithm } => {
133 write!(f, "Authentication failed for {}", algorithm)
134 }
135 Error::Randomness => f.write_str("Caller-provided randomness source failed"),
136 Error::NotImplemented { feature } => {
137 write!(f, "Feature not implemented: {}", feature)
138 }
139 Error::Processing { operation, details } => {
140 write!(f, "Processing error in {}: {}", operation, details)
141 }
142 Error::MacError { algorithm, details } => {
143 write!(f, "MAC error in {}: {}", algorithm, details)
144 }
145 #[cfg(feature = "std")]
146 Error::External { source, details } => {
147 write!(f, "External error from {}: {}", source, details)
148 }
149 #[cfg(not(feature = "std"))]
150 Error::External { source } => {
151 write!(f, "External error from {}", source)
152 }
153 Error::Other(msg) => write!(f, "{}", msg),
154 }
155 }
156}
157
158#[cfg(feature = "std")]
160impl std::error::Error for Error {}
161
162impl From<Error> for CoreError {
164 fn from(err: Error) -> Self {
165 match err {
166 Error::Parameter { name, reason } => {
167 let context = match &name {
170 Cow::Borrowed(name) => *name,
171 Cow::Owned(_) => "algorithm parameter",
172 };
173 #[cfg(not(feature = "std"))]
174 let _ = reason;
175 CoreError::InvalidParameter {
176 context,
177 #[cfg(feature = "std")]
178 message: match name {
179 Cow::Borrowed(_) => reason.into_owned(),
180 Cow::Owned(name) => format!("{name}: {reason}"),
181 },
182 }
183 }
184 Error::Length {
185 context,
186 expected,
187 actual,
188 } => CoreError::InvalidLength {
189 context,
190 expected,
191 actual,
192 },
193 Error::Authentication { algorithm } => CoreError::AuthenticationFailed {
194 context: algorithm,
195 #[cfg(feature = "std")]
196 message: "authentication failed".to_string(),
197 },
198 Error::Randomness => CoreError::RandomGenerationError {
199 context: "caller-provided RNG",
200 #[cfg(feature = "std")]
201 message: "caller-provided randomness source failed".to_string(),
202 },
203 Error::NotImplemented { feature } => CoreError::NotImplemented { feature },
204 Error::Processing { operation, details } => {
205 #[cfg(not(feature = "std"))]
206 let _ = details;
207 CoreError::Other {
208 context: operation,
209 #[cfg(feature = "std")]
210 message: details.to_string(),
211 }
212 }
213 Error::MacError { algorithm, details } => {
214 #[cfg(not(feature = "std"))]
215 let _ = details;
216 CoreError::Other {
217 context: algorithm,
218 #[cfg(feature = "std")]
219 message: details.to_string(),
220 }
221 }
222 #[cfg(feature = "std")]
223 Error::External { source, details } => CoreError::Other {
224 context: source,
225 message: details,
226 },
227 #[cfg(not(feature = "std"))]
228 Error::External { source } => CoreError::Other {
229 context: source,
230 #[cfg(feature = "std")]
231 message: "external error".to_string(),
232 },
233 Error::Other(msg) => {
234 #[cfg(not(feature = "std"))]
235 let _ = msg;
236 CoreError::Other {
237 context: "primitives",
238 #[cfg(feature = "std")]
239 message: msg.to_string(),
240 }
241 }
242 }
243 }
244}
245
246impl From<dcrypt_internal::random::Error> for Error {
247 fn from(_: dcrypt_internal::random::Error) -> Self {
248 Self::Randomness
249 }
250}
251
252#[inline]
254pub fn to_core_result<T>(r: Result<T>, ctx: &'static str) -> CoreResult<T> {
255 r.map_err(|e| {
256 let mut core = CoreError::from(e);
257 core = core.with_context(ctx);
258 core
259 })
260}
261
262pub mod validate;
264
265#[cfg(test)]
266mod conversion_tests {
267 use super::*;
268
269 #[test]
270 fn randomness_preserves_its_public_error_category() {
271 let error = CoreError::from(Error::Randomness);
272 assert!(matches!(
273 error,
274 CoreError::RandomGenerationError {
275 context: "caller-provided RNG",
276 ..
277 }
278 ));
279 }
280
281 #[test]
282 fn owned_parameter_names_do_not_become_static_allocations() {
283 let error = CoreError::from(Error::param(
284 String::from("attacker-controlled name"),
285 "invalid value",
286 ));
287 assert!(matches!(
288 error,
289 CoreError::InvalidParameter {
290 context: "algorithm parameter",
291 ..
292 }
293 ));
294 }
295}