wolf_crypto/error.rs
1use core::fmt;
2use core::convert::Infallible;
3
4/// A generic error type representing an unspecified failure in cryptographic operations.
5///
6/// In cryptographic contexts, it is often necessary to hide the specific reason for
7/// an operation's failure to prevent leaking sensitive information to potential attackers.
8/// `Unspecified` serves this purpose by providing a simple, non-descriptive error type
9/// that can be used in situations where the cause of the failure should not be exposed.
10#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
11pub struct Unspecified;
12
13impl fmt::Display for Unspecified {
14 /// Writes "Unspecified" to the formatter.
15 #[inline]
16 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17 f.write_str("Unspecified")
18 }
19}
20
21std! { impl std::error::Error for Unspecified {} }
22no_std_io! {
23 impl embedded_io::Error for Unspecified {
24 fn kind(&self) -> embedded_io::ErrorKind {
25 embedded_io::ErrorKind::Other
26 }
27 }
28}
29
30impl From<crate::buf::InvalidSize> for Unspecified {
31 #[inline]
32 fn from(_value: crate::buf::InvalidSize) -> Self { Self }
33}
34
35impl From<Infallible> for Unspecified {
36 #[inline]
37 fn from(_value: Infallible) -> Self { Self }
38}
39
40/// A key derivation function was attempted to be used with zero iterations.
41#[derive(Debug, Copy, Clone)]
42pub struct InvalidIters;
43
44impl fmt::Display for InvalidIters {
45 #[inline]
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 f.write_str("InvalidIters")
48 }
49}
50
51std! {
52 impl std::error::Error for InvalidIters {}
53}
54
55impl From<InvalidIters> for Unspecified {
56 #[inline]
57 fn from(_value: InvalidIters) -> Self { Self }
58}
59
60/// Trait for transforming a `Result<T, E>` into a `Result<T, Unspecified>`.
61///
62/// This trait is useful in contexts where you want to prevent specific error details from being
63/// exposed, such as in cryptographic operations or security-critical code paths, where leaking
64/// error information could aid in side-channel attacks.
65///
66/// # Examples
67///
68/// ```
69/// use wolf_crypto::{Unspecified, MakeOpaque};
70///
71/// let success: Result<u32, &str> = Ok(42);
72/// let result = success.opaque();
73/// assert_eq!(result, Ok(42));
74///
75/// let failure: Result<u32, &str> = Err("error");
76/// let result = failure.opaque();
77/// assert_eq!(result, Err(Unspecified));
78/// ```
79///
80/// **Bind Combinator**
81/// ```
82/// use wolf_crypto::{Unspecified, MakeOpaque};
83///
84/// let failure = Ok::<usize, ()>(7)
85/// .opaque_bind(|not_seven| if not_seven == 7 {
86/// Err("Did not expect 7")
87/// } else {
88/// Ok(not_seven)
89/// });
90/// assert_eq!(failure, Err(Unspecified));
91///
92/// let ok = Ok::<usize, ()>(42)
93/// .opaque_bind(|res| if res == 42 {
94/// Ok("meaning of life")
95/// } else {
96/// Err("Expected the meaning of life")
97/// });
98/// assert_eq!(ok, Ok("meaning of life"));
99/// ```
100///
101/// **Map Combinator**
102/// ```
103/// use wolf_crypto::{Unspecified, MakeOpaque};
104///
105/// assert_eq!(
106/// Err::<usize, usize>(7).opaque_map(|num| num * 6),
107/// Err(Unspecified)
108/// );
109/// assert_eq!(
110/// Ok::<usize, usize>(7).opaque_map(|num| num * 6),
111/// Ok(42)
112/// );
113/// ```
114#[allow(clippy::missing_errors_doc)]
115pub trait MakeOpaque<T> {
116 /// Transforms the `Result<T, E>` into a `Result<T, Unspecified>`.
117 ///
118 /// If the original `Result` was `Ok(T)`, it remains unchanged. However, if it was `Err(E)`,
119 /// the error is transformed into a generic `Unspecified` error, hiding the underlying cause.
120 ///
121 /// # Example
122 ///
123 /// ```
124 /// # use wolf_crypto::{MakeOpaque, Unspecified};
125 /// let success: Result<u32, &str> = Ok(42);
126 /// let result = success.opaque();
127 /// assert_eq!(result, Ok(42));
128 ///
129 /// let failure: Result<u32, &str> = Err("error");
130 /// let result = failure.opaque();
131 /// assert_eq!(result, Err(Unspecified));
132 /// ```
133 fn opaque(self) -> Result<T, Unspecified>;
134
135 /// Calls `op` if the result is [`Ok`], converting all errors to the [`Unspecified`] type.
136 ///
137 /// This method is similar to Rust's [`and_then`] and Haskell's `bind`, though it does not
138 /// require the closure to have the same error type due to it transforming all errors to the
139 /// [`Unspecified`] type.
140 ///
141 /// This function can be used for control flow based on `Result` values.
142 ///
143 /// # Example
144 ///
145 /// ```
146 /// # use wolf_crypto::{MakeOpaque, Unspecified};
147 /// let success: Result<u32, &str> = Ok(7);
148 /// let result = success.opaque_bind(|v| v.checked_mul(6).ok_or(()));
149 /// assert_eq!(result, Ok(42));
150 ///
151 /// let failure: Result<u32, &str> = Err("error");
152 /// let result = failure.opaque_bind(|v| v.checked_mul(6).ok_or(()));
153 /// assert_eq!(result, Err(Unspecified));
154 /// ```
155 ///
156 /// [`and_then`]: Result::and_then
157 fn opaque_bind<F: FnOnce(T) -> Result<N, NE>, N, NE>(self, op: F) -> Result<N, Unspecified>;
158
159 /// Maps a `Result<T, E>` to `Result<U, Unspecified>` by applying a function to a
160 /// contained [`Ok`] value, and replacing the error with [`Unspecified`].
161 ///
162 /// This function can be used to compose the results of two functions.
163 ///
164 /// # Examples
165 ///
166 /// ```
167 /// # use wolf_crypto::{MakeOpaque, Unspecified};
168 /// let success: Result<u32, &str> = Ok(42);
169 /// let result = success.opaque_map(|v| v + 1);
170 /// assert_eq!(result, Ok(43));
171 ///
172 /// let failure: Result<u32, &str> = Err("error");
173 /// let result = failure.opaque_map(|v| v + 1);
174 /// assert_eq!(result, Err(Unspecified));
175 /// ```
176 fn opaque_map<F: FnOnce(T) -> N, N>(self, op: F) -> Result<N, Unspecified>;
177}
178
179#[allow(clippy::option_if_let_else)]
180impl<T, E> MakeOpaque<T> for Result<T, E> {
181 #[inline]
182 fn opaque(self) -> Result<T, Unspecified> {
183 match self {
184 Ok(ok) => Ok(ok),
185 Err(_) => Err(Unspecified)
186 }
187 }
188
189 #[inline]
190 fn opaque_bind<F: FnOnce(T) -> Result<N, NE>, N, NE>(self, op: F) -> Result<N, Unspecified> {
191 match self {
192 Ok(ok) => op(ok).opaque(),
193 Err(_) => Err(Unspecified)
194 }
195 }
196
197 #[inline]
198 fn opaque_map<F: FnOnce(T) -> N, N>(self, op: F) -> Result<N, Unspecified> {
199 match self {
200 Ok(ok) => Ok(op(ok)),
201 Err(_) => Err(Unspecified)
202 }
203 }
204}