clock_curve_math/validation.rs
1//! Input validation utilities for clock-curve-math.
2//!
3//! This module provides comprehensive validation functions for all public APIs
4//! to ensure inputs are within valid ranges and prevent invalid operations.
5//!
6//! ## Validation Functions
7//!
8//! The validation functions check that inputs conform to the mathematical
9//! constraints of the cryptographic primitives:
10//!
11//! - **Field elements** must be in the range [0, p) where p = 2^255 - 19
12//! - **Scalars** must be in the range [0, l) where l = 2^252 + 27742317777372353535851937790883648493
13//! - **Exponents** must be non-negative (though BigInt always represents non-negative values)
14//! - **Moduli** must be positive and non-zero
15//! - **Buffers** must have the correct size for the operation
16//!
17//! ## Error Conditions
18//!
19//! All validation functions return [`MathError`] variants that precisely
20//! describe the validation failure:
21//!
22//! - [`MathError::InvalidFieldElement`]: Value not in [0, p)
23//! - [`MathError::InvalidScalar`]: Value not in [0, l)
24//! - [`MathError::InvalidExponent`]: Negative exponent provided
25//! - [`MathError::InvalidModulus`]: Zero or negative modulus
26//! - [`MathError::BufferTooSmall`]: Input buffer smaller than required
27//! - [`MathError::BufferTooLarge`]: Input buffer larger than required
28//!
29//! ## Usage
30//!
31//! ```rust
32//! use clock_curve_math::validation::*;
33//!
34//! // Validate a field element byte representation
35//! let bytes = [42u8; 32];
36//! assert!(validate_field_bytes(&bytes).is_ok());
37//!
38//! // Validate a scalar BigInt value
39//! let scalar_value = clock_curve_math::BigInt::from_u64(12345);
40//! assert!(validate_scalar_bigint(&scalar_value).is_ok());
41//! ```
42
43use crate::bigint::BigInt;
44use crate::constants::{L_LIMBS, P_LIMBS};
45use crate::error::MathError;
46
47/// Validate that a byte array represents a valid field element (in [0, p)).
48///
49/// # Arguments
50///
51/// * `bytes` - The byte array to validate (must be 32 bytes)
52///
53/// # Returns
54///
55/// Returns `Ok(())` if the bytes represent a valid field element, or an error otherwise.
56///
57/// # Errors
58///
59/// Returns [`MathError::InvalidFieldElement`] if the value is not in [0, p).
60/// Returns [`MathError::BufferTooSmall`] or [`MathError::BufferTooLarge`] if the buffer size is incorrect.
61pub fn validate_field_bytes(bytes: &[u8]) -> Result<(), MathError> {
62 if bytes.len() != 32 {
63 return if bytes.len() < 32 {
64 Err(MathError::BufferTooSmall)
65 } else {
66 Err(MathError::BufferTooLarge)
67 };
68 }
69
70 // SAFETY: We just checked that bytes.len() == 32
71 let bytes_array: &[u8; 32] = bytes.try_into().unwrap();
72 let value = BigInt::from_bytes(bytes_array);
73 let p = BigInt::from_limbs(&P_LIMBS);
74
75 if value.cmp(&p) != core::cmp::Ordering::Less {
76 return Err(MathError::InvalidFieldElement);
77 }
78
79 Ok(())
80}
81
82/// Validate that a byte array represents a valid scalar (in [0, l)).
83///
84/// # Arguments
85///
86/// * `bytes` - The byte array to validate (must be 32 bytes)
87///
88/// # Returns
89///
90/// Returns `Ok(())` if the bytes represent a valid scalar, or an error otherwise.
91///
92/// # Errors
93///
94/// Returns [`MathError::InvalidScalar`] if the value is not in [0, l).
95/// Returns [`MathError::BufferTooSmall`] or [`MathError::BufferTooLarge`] if the buffer size is incorrect.
96pub fn validate_scalar_bytes(bytes: &[u8]) -> Result<(), MathError> {
97 if bytes.len() != 32 {
98 return if bytes.len() < 32 {
99 Err(MathError::BufferTooSmall)
100 } else {
101 Err(MathError::BufferTooLarge)
102 };
103 }
104
105 // SAFETY: We just checked that bytes.len() == 32
106 let bytes_array: &[u8; 32] = bytes.try_into().unwrap();
107 let value = BigInt::from_bytes(bytes_array);
108 let l = BigInt::from_limbs(&L_LIMBS);
109
110 if value.cmp(&l) != core::cmp::Ordering::Less {
111 return Err(MathError::InvalidScalar);
112 }
113
114 Ok(())
115}
116
117/// Validate that a BigInt represents a valid field element (in [0, p)).
118///
119/// # Arguments
120///
121/// * `value` - The BigInt value to validate
122///
123/// # Returns
124///
125/// Returns `Ok(())` if the value is a valid field element, or an error otherwise.
126///
127/// # Errors
128///
129/// Returns [`MathError::InvalidFieldElement`] if the value is not in [0, p).
130pub fn validate_field_bigint(value: &BigInt) -> Result<(), MathError> {
131 let p = BigInt::from_limbs(&P_LIMBS);
132
133 // Check value < p (BigInt values are always >= 0)
134 if value.cmp(&p) != core::cmp::Ordering::Less {
135 return Err(MathError::InvalidFieldElement);
136 }
137
138 Ok(())
139}
140
141/// Validate that a BigInt represents a valid scalar (in [0, l)).
142///
143/// # Arguments
144///
145/// * `value` - The BigInt value to validate
146///
147/// # Returns
148///
149/// Returns `Ok(())` if the value is a valid scalar, or an error otherwise.
150///
151/// # Errors
152///
153/// Returns [`MathError::InvalidScalar`] if the value is not in [0, l).
154pub fn validate_scalar_bigint(value: &BigInt) -> Result<(), MathError> {
155 let l = BigInt::from_limbs(&L_LIMBS);
156
157 // Check value < l (BigInt values are always >= 0)
158 if value.cmp(&l) != core::cmp::Ordering::Less {
159 return Err(MathError::InvalidScalar);
160 }
161
162 Ok(())
163}
164
165/// Validate that a BigInt is a valid exponent for modular exponentiation.
166///
167/// # Arguments
168///
169/// * `exponent` - The exponent to validate
170///
171/// # Returns
172///
173/// Returns `Ok(())` if the exponent is valid, or an error otherwise.
174///
175/// # Errors
176///
177/// Returns [`MathError::InvalidExponent`] if the exponent is negative.
178pub fn validate_exponent(exponent: &BigInt) -> Result<(), MathError> {
179 if exponent.cmp(&crate::ZERO()) == core::cmp::Ordering::Less {
180 return Err(MathError::InvalidExponent);
181 }
182
183 Ok(())
184}
185
186/// Validate that a BigInt is a valid modulus for modular operations.
187///
188/// # Arguments
189///
190/// * `modulus` - The modulus to validate
191///
192/// # Returns
193///
194/// Returns `Ok(())` if the modulus is valid, or an error otherwise.
195///
196/// # Errors
197///
198/// Returns [`MathError::InvalidModulus`] if the modulus is zero or negative.
199pub fn validate_modulus(modulus: &BigInt) -> Result<(), MathError> {
200 if modulus.cmp(&crate::ZERO()) != core::cmp::Ordering::Greater {
201 return Err(MathError::InvalidModulus);
202 }
203
204 Ok(())
205}
206
207/// Validate that a value is not zero (for operations that require non-zero inputs).
208///
209/// # Arguments
210///
211/// * `value` - The value to check
212/// * `_operation` - Description of the operation requiring non-zero input (currently unused)
213///
214/// # Returns
215///
216/// Returns `Ok(())` if the value is non-zero, or an error otherwise.
217///
218/// # Errors
219///
220/// Returns [`MathError::DivisionByZero`] if the value is zero.
221pub fn validate_non_zero(value: &BigInt, _operation: &str) -> Result<(), MathError> {
222 if value.is_zero() {
223 // Note: DivisionByZero is used here even for operations other than division
224 // that require non-zero inputs (like modular inverse)
225 return Err(MathError::DivisionByZero);
226 }
227
228 Ok(())
229}
230
231/// Validate buffer size for byte array operations.
232///
233/// # Arguments
234///
235/// * `buffer` - The buffer to validate
236/// * `expected_size` - The expected buffer size
237///
238/// # Returns
239///
240/// Returns `Ok(())` if the buffer size is correct, or an error otherwise.
241///
242/// # Errors
243///
244/// Returns [`MathError::BufferTooSmall`] or [`MathError::BufferTooLarge`] if the buffer size is incorrect.
245pub fn validate_buffer_size(buffer: &[u8], expected_size: usize) -> Result<(), MathError> {
246 if buffer.len() != expected_size {
247 return if buffer.len() < expected_size {
248 Err(MathError::BufferTooSmall)
249 } else {
250 Err(MathError::BufferTooLarge)
251 };
252 }
253
254 Ok(())
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use crate::bigint::BigInt;
261
262 #[test]
263 fn test_validate_field_bytes_valid() {
264 let bytes = [42u8; 32];
265 assert!(validate_field_bytes(&bytes).is_ok());
266 }
267
268 #[test]
269 fn test_validate_field_bytes_invalid_too_large() {
270 // p = 2^255 - 19, so any value >= p should be invalid
271 let p_bytes = [
272 0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
273 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F
274 ];
275 assert!(matches!(validate_field_bytes(&p_bytes), Err(MathError::InvalidFieldElement)));
276 }
277
278 #[test]
279 fn test_validate_field_bytes_wrong_size() {
280 let small_buffer = [42u8; 16];
281 assert!(matches!(validate_field_bytes(&small_buffer), Err(MathError::BufferTooSmall)));
282
283 let large_buffer = [42u8; 64];
284 assert!(matches!(validate_field_bytes(&large_buffer), Err(MathError::BufferTooLarge)));
285 }
286
287 #[test]
288 fn test_validate_scalar_bytes_valid() {
289 // Use a small value that is definitely < l
290 let mut bytes = [0u8; 32];
291 bytes[0] = 42; // Only the least significant byte is non-zero
292 assert!(validate_scalar_bytes(&bytes).is_ok());
293 }
294
295 #[test]
296 fn test_validate_scalar_bytes_invalid_too_large() {
297 // Create bytes that represent a value >= l
298 let large_bytes = [0xFF; 32];
299 // This will likely be >= l, but let's test it
300 let result = validate_scalar_bytes(&large_bytes);
301 // It might be valid or invalid depending on the exact value, but shouldn't panic
302 assert!(result.is_ok() || matches!(result, Err(MathError::InvalidScalar)));
303 }
304
305 #[test]
306 fn test_validate_field_bigint_valid() {
307 let value = BigInt::from_u64(42);
308 assert!(validate_field_bigint(&value).is_ok());
309 }
310
311 #[test]
312 fn test_validate_field_bigint_too_large() {
313 let p = BigInt::from_limbs(&P_LIMBS);
314 let too_large = p.add(&BigInt::from_u64(1));
315 assert!(matches!(validate_field_bigint(&too_large), Err(MathError::InvalidFieldElement)));
316 }
317
318 #[test]
319 fn test_validate_exponent_negative() {
320 // BigInt doesn't support negative numbers, so we test with a large positive number
321 // that would underflow if it were negative
322 let large_value = BigInt::from_limbs(&[u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
323 // This should pass since BigInt treats all values as positive
324 assert!(validate_exponent(&large_value).is_ok());
325 }
326
327 #[test]
328 fn test_validate_exponent_zero() {
329 let zero = BigInt::from_u64(0);
330 assert!(validate_exponent(&zero).is_ok());
331 }
332
333 #[test]
334 fn test_validate_modulus_zero() {
335 let zero = BigInt::from_u64(0);
336 assert!(matches!(validate_modulus(&zero), Err(MathError::InvalidModulus)));
337 }
338
339 #[test]
340 fn test_validate_modulus_large_positive() {
341 // Test with a modulus that's actually valid (non-zero)
342 let valid_modulus = BigInt::from_u64(17);
343 assert!(validate_modulus(&valid_modulus).is_ok());
344 }
345
346 #[test]
347 fn test_validate_non_zero() {
348 let zero = BigInt::from_u64(0);
349 assert!(matches!(validate_non_zero(&zero, "test"), Err(MathError::DivisionByZero)));
350
351 let non_zero = BigInt::from_u64(42);
352 assert!(validate_non_zero(&non_zero, "test").is_ok());
353 }
354}