ash_core/compare.rs
1//! Constant-time comparison for timing-attack resistance.
2//!
3//! All proof comparisons must use these functions to prevent
4//! timing-based side-channel attacks.
5
6use subtle::ConstantTimeEq;
7
8/// Perform a constant-time comparison of two byte slices.
9///
10/// This function takes the same amount of time regardless of where
11/// the first difference occurs, preventing timing attacks.
12///
13/// # Security Note
14///
15/// Always use this function when comparing proofs, tokens, or any
16/// security-sensitive values. Regular `==` comparison can leak
17/// information about where differences occur.
18///
19/// # Example
20///
21/// ```rust
22/// use ash_core::timing_safe_equal;
23///
24/// let a = b"secret_proof_123";
25/// let b = b"secret_proof_123";
26/// let c = b"secret_proof_456";
27///
28/// assert!(timing_safe_equal(a, b));
29/// assert!(!timing_safe_equal(a, c));
30/// ```
31pub fn timing_safe_equal(a: &[u8], b: &[u8]) -> bool {
32 // Length check is not constant-time, but that's acceptable
33 // since proof lengths are public knowledge
34 if a.len() != b.len() {
35 return false;
36 }
37
38 // Use subtle crate for constant-time comparison
39 a.ct_eq(b).into()
40}
41
42/// Compare two strings in constant time.
43///
44/// Convenience wrapper around `timing_safe_equal` for string comparison.
45///
46/// # Example
47///
48/// ```rust
49/// use ash_core::timing_safe_equal;
50///
51/// let proof1 = "abc123xyz";
52/// let proof2 = "abc123xyz";
53///
54/// assert!(timing_safe_equal(proof1.as_bytes(), proof2.as_bytes()));
55/// ```
56#[allow(dead_code)]
57pub fn ash_timing_safe_compare(a: &str, b: &str) -> bool {
58 timing_safe_equal(a.as_bytes(), b.as_bytes())
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn test_timing_safe_equal_same() {
67 let a = b"hello world";
68 let b = b"hello world";
69 assert!(timing_safe_equal(a, b));
70 }
71
72 #[test]
73 fn test_timing_safe_equal_different() {
74 let a = b"hello world";
75 let b = b"hello worle";
76 assert!(!timing_safe_equal(a, b));
77 }
78
79 #[test]
80 fn test_timing_safe_equal_different_length() {
81 let a = b"hello";
82 let b = b"hello world";
83 assert!(!timing_safe_equal(a, b));
84 }
85
86 #[test]
87 fn test_timing_safe_equal_empty() {
88 let a = b"";
89 let b = b"";
90 assert!(timing_safe_equal(a, b));
91 }
92
93 #[test]
94 fn test_ash_timing_safe_compare() {
95 assert!(ash_timing_safe_compare("test", "test"));
96 assert!(!ash_timing_safe_compare("test", "Test"));
97 }
98}