libq/utils/
mod.rs

1//! Utility functions for lib-Q
2//!
3//! This module provides common utility functions used throughout the library.
4
5use crate::error::{Error, Result};
6use getrandom;
7
8/// Constant-time comparison of two byte slices
9///
10/// This function performs a constant-time comparison to prevent timing attacks.
11///
12/// # Arguments
13///
14/// * `a` - First byte slice
15/// * `b` - Second byte slice
16///
17/// # Returns
18///
19/// `true` if the slices are equal, `false` otherwise
20pub fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
21    if a.len() != b.len() {
22        return false;
23    }
24
25    let mut result = 0u8;
26    for (x, y) in a.iter().zip(b.iter()) {
27        result |= x ^ y;
28    }
29    result == 0
30}
31
32/// Generate cryptographically secure random bytes
33///
34/// # Arguments
35///
36/// * `length` - The number of bytes to generate
37///
38/// # Returns
39///
40/// A vector of random bytes
41///
42/// # Errors
43///
44/// Returns an error if random number generation fails
45pub fn random_bytes(length: usize) -> Result<Vec<u8>> {
46    // Validate input
47    if length == 0 {
48        return Err(Error::InvalidMessageSize { max: 0, actual: 0 });
49    }
50
51    // Check for reasonable maximum size (1MB to prevent DoS)
52    const MAX_RANDOM_SIZE: usize = 1024 * 1024; // 1MB
53    if length > MAX_RANDOM_SIZE {
54        return Err(Error::InvalidMessageSize {
55            max: MAX_RANDOM_SIZE,
56            actual: length,
57        });
58    }
59
60    let mut bytes = vec![0u8; length];
61    getrandom::fill(&mut bytes).map_err(|_| Error::RandomGenerationFailed {
62        operation: "random_bytes".to_string(),
63    })?;
64
65    Ok(bytes)
66}
67
68/// Generate a random nonce
69///
70/// # Arguments
71///
72/// * `length` - The length of the nonce
73///
74/// # Returns
75///
76/// A random nonce of the specified length
77pub fn random_nonce(length: usize) -> Result<Vec<u8>> {
78    random_bytes(length)
79}
80
81/// Convert bytes to hex string
82///
83/// # Arguments
84///
85/// * `bytes` - The bytes to convert
86///
87/// # Returns
88///
89/// A hex string representation of the bytes
90pub fn bytes_to_hex(bytes: &[u8]) -> String {
91    bytes.iter().map(|b| format!("{b:02x}")).collect()
92}
93
94/// Convert hex string to bytes
95///
96/// # Arguments
97///
98/// * `hex` - The hex string to convert
99///
100/// # Returns
101///
102/// A vector of bytes
103///
104/// # Errors
105///
106/// Returns an error if the hex string is invalid
107pub fn hex_to_bytes(hex: &str) -> Result<Vec<u8>> {
108    // Validate input
109    if hex.is_empty() {
110        return Err(Error::InvalidMessageSize { max: 0, actual: 0 });
111    }
112
113    if hex.len() % 2 != 0 {
114        return Err(Error::InternalError {
115            operation: "hex_to_bytes".to_string(),
116            details: "Hex string length must be even".to_string(),
117        });
118    }
119
120    // Check for reasonable maximum size (1MB to prevent DoS)
121    const MAX_HEX_SIZE: usize = 1024 * 1024; // 1MB
122    if hex.len() > MAX_HEX_SIZE {
123        return Err(Error::InvalidMessageSize {
124            max: MAX_HEX_SIZE,
125            actual: hex.len(),
126        });
127    }
128
129    let mut bytes = Vec::with_capacity(hex.len() / 2);
130    for i in (0..hex.len()).step_by(2) {
131        let byte = u8::from_str_radix(&hex[i..i + 2], 16).map_err(|_| Error::InternalError {
132            operation: "hex_to_bytes".to_string(),
133            details: "Invalid hex character".to_string(),
134        })?;
135        bytes.push(byte);
136    }
137    Ok(bytes)
138}
139
140/// Secure memory zeroization
141///
142/// This function securely zeroizes a byte slice to prevent sensitive data
143/// from remaining in memory.
144///
145/// # Arguments
146///
147/// * `data` - The data to zeroize
148pub fn secure_zeroize(data: &mut [u8]) {
149    for byte in data.iter_mut() {
150        *byte = 0;
151    }
152}
153
154/// Validate input data size
155///
156/// # Arguments
157///
158/// * `data` - The data to validate
159/// * `min_size` - Minimum allowed size
160/// * `max_size` - Maximum allowed size
161///
162/// # Returns
163///
164/// `Ok(())` if the data size is valid, or an error if not
165pub fn validate_data_size(data: &[u8], min_size: usize, max_size: usize) -> Result<()> {
166    if data.len() < min_size {
167        return Err(Error::InvalidMessageSize {
168            max: min_size,
169            actual: data.len(),
170        });
171    }
172
173    if data.len() > max_size {
174        return Err(Error::InvalidMessageSize {
175            max: max_size,
176            actual: data.len(),
177        });
178    }
179
180    Ok(())
181}
182
183/// Generate a random key of specified size
184///
185/// # Arguments
186///
187/// * `size` - The size of the key in bytes
188///
189/// # Returns
190///
191/// A random key of the specified size
192pub fn random_key(size: usize) -> Result<Vec<u8>> {
193    // Validate key size
194    if size == 0 {
195        return Err(Error::InvalidKeySize {
196            expected: 1,
197            actual: 0,
198        });
199    }
200
201    // Check for reasonable maximum key size (1MB)
202    const MAX_KEY_SIZE: usize = 1024 * 1024; // 1MB
203    if size > MAX_KEY_SIZE {
204        return Err(Error::InvalidKeySize {
205            expected: MAX_KEY_SIZE,
206            actual: size,
207        });
208    }
209
210    random_bytes(size)
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216
217    #[test]
218    fn test_constant_time_compare() {
219        let a = vec![1, 2, 3, 4];
220        let b = vec![1, 2, 3, 4];
221        let c = vec![1, 2, 3, 5];
222        let d = vec![1, 2, 3];
223
224        assert!(constant_time_compare(&a, &b));
225        assert!(!constant_time_compare(&a, &c));
226        assert!(!constant_time_compare(&a, &d));
227    }
228
229    #[test]
230    fn test_random_bytes() {
231        // Test valid sizes
232        let bytes1 = random_bytes(16).expect("Random bytes generation should succeed");
233        let bytes2 = random_bytes(32).expect("Random bytes generation should succeed");
234
235        assert_eq!(bytes1.len(), 16);
236        assert_eq!(bytes2.len(), 32);
237
238        // Test that they're different (very unlikely to be the same)
239        assert_ne!(bytes1, bytes2);
240
241        // Test invalid sizes
242        assert!(random_bytes(0).is_err());
243    }
244
245    #[test]
246    fn test_bytes_to_hex() {
247        let bytes = vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
248        let hex = bytes_to_hex(&bytes);
249        assert_eq!(hex, "0123456789abcdef");
250    }
251
252    #[test]
253    fn test_hex_to_bytes() {
254        let hex = "0123456789abcdef";
255        let bytes = hex_to_bytes(hex).expect("Hex to bytes conversion should succeed");
256        assert_eq!(bytes, vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
257
258        // Test invalid hex
259        assert!(hex_to_bytes("123").is_err()); // Odd length
260        assert!(hex_to_bytes("").is_err()); // Empty
261    }
262
263    #[test]
264    fn test_secure_zeroize() {
265        let mut data = vec![1, 2, 3, 4, 5];
266        secure_zeroize(&mut data);
267        assert_eq!(data, vec![0, 0, 0, 0, 0]);
268    }
269
270    #[test]
271    fn test_validate_data_size() {
272        let data = vec![1, 2, 3, 4];
273
274        // Valid sizes
275        assert!(validate_data_size(&data, 1, 10).is_ok());
276        assert!(validate_data_size(&data, 4, 4).is_ok());
277
278        // Invalid sizes
279        assert!(validate_data_size(&data, 5, 10).is_err()); // Too small
280        assert!(validate_data_size(&data, 1, 3).is_err()); // Too large
281    }
282
283    #[test]
284    fn test_random_key() {
285        let key = random_key(32).expect("Random key generation should succeed");
286        assert_eq!(key.len(), 32);
287
288        // Test invalid sizes
289        assert!(random_key(0).is_err());
290    }
291}