ant_quic/relay/
authenticator.rs

1//! Ed25519-based authentication for relay operations with anti-replay protection.
2
3use crate::relay::{RelayError, RelayResult};
4use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
5use rand::rngs::OsRng;
6use std::collections::HashSet;
7use std::sync::{Arc, Mutex};
8use std::time::{SystemTime, UNIX_EPOCH};
9
10/// Cryptographic authentication token for relay operations
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct AuthToken {
13    /// Unique nonce to prevent replay attacks
14    pub nonce: u64,
15    /// Timestamp when token was created (Unix timestamp)
16    pub timestamp: u64,
17    /// Requested bandwidth limit in bytes per second
18    pub bandwidth_limit: u32,
19    /// Session timeout in seconds
20    pub timeout_seconds: u32,
21    /// Ed25519 signature over the token data
22    pub signature: [u8; 64],
23}
24
25/// Ed25519 authenticator with anti-replay protection
26#[derive(Debug)]
27pub struct RelayAuthenticator {
28    /// Private signing key for this node
29    signing_key: SigningKey,
30    /// Public verification key for this node
31    verifying_key: VerifyingKey,
32    /// Set of used nonces for anti-replay protection
33    used_nonces: Arc<Mutex<HashSet<u64>>>,
34    /// Maximum age of tokens in seconds (default: 5 minutes)
35    max_token_age: u64,
36    /// Size of anti-replay window
37    replay_window_size: u64,
38}
39
40impl AuthToken {
41    /// Create a new authentication token
42    pub fn new(
43        bandwidth_limit: u32,
44        timeout_seconds: u32,
45        signing_key: &SigningKey,
46    ) -> RelayResult<Self> {
47        let nonce = Self::generate_nonce();
48        let timestamp = Self::current_timestamp()?;
49        
50        let mut token = Self {
51            nonce,
52            timestamp,
53            bandwidth_limit,
54            timeout_seconds,
55            signature: [0; 64],
56        };
57        
58        // Sign the token
59        let signature_bytes = signing_key.sign(&token.signable_data()).to_bytes();
60        token.signature = signature_bytes;
61        
62        Ok(token)
63    }
64
65    /// Generate a cryptographically secure nonce
66    fn generate_nonce() -> u64 {
67        use rand::Rng;
68        OsRng.r#gen()
69    }
70
71    /// Get current Unix timestamp
72    fn current_timestamp() -> RelayResult<u64> {
73        SystemTime::now()
74            .duration_since(UNIX_EPOCH)
75            .map(|d| d.as_secs())
76            .map_err(|_| RelayError::AuthenticationFailed {
77                reason: "System time before Unix epoch".to_string(),
78            })
79    }
80
81    /// Get the data that should be signed
82    fn signable_data(&self) -> Vec<u8> {
83        let mut data = Vec::new();
84        data.extend_from_slice(&self.nonce.to_le_bytes());
85        data.extend_from_slice(&self.timestamp.to_le_bytes());
86        data.extend_from_slice(&self.bandwidth_limit.to_le_bytes());
87        data.extend_from_slice(&self.timeout_seconds.to_le_bytes());
88        data
89    }
90
91    /// Verify the token signature
92    pub fn verify(&self, verifying_key: &VerifyingKey) -> RelayResult<()> {
93        let signature = Signature::from_bytes(&self.signature);
94
95        verifying_key
96            .verify(&self.signable_data(), &signature)
97            .map_err(|_| RelayError::AuthenticationFailed {
98                reason: "Signature verification failed".to_string(),
99            })
100    }
101
102    /// Check if the token has expired
103    pub fn is_expired(&self, max_age_seconds: u64) -> RelayResult<bool> {
104        let current_time = Self::current_timestamp()?;
105        Ok(current_time > self.timestamp + max_age_seconds)
106    }
107}
108
109impl RelayAuthenticator {
110    /// Create a new authenticator with a random key pair
111    pub fn new() -> Self {
112        let signing_key = SigningKey::generate(&mut OsRng);
113        let verifying_key = signing_key.verifying_key();
114        
115        Self {
116            signing_key,
117            verifying_key,
118            used_nonces: Arc::new(Mutex::new(HashSet::new())),
119            max_token_age: 300, // 5 minutes
120            replay_window_size: 1000,
121        }
122    }
123
124    /// Create an authenticator with a specific signing key
125    pub fn with_key(signing_key: SigningKey) -> Self {
126        let verifying_key = signing_key.verifying_key();
127        
128        Self {
129            signing_key,
130            verifying_key,
131            used_nonces: Arc::new(Mutex::new(HashSet::new())),
132            max_token_age: 300,
133            replay_window_size: 1000,
134        }
135    }
136
137    /// Get the public verifying key
138    pub fn verifying_key(&self) -> &VerifyingKey {
139        &self.verifying_key
140    }
141
142    /// Create a new authentication token
143    pub fn create_token(
144        &self,
145        bandwidth_limit: u32,
146        timeout_seconds: u32,
147    ) -> RelayResult<AuthToken> {
148        AuthToken::new(bandwidth_limit, timeout_seconds, &self.signing_key)
149    }
150
151    /// Verify an authentication token with anti-replay protection
152    pub fn verify_token(
153        &self,
154        token: &AuthToken,
155        peer_verifying_key: &VerifyingKey,
156    ) -> RelayResult<()> {
157        // Check signature
158        token.verify(peer_verifying_key)?;
159
160        // Check if token has expired
161        if token.is_expired(self.max_token_age)? {
162            return Err(RelayError::AuthenticationFailed {
163                reason: "Token expired".to_string(),
164            });
165        }
166
167        // Check for replay attack
168        let mut used_nonces = self.used_nonces.lock().unwrap();
169        
170        if used_nonces.contains(&token.nonce) {
171            return Err(RelayError::AuthenticationFailed {
172                reason: "Token replay detected".to_string(),
173            });
174        }
175
176        // Add nonce to used set (with size limit)
177        if used_nonces.len() >= self.replay_window_size as usize {
178            // Remove oldest entries (simple approach - in production might use LRU)
179            let to_remove: Vec<_> = used_nonces.iter().take(100).cloned().collect();
180            for nonce in to_remove {
181                used_nonces.remove(&nonce);
182            }
183        }
184        
185        used_nonces.insert(token.nonce);
186        
187        Ok(())
188    }
189
190    /// Set maximum token age
191    pub fn set_max_token_age(&mut self, max_age_seconds: u64) {
192        self.max_token_age = max_age_seconds;
193    }
194
195    /// Clear all used nonces (for testing)
196    pub fn clear_nonces(&self) {
197        let mut used_nonces = self.used_nonces.lock().unwrap();
198        used_nonces.clear();
199    }
200
201    /// Get number of used nonces (for testing)
202    pub fn nonce_count(&self) -> usize {
203        let used_nonces = self.used_nonces.lock().unwrap();
204        used_nonces.len()
205    }
206}
207
208impl Default for RelayAuthenticator {
209    fn default() -> Self {
210        Self::new()
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217    use std::thread;
218    use std::time::Duration;
219
220    #[test]
221    fn test_auth_token_creation_and_verification() {
222        let authenticator = RelayAuthenticator::new();
223        let token = authenticator.create_token(1024, 300).unwrap();
224        
225        assert!(token.bandwidth_limit == 1024);
226        assert!(token.timeout_seconds == 300);
227        assert!(token.nonce != 0);
228        assert!(token.timestamp > 0);
229        
230        // Verify token
231        assert!(token.verify(authenticator.verifying_key()).is_ok());
232    }
233
234    #[test]
235    fn test_token_verification_with_wrong_key() {
236        let authenticator1 = RelayAuthenticator::new();
237        let authenticator2 = RelayAuthenticator::new();
238        
239        let token = authenticator1.create_token(1024, 300).unwrap();
240        
241        // Should fail with wrong key
242        assert!(token.verify(authenticator2.verifying_key()).is_err());
243    }
244
245    #[test]
246    fn test_token_expiration() {
247        let mut authenticator = RelayAuthenticator::new();
248        authenticator.set_max_token_age(1); // 1 second
249        
250        let token = authenticator.create_token(1024, 300).unwrap();
251        
252        // Should not be expired immediately
253        assert!(!token.is_expired(1).unwrap());
254        
255        // Wait for expiration
256        thread::sleep(Duration::from_millis(1100));
257        
258        // Should be expired now
259        assert!(token.is_expired(1).unwrap());
260    }
261
262    #[test]
263    fn test_anti_replay_protection() {
264        let authenticator = RelayAuthenticator::new();
265        let token = authenticator.create_token(1024, 300).unwrap();
266        
267        // First verification should succeed
268        assert!(authenticator.verify_token(&token, authenticator.verifying_key()).is_ok());
269        
270        // Second verification should fail (replay)
271        assert!(authenticator.verify_token(&token, authenticator.verifying_key()).is_err());
272    }
273
274    #[test]
275    fn test_nonce_uniqueness() {
276        let authenticator = RelayAuthenticator::new();
277        let mut nonces = HashSet::new();
278        
279        // Generate many tokens and check nonce uniqueness
280        for _ in 0..1000 {
281            let token = authenticator.create_token(1024, 300).unwrap();
282            assert!(!nonces.contains(&token.nonce), "Duplicate nonce detected");
283            nonces.insert(token.nonce);
284        }
285    }
286
287    #[test]
288    fn test_token_signable_data() {
289        let authenticator = RelayAuthenticator::new();
290        let token1 = authenticator.create_token(1024, 300).unwrap();
291        let token2 = authenticator.create_token(1024, 300).unwrap();
292        
293        // Different tokens should have different signable data (due to nonce/timestamp)
294        assert_ne!(token1.signable_data(), token2.signable_data());
295    }
296
297    #[test]
298    fn test_nonce_window_management() {
299        let authenticator = RelayAuthenticator::new();
300        
301        // Fill up the nonce window
302        for _ in 0..1000 {
303            let token = authenticator.create_token(1024, 300).unwrap();
304            let _ = authenticator.verify_token(&token, authenticator.verifying_key());
305        }
306        
307        assert_eq!(authenticator.nonce_count(), 1000);
308        
309        // Add one more token (should trigger cleanup)
310        let token = authenticator.create_token(1024, 300).unwrap();
311        let _ = authenticator.verify_token(&token, authenticator.verifying_key());
312        
313        // Window should be maintained at reasonable size
314        assert!(authenticator.nonce_count() <= 1000);
315    }
316
317    #[test]
318    fn test_clear_nonces() {
319        let authenticator = RelayAuthenticator::new();
320        let token = authenticator.create_token(1024, 300).unwrap();
321        
322        // Use token
323        let _ = authenticator.verify_token(&token, authenticator.verifying_key());
324        assert!(authenticator.nonce_count() > 0);
325        
326        // Clear nonces
327        authenticator.clear_nonces();
328        assert_eq!(authenticator.nonce_count(), 0);
329        
330        // Should be able to use the same token again
331        assert!(authenticator.verify_token(&token, authenticator.verifying_key()).is_ok());
332    }
333
334    #[test]
335    fn test_with_specific_key() {
336        let signing_key = SigningKey::generate(&mut OsRng);
337        let authenticator = RelayAuthenticator::with_key(signing_key);
338        
339        let token = authenticator.create_token(1024, 300).unwrap();
340        assert!(token.verify(authenticator.verifying_key()).is_ok());
341    }
342}