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}