auth_framework/security/timing_protection.rs
1//! Timing attack protection utilities
2//!
3//! This module provides utilities to prevent timing side-channel attacks,
4//! particularly important for authentication frameworks where attackers
5//! could exploit timing differences to extract sensitive information.
6
7use crate::errors::Result;
8use rand::Rng;
9use std::time::Duration;
10use subtle::ConstantTimeEq;
11
12/// Perform constant-time comparison of byte arrays
13///
14/// This function provides protection against timing attacks by ensuring
15/// the comparison takes the same amount of time regardless of where the
16/// first difference occurs.
17///
18/// # Arguments
19/// * `a` - First byte array to compare
20/// * `b` - Second byte array to compare
21///
22/// # Returns
23/// `true` if arrays are equal, `false` otherwise
24///
25/// # Security
26/// Uses the `subtle` crate's constant-time comparison to prevent
27/// timing side-channel attacks.
28pub fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
29 if a.len() != b.len() {
30 return false;
31 }
32 a.ct_eq(b).into()
33}
34
35/// Add a small random delay to mask timing patterns
36///
37/// This function adds a small, random delay to help mask timing patterns
38/// that could be exploited by attackers. Useful for authentication
39/// operations where you want to prevent timing analysis.
40///
41/// # Arguments
42/// * `base_delay_ms` - Base delay in milliseconds (default: 0)
43/// * `max_random_ms` - Maximum additional random delay in milliseconds (default: 10)
44///
45/// # Security
46/// The random delay helps prevent attackers from using timing analysis
47/// to determine success/failure patterns or extract sensitive information.
48pub async fn random_delay(base_delay_ms: u64, max_random_ms: u64) {
49 let base_delay = Duration::from_millis(base_delay_ms);
50 let random_delay = Duration::from_millis(rand::rng().random_range(0..max_random_ms));
51 let total_delay = base_delay + random_delay;
52
53 tokio::time::sleep(total_delay).await;
54}
55
56/// Perform a constant-time string comparison
57///
58/// Compares two strings in constant time to prevent timing attacks.
59///
60/// # Arguments
61/// * `a` - First string to compare
62/// * `b` - Second string to compare
63///
64/// # Returns
65/// `true` if strings are equal, `false` otherwise
66pub fn constant_time_string_compare(a: &str, b: &str) -> bool {
67 constant_time_compare(a.as_bytes(), b.as_bytes())
68}
69
70/// Wrapper for sensitive authentication operations with timing protection
71///
72/// This function wraps sensitive operations with timing protection,
73/// ensuring that both success and failure cases take similar amounts of time.
74///
75/// # Arguments
76/// * `operation` - The async operation to perform
77/// * `min_duration_ms` - Minimum time the operation should take
78///
79/// # Returns
80/// The result of the operation
81///
82/// # Security
83/// Ensures that timing differences don't leak information about the
84/// success or failure of the operation.
85pub async fn timing_safe_operation<T, F, Fut>(operation: F, min_duration_ms: u64) -> Result<T>
86where
87 F: FnOnce() -> Fut,
88 Fut: std::future::Future<Output = Result<T>>,
89{
90 let start = std::time::Instant::now();
91 let result = operation().await;
92 let elapsed = start.elapsed();
93
94 let min_duration = Duration::from_millis(min_duration_ms);
95 if elapsed < min_duration {
96 let remaining = min_duration - elapsed;
97 tokio::time::sleep(remaining).await;
98 }
99
100 result
101}
102
103/// RSA operation wrapper with blinding protection
104///
105/// This is a placeholder for RSA operations that need timing protection.
106/// In a real implementation, this would use RSA blinding techniques
107/// to prevent timing attacks on RSA operations.
108///
109/// # Security Note
110/// This is a placeholder implementation. For production use with RSA
111/// operations, consider using the `ring` crate or other cryptographic
112/// libraries that implement proper RSA blinding.
113pub async fn rsa_operation_protected<T, F, Fut>(operation: F) -> Result<T>
114where
115 F: FnOnce() -> Fut,
116 Fut: std::future::Future<Output = Result<T>>,
117{
118 // Add random delay before operation
119 random_delay(1, 5).await;
120
121 // Perform the operation with minimum timing
122 timing_safe_operation(operation, 10).await
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_constant_time_compare() {
131 let a = b"hello";
132 let b = b"hello";
133 let c = b"world";
134
135 assert!(constant_time_compare(a, b));
136 assert!(!constant_time_compare(a, c));
137 assert!(!constant_time_compare(a, b"hi"));
138 }
139
140 #[test]
141 fn test_constant_time_string_compare() {
142 assert!(constant_time_string_compare("hello", "hello"));
143 assert!(!constant_time_string_compare("hello", "world"));
144 assert!(!constant_time_string_compare("hello", "hi"));
145 }
146
147 #[tokio::test]
148 async fn test_random_delay() {
149 let start = std::time::Instant::now();
150 random_delay(0, 5).await;
151 let elapsed = start.elapsed();
152
153 // Should take at least some time but less than 50ms
154 assert!(elapsed >= Duration::from_millis(0));
155 assert!(elapsed < Duration::from_millis(50));
156 }
157
158 #[tokio::test]
159 async fn test_timing_safe_operation() {
160 let start = std::time::Instant::now();
161
162 let result = timing_safe_operation(
163 || async { Ok::<_, crate::errors::AuthError>("success") },
164 50,
165 )
166 .await;
167
168 let elapsed = start.elapsed();
169
170 assert!(result.is_ok());
171 assert!(elapsed >= Duration::from_millis(50));
172 }
173}