wolf_crypto/
opaque_res.rs

1//! Convenient Error Handling and Accumulation
2//!
3//! This module provides a simple, opaque error type (`Res`) designed to prevent
4//! side-channel attacks through timing or error messages. It allows for
5//! accumulation of error states without revealing specific error details.
6use core::ffi::c_int;
7use crate::error::Unspecified;
8use core::fmt;
9
10/// An opaque result type for error handling without exposing error details.
11///
12/// This type is designed to prevent side-channel attacks by not revealing
13/// specific error information. It only indicates success or failure.
14#[must_use = "You must handle the potential error"]
15#[repr(transparent)]
16pub struct Res(pub(crate) bool);
17
18impl fmt::Debug for Res {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        f.write_fmt(format_args!("Res {{ is_ok: {} }}", self.0 as u8))
21    }
22}
23
24impl Default for Res {
25    #[inline]
26    fn default() -> Self {
27        Self::new()
28    }
29}
30
31impl Res {
32    /// Represents a successful result.
33    pub const OK: Self = Self(true);
34    /// Represents an error result.
35    pub const ERR: Self = Self(false);
36
37    /// Creates a new `Res` instance initialized to `OK`.
38    ///
39    /// # Returns
40    ///
41    /// A new `Res` instance representing success.
42    pub const fn new() -> Self { Self::OK }
43
44    /// Checks if the result is OK (successful).
45    ///
46    /// # Returns
47    ///
48    /// `true` if the result is OK, `false` otherwise.
49    #[inline]
50    pub const fn is_ok(&self) -> bool {
51        self.0
52    }
53
54    /// Checks if the result is an error.
55    ///
56    /// # Returns
57    ///
58    /// `true` if the result is an error, `false` otherwise.
59    #[inline]
60    pub const fn is_err(&self) -> bool {
61        !self.0
62    }
63
64    /// Updates the result based on a boolean condition.
65    ///
66    /// If `res` is `false`, this method will set the `Res` to an error state.
67    ///
68    /// # Arguments
69    ///
70    /// * `res` - A boolean representing a condition to check.
71    #[inline]
72    pub fn check(&mut self, res: bool) {
73        self.0 &= res;
74    }
75
76    /// Ensures that a C integer result is equal to 1.
77    ///
78    /// Sets the `Res` to an error state if the input is not 1.
79    ///
80    /// # Arguments
81    ///
82    /// * `res` - A C integer to check.
83    #[inline]
84    pub fn ensure_1(&mut self, res: c_int) {
85        self.0 &= (res as u8) == 1u8;
86    }
87
88    /// Ensures that a C integer result is equal to 0.
89    ///
90    /// Sets the `Res` to an error state if the input is not 0.
91    ///
92    /// # Arguments
93    ///
94    /// * `res` - A C integer to check.
95    #[inline]
96    pub fn ensure_0(&mut self, res: c_int) {
97        self.0 &= (res as u8) == 0u8;
98    }
99
100    /// Ensures that a C integer result is positive.
101    ///
102    /// Sets the `Res` to an error state if the input is not positive.
103    ///
104    /// # Arguments
105    ///
106    /// * `res` - A C integer to check.
107    #[inline]
108    pub fn ensure_pos(&mut self, res: c_int) {
109        const R_SHR: c_int = (core::mem::size_of::<c_int>() * 8 - 1) as c_int;
110        self.0 &= (!(res >> R_SHR) as u8) & 1 == 1;
111    }
112
113    /// Combines this `Res` with another `Res`.
114    ///
115    /// The result will be OK only if both `Res` instances are OK.
116    ///
117    /// # Arguments
118    ///
119    /// * `res` - Another `Res` instance to combine with this one.
120    #[inline]
121    pub fn ensure(&mut self, res: Self) {
122        self.0 &= res.0;
123    }
124
125    /// Converts the `Res` into a `Result<OK, Unspecified>`.
126    ///
127    /// # Warning
128    ///
129    /// This method is not constant time and should be used carefully in
130    /// security-sensitive contexts.
131    ///
132    /// # Arguments
133    ///
134    /// * `ok` - The value to return in the `Ok` variant if the `Res` is OK.
135    ///
136    /// # Returns
137    ///
138    /// `Ok(ok)` if the `Res` is OK, `Err(Unspecified)` otherwise.
139    #[allow(clippy::missing_errors_doc)]
140    #[inline(always)]
141    pub fn unit_err<OK>(self, ok: OK) -> Result<OK, Unspecified> {
142        if self.is_ok() {
143            Ok(ok)
144        } else {
145            Err(Unspecified)
146        }
147    }
148
149    /// Converts the `Res` into a `Result<OK, Unspecified>`, with a closure for the OK case.
150    ///
151    /// # Warning
152    ///
153    /// This method is not constant time and should be used carefully in
154    /// security-sensitive contexts.
155    ///
156    /// # When to Use
157    ///
158    /// Use this method when the creation of the `OK` value depends on the result
159    /// being OK for safety reasons. The closure is only called if the `Res` is OK,
160    /// ensuring that any preconditions for the OK value's creation are met.
161    ///
162    /// # Arguments
163    ///
164    /// * `ok` - A closure that returns the value for the `Ok` variant if the `Res` is OK.
165    ///
166    /// # Returns
167    ///
168    /// `Ok(ok())` if the `Res` is OK, `Err(Unspecified)` otherwise.
169    #[inline(always)]
170    #[allow(clippy::missing_errors_doc)]
171    pub fn unit_err_with<F, OK>(self, ok: F) -> Result<OK, Unspecified>
172        where F: FnOnce() -> OK
173    {
174        if self.is_ok() {
175            Ok(ok())
176        } else {
177            Err(Unspecified)
178        }
179    }
180
181    /// Unwraps the `Res`, panicking if it's an error.
182    ///
183    /// # Panics
184    ///
185    /// Panics if the `Res` is an error.
186    ///
187    /// # Warning
188    ///
189    /// This method should generally be avoided in production code, as it can lead
190    /// to program termination. It's primarily useful for testing or in situations
191    /// where an error truly represents an unrecoverable state.
192    #[inline]
193    #[track_caller]
194    pub fn unwrap(self) {
195        self.unit_err(()).unwrap();
196    }
197}