Skip to main content

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}