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