clock_bigint/
error.rs

1//! Error types for `clock-bigint` operations.
2//!
3//! This module defines the error types used throughout the `clock-bigint` library.
4//! All errors implement `std::error::Error` when the `std` feature is enabled.
5//!
6//! ## Error Handling Philosophy
7//!
8//! The library follows these principles for error handling:
9//! - **Constant-time**: Error conditions should not leak timing information
10//! - **Minimal**: Only essential error types to avoid API surface complexity
11//! - **Informative**: Error messages provide actionable information
12//! - **Non-exhaustive**: New error variants may be added in future versions
13//!
14//! ## Common Error Scenarios
15//!
16//! - **Overflow**: Operations that would exceed the BigInt's maximum limb capacity
17//! - **InvalidEncoding**: Malformed binary encodings or non-canonical representations
18//! - **DivisionByZero**: Division or modulo operations with a zero divisor
19//! - **InvalidModulus**: Montgomery arithmetic with unsupported modulus values
20
21use core::fmt;
22
23/// Errors that can occur during BigInt operations.
24///
25/// All variants are designed to be constant-time and provide minimal
26/// information to prevent side-channel attacks while still being useful
27/// for debugging.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29#[non_exhaustive]
30pub enum BigIntError {
31    /// Operation would exceed the maximum capacity.
32    ///
33    /// This error occurs when a BigInt operation would require more limbs
34    /// than the configured maximum capacity. This prevents unbounded memory
35    /// allocation and ensures constant-time behavior.
36    ///
37    /// # Examples
38    ///
39    /// ```rust
40    /// use clock_bigint::{BigInt, add_magnitude};
41    ///
42    /// // Create BigInts with values that need 1 limb each
43    /// let a = BigInt::from_u64(u64::MAX, 10);
44    /// let b = BigInt::from_u64(u64::MAX, 10);
45    ///
46    /// // Try to add into a result with insufficient capacity (only 1 limb max)
47    /// let mut result = BigInt::new(1); // Only 1 limb capacity
48    /// let add_result = add_magnitude(&a, &b, &mut result);
49    /// assert!(add_result.is_err());
50    /// ```
51    Overflow,
52
53    /// Invalid encoding detected (non-canonical form).
54    ///
55    /// This error occurs when attempting to decode malformed or non-canonical
56    /// binary representations of BigInt values. The encoding format requires
57    /// specific structure and canonical forms for security and correctness.
58    ///
59    /// # Examples
60    ///
61    /// ```rust
62    /// use clock_bigint::BigInt;
63    ///
64    /// // Empty data cannot be decoded
65    /// let result = clock_bigint::decode(&[]);
66    /// assert!(matches!(result, Err(clock_bigint::BigIntError::InvalidEncoding)));
67    ///
68    /// // Invalid length prefix
69    /// let invalid_data = vec![0u8; 10]; // Too short for valid encoding
70    /// let result = clock_bigint::decode(&invalid_data);
71    /// assert!(matches!(result, Err(clock_bigint::BigIntError::InvalidEncoding)));
72    /// ```
73    InvalidEncoding,
74
75    /// Division by zero attempted.
76    ///
77    /// This error occurs when attempting to divide by zero or compute
78    /// modulo zero. These operations are mathematically undefined.
79    ///
80    /// # Examples
81    ///
82    /// ```rust
83    /// use clock_bigint::{BigInt, div};
84    ///
85    /// let a = BigInt::from_u64(10, 10);
86    /// let zero = BigInt::from_u64(0, 10);
87    ///
88    /// // Division by zero
89    /// let result = div(&a, &zero);
90    /// assert!(matches!(result, Err(clock_bigint::BigIntError::DivisionByZero)));
91    /// ```
92    DivisionByZero,
93
94    /// Invalid modulus for Montgomery arithmetic.
95    ///
96    /// This error occurs when attempting to use Montgomery arithmetic with
97    /// an unsupported modulus. Montgomery operations require the modulus to be
98    /// odd and non-zero for the algorithms to work correctly.
99    ///
100    /// # Examples
101    ///
102    /// ```rust
103    /// use clock_bigint::{BigInt, MontgomeryContext};
104    ///
105    /// let even_modulus = BigInt::from_u64(10, 10); // Even modulus
106    ///
107    /// // Creating Montgomery context with even modulus fails
108    /// let result = MontgomeryContext::new(even_modulus);
109    /// assert!(matches!(result, Err(clock_bigint::BigIntError::InvalidModulus)));
110    ///
111    /// let zero_modulus = BigInt::from_u64(0, 10); // Zero modulus
112    /// let result = MontgomeryContext::new(zero_modulus);
113    /// assert!(matches!(result, Err(clock_bigint::BigIntError::InvalidModulus)));
114    /// ```
115    InvalidModulus,
116
117    /// Invalid internal state.
118    ///
119    /// This error occurs when an operation is attempted but the BigInt
120    /// is in an invalid internal state, such as having non-canonical
121    /// representation when canonical form is required.
122    ///
123    /// # Examples
124    ///
125    /// ```rust
126    /// use clock_bigint::BigInt;
127    ///
128    /// let mut bi = BigInt::from_u64(10, 10);
129    /// // Manually corrupt internal state (normally not possible through public API)
130    /// // This would cause InvalidState if the corruption were detected
131    /// ```
132    InvalidState,
133}
134
135impl fmt::Display for BigIntError {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        match self {
138            BigIntError::Overflow => {
139                write!(f, "BigInt operation would exceed maximum capacity")
140            }
141            BigIntError::InvalidEncoding => {
142                write!(
143                    f,
144                    "Invalid BigInt encoding: data is malformed or non-canonical"
145                )
146            }
147            BigIntError::DivisionByZero => {
148                write!(f, "Division by zero is undefined")
149            }
150            BigIntError::InvalidModulus => {
151                write!(
152                    f,
153                    "Invalid modulus for Montgomery arithmetic: must be odd and non-zero"
154                )
155            }
156            BigIntError::InvalidState => {
157                write!(f, "BigInt is in an invalid internal state")
158            }
159        }
160    }
161}
162
163
164/// Result type alias for BigInt operations.
165///
166/// This is a convenience type that uses `BigIntError` as the error type
167/// for all fallible BigInt operations.
168pub type Result<T> = core::result::Result<T, BigIntError>;
169
170#[cfg(all(test, feature = "alloc"))]
171mod tests {
172    use super::*;
173    use alloc::string::String;
174    use alloc::format;
175    use core::fmt::Write;
176
177    #[test]
178    fn test_error_display() {
179        let mut buf = String::new();
180
181        // Test Overflow display
182        write!(buf, "{}", BigIntError::Overflow).unwrap();
183        assert!(buf.contains("exceed maximum capacity"));
184        buf.clear();
185
186        // Test InvalidEncoding display
187        write!(buf, "{}", BigIntError::InvalidEncoding).unwrap();
188        assert!(buf.contains("malformed or non-canonical"));
189        buf.clear();
190
191        // Test DivisionByZero display
192        write!(buf, "{}", BigIntError::DivisionByZero).unwrap();
193        assert!(buf.contains("undefined"));
194        buf.clear();
195
196        // Test InvalidModulus display
197        write!(buf, "{}", BigIntError::InvalidModulus).unwrap();
198        assert!(buf.contains("odd and non-zero"));
199        buf.clear();
200    }
201
202    #[test]
203    fn test_error_debug() {
204        // Test that Debug works and includes the variant name
205        let overflow_str = format!("{:?}", BigIntError::Overflow);
206        assert!(overflow_str.contains("Overflow"));
207
208        let encoding_str = format!("{:?}", BigIntError::InvalidEncoding);
209        assert!(encoding_str.contains("InvalidEncoding"));
210
211        let div_zero_str = format!("{:?}", BigIntError::DivisionByZero);
212        assert!(div_zero_str.contains("DivisionByZero"));
213
214        let modulus_str = format!("{:?}", BigIntError::InvalidModulus);
215        assert!(modulus_str.contains("InvalidModulus"));
216
217        let state_str = format!("{:?}", BigIntError::InvalidState);
218        assert!(state_str.contains("InvalidState"));
219    }
220
221    #[test]
222    fn test_error_equality() {
223        // Test that errors are equal to themselves
224        assert_eq!(BigIntError::Overflow, BigIntError::Overflow);
225        assert_eq!(BigIntError::InvalidEncoding, BigIntError::InvalidEncoding);
226        assert_eq!(BigIntError::DivisionByZero, BigIntError::DivisionByZero);
227        assert_eq!(BigIntError::InvalidModulus, BigIntError::InvalidModulus);
228        assert_eq!(BigIntError::InvalidState, BigIntError::InvalidState);
229
230        // Test that different errors are not equal
231        assert_ne!(BigIntError::Overflow, BigIntError::InvalidEncoding);
232        assert_ne!(BigIntError::DivisionByZero, BigIntError::InvalidModulus);
233        assert_ne!(BigIntError::InvalidModulus, BigIntError::InvalidState);
234    }
235
236    #[test]
237    fn test_error_clone() {
238        // Test that errors can be cloned
239        let original = BigIntError::Overflow;
240        let cloned = original.clone();
241        assert_eq!(original, cloned);
242    }
243
244    #[test]
245    fn test_error_copy() {
246        // Test that errors implement Copy
247        let original = BigIntError::InvalidEncoding;
248        let copied = original; // This uses Copy, not Clone
249        assert_eq!(original, copied);
250    }
251
252    #[test]
253    fn test_result_type_alias() {
254        // Test that the Result type alias works
255        let ok_result: Result<i32> = Ok(42);
256        assert_eq!(ok_result.unwrap(), 42);
257
258        let err_result: Result<i32> = Err(BigIntError::Overflow);
259        assert!(err_result.is_err());
260        assert_eq!(err_result.unwrap_err(), BigIntError::Overflow);
261    }
262
263}