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}