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 + 27_742_317_777_372_353_535_851_937_790_883_648_493
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 // Convert to fixed-size array safely
71 let bytes_array: &[u8; 32] = bytes
72 .try_into()
73 .map_err(|_| MathError::InvalidFieldElement)?;
74 let value = BigInt::from_bytes(bytes_array);
75 let p = BigInt::from_limbs(&P_LIMBS);
76
77 if value.ct_cmp(&p) != core::cmp::Ordering::Less {
78 return Err(MathError::InvalidFieldElement);
79 }
80
81 Ok(())
82}
83
84/// Validate that a byte array represents a valid scalar (in [0, l)).
85///
86/// # Arguments
87///
88/// * `bytes` - The byte array to validate (must be 32 bytes)
89///
90/// # Returns
91///
92/// Returns `Ok(())` if the bytes represent a valid scalar, or an error otherwise.
93///
94/// # Errors
95///
96/// Returns [`MathError::InvalidScalar`] if the value is not in [0, l).
97/// Returns [`MathError::BufferTooSmall`] or [`MathError::BufferTooLarge`] if the buffer size is incorrect.
98pub fn validate_scalar_bytes(bytes: &[u8]) -> Result<(), MathError> {
99 if bytes.len() != 32 {
100 return if bytes.len() < 32 {
101 Err(MathError::BufferTooSmall)
102 } else {
103 Err(MathError::BufferTooLarge)
104 };
105 }
106
107 // Convert to fixed-size array safely
108 let bytes_array: &[u8; 32] = bytes.try_into().map_err(|_| MathError::InvalidScalar)?;
109 let value = BigInt::from_bytes(bytes_array);
110 let l = BigInt::from_limbs(&L_LIMBS);
111
112 if value.ct_cmp(&l) != core::cmp::Ordering::Less {
113 return Err(MathError::InvalidScalar);
114 }
115
116 Ok(())
117}
118
119/// Validate that a BigInt represents a valid field element (in [0, p)).
120///
121/// # Arguments
122///
123/// * `value` - The BigInt value to validate
124///
125/// # Returns
126///
127/// Returns `Ok(())` if the value is a valid field element, or an error otherwise.
128///
129/// # Errors
130///
131/// Returns [`MathError::InvalidFieldElement`] if the value is not in [0, p).
132pub fn validate_field_bigint(value: &BigInt) -> Result<(), MathError> {
133 let p = BigInt::from_limbs(&P_LIMBS);
134
135 // Check value < p (BigInt values are always >= 0)
136 if value.ct_cmp(&p) != core::cmp::Ordering::Less {
137 return Err(MathError::InvalidFieldElement);
138 }
139
140 Ok(())
141}
142
143/// Validate that a BigInt represents a valid scalar (in [0, l)).
144///
145/// # Arguments
146///
147/// * `value` - The BigInt value to validate
148///
149/// # Returns
150///
151/// Returns `Ok(())` if the value is a valid scalar, or an error otherwise.
152///
153/// # Errors
154///
155/// Returns [`MathError::InvalidScalar`] if the value is not in [0, l).
156pub fn validate_scalar_bigint(value: &BigInt) -> Result<(), MathError> {
157 let l = BigInt::from_limbs(&L_LIMBS);
158
159 // Check value < l (BigInt values are always >= 0)
160 if value.ct_cmp(&l) != core::cmp::Ordering::Less {
161 return Err(MathError::InvalidScalar);
162 }
163
164 Ok(())
165}
166
167/// Validate that a BigInt is a valid exponent for modular exponentiation.
168///
169/// # Arguments
170///
171/// * `exponent` - The exponent to validate
172///
173/// # Returns
174///
175/// Returns `Ok(())` if the exponent is valid, or an error otherwise.
176///
177/// # Errors
178///
179/// Returns [`MathError::InvalidExponent`] if the exponent is negative.
180pub fn validate_exponent(exponent: &BigInt) -> Result<(), MathError> {
181 if exponent.ct_cmp(&crate::ZERO()) == core::cmp::Ordering::Less {
182 return Err(MathError::InvalidExponent);
183 }
184
185 Ok(())
186}
187
188/// Validate that a BigInt is a valid modulus for modular operations.
189///
190/// # Arguments
191///
192/// * `modulus` - The modulus to validate
193///
194/// # Returns
195///
196/// Returns `Ok(())` if the modulus is valid, or an error otherwise.
197///
198/// # Errors
199///
200/// Returns [`MathError::InvalidModulus`] if the modulus is zero or negative.
201pub fn validate_modulus(modulus: &BigInt) -> Result<(), MathError> {
202 if modulus.ct_cmp(&crate::ZERO()) != core::cmp::Ordering::Greater {
203 return Err(MathError::InvalidModulus);
204 }
205
206 Ok(())
207}
208
209/// Validate that a value is not zero (for operations that require non-zero inputs).
210///
211/// # Arguments
212///
213/// * `value` - The value to check
214/// * `_operation` - Description of the operation requiring non-zero input (currently unused)
215///
216/// # Returns
217///
218/// Returns `Ok(())` if the value is non-zero, or an error otherwise.
219///
220/// # Errors
221///
222/// Returns [`MathError::DivisionByZero`] if the value is zero.
223pub fn validate_non_zero(value: &BigInt, _operation: &str) -> Result<(), MathError> {
224 if value.is_zero() {
225 // Note: DivisionByZero is used here even for operations other than division
226 // that require non-zero inputs (like modular inverse)
227 return Err(MathError::DivisionByZero);
228 }
229
230 Ok(())
231}
232
233/// Validate buffer size for byte array operations.
234///
235/// # Arguments
236///
237/// * `buffer` - The buffer to validate
238/// * `expected_size` - The expected buffer size
239///
240/// # Returns
241///
242/// Returns `Ok(())` if the buffer size is correct, or an error otherwise.
243///
244/// # Errors
245///
246/// Returns [`MathError::BufferTooSmall`] or [`MathError::BufferTooLarge`] if the buffer size is incorrect.
247pub fn validate_buffer_size(buffer: &[u8], expected_size: usize) -> Result<(), MathError> {
248 if buffer.len() != expected_size {
249 return if buffer.len() < expected_size {
250 Err(MathError::BufferTooSmall)
251 } else {
252 Err(MathError::BufferTooLarge)
253 };
254 }
255
256 Ok(())
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262 use crate::bigint::BigInt;
263
264 #[test]
265 fn test_validate_field_bytes_valid() {
266 let bytes = [42u8; 32];
267 assert!(validate_field_bytes(&bytes).is_ok());
268 }
269
270 #[test]
271 fn test_validate_field_bytes_invalid_too_large() {
272 // p = 2^255 - 19, so any value >= p should be invalid
273 let p_bytes = [
274 0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
275 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
276 0xFF, 0xFF, 0xFF, 0x7F,
277 ];
278 assert!(matches!(
279 validate_field_bytes(&p_bytes),
280 Err(MathError::InvalidFieldElement)
281 ));
282 }
283
284 #[test]
285 fn test_validate_field_bytes_wrong_size() {
286 let small_buffer = [42u8; 16];
287 assert!(matches!(
288 validate_field_bytes(&small_buffer),
289 Err(MathError::BufferTooSmall)
290 ));
291
292 let large_buffer = [42u8; 64];
293 assert!(matches!(
294 validate_field_bytes(&large_buffer),
295 Err(MathError::BufferTooLarge)
296 ));
297 }
298
299 #[test]
300 fn test_validate_scalar_bytes_valid() {
301 // Use a small value that is definitely < l
302 let mut bytes = [0u8; 32];
303 bytes[0] = 42; // Only the least significant byte is non-zero
304 assert!(validate_scalar_bytes(&bytes).is_ok());
305 }
306
307 #[test]
308 fn test_validate_scalar_bytes_invalid_too_large() {
309 // Create bytes that represent a value >= l
310 let large_bytes = [0xFF; 32];
311 // This will likely be >= l, but let's test it
312 let result = validate_scalar_bytes(&large_bytes);
313 // It might be valid or invalid depending on the exact value, but shouldn't panic
314 assert!(result.is_ok() || matches!(result, Err(MathError::InvalidScalar)));
315 }
316
317 #[test]
318 fn test_validate_field_bigint_valid() {
319 let value = BigInt::from_u64(42);
320 assert!(validate_field_bigint(&value).is_ok());
321 }
322
323 #[test]
324 fn test_validate_field_bigint_too_large() {
325 let p = BigInt::from_limbs(&P_LIMBS);
326 let too_large = p.add(&BigInt::from_u64(1));
327 assert!(matches!(
328 validate_field_bigint(&too_large),
329 Err(MathError::InvalidFieldElement)
330 ));
331 }
332
333 #[test]
334 fn test_validate_exponent_negative() {
335 // BigInt doesn't support negative numbers, so we test with a large positive number
336 // that would underflow if it were negative
337 let large_value = BigInt::from_limbs(&[u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
338 // This should pass since BigInt treats all values as positive
339 assert!(validate_exponent(&large_value).is_ok());
340 }
341
342 #[test]
343 fn test_validate_exponent_zero() {
344 let zero = BigInt::from_u64(0);
345 assert!(validate_exponent(&zero).is_ok());
346 }
347
348 #[test]
349 fn test_validate_modulus_zero() {
350 let zero = BigInt::from_u64(0);
351 assert!(matches!(
352 validate_modulus(&zero),
353 Err(MathError::InvalidModulus)
354 ));
355 }
356
357 #[test]
358 fn test_validate_modulus_large_positive() {
359 // Test with a modulus that's actually valid (non-zero)
360 let valid_modulus = BigInt::from_u64(17);
361 assert!(validate_modulus(&valid_modulus).is_ok());
362 }
363
364 #[test]
365 fn test_validate_non_zero() {
366 let zero = BigInt::from_u64(0);
367 assert!(matches!(
368 validate_non_zero(&zero, "test"),
369 Err(MathError::DivisionByZero)
370 ));
371
372 let non_zero = BigInt::from_u64(42);
373 assert!(validate_non_zero(&non_zero, "test").is_ok());
374 }
375}