ant_quic/
auth.rs

1//! Authentication module for P2P connections using Ed25519 keys
2//!
3//! This module provides authentication functionality for P2P connections,
4//! including peer identity verification, challenge-response authentication,
5//! and secure session establishment.
6
7use std::{
8    collections::HashMap,
9    sync::Arc,
10    time::{Duration, Instant, SystemTime},
11};
12
13use ed25519_dalek::{
14    Signature, Signer, SigningKey as Ed25519SecretKey, Verifier, VerifyingKey as Ed25519PublicKey,
15};
16
17use serde::{Deserialize, Serialize};
18use tokio::sync::RwLock;
19use tracing::{debug, info, warn, error};
20
21use crate::{
22    nat_traversal_api::PeerId,
23    crypto::raw_public_keys::key_utils::{
24        public_key_to_bytes, public_key_from_bytes,
25        derive_peer_id_from_public_key, verify_peer_id,
26    },
27};
28
29/// Constant-time equality comparison for byte arrays
30fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
31    // For constant-time execution, we must not return early based on length
32    let len_equal = a.len() == b.len();
33    
34    // Process up to the shorter length to avoid bounds issues
35    let min_len = a.len().min(b.len());
36    let mut result = 0u8;
37    
38    // Compare bytes up to min length
39    for i in 0..min_len {
40        result |= a[i] ^ b[i];
41    }
42    
43    // If lengths differ, ensure result is non-zero
44    if !len_equal {
45        result |= 1;
46    }
47    
48    // Constant-time conversion to bool
49    result == 0
50}
51
52/// Authentication error types
53#[derive(Debug, thiserror::Error)]
54pub enum AuthError {
55    #[error("Invalid signature")]
56    InvalidSignature,
57    #[error("Challenge expired")]
58    ChallengeExpired,
59    #[error("Peer not found")]
60    PeerNotFound,
61    #[error("Authentication timeout")]
62    Timeout,
63    #[error("Invalid peer ID")]
64    InvalidPeerId,
65    #[error("Signature error: {0}")]
66    SignatureError(String),
67    #[error("Serialization error: {0}")]
68    SerializationError(String),
69    #[error("Key error: {0}")]
70    KeyError(String),
71}
72
73/// Authentication configuration
74#[derive(Debug, Clone)]
75pub struct AuthConfig {
76    /// Timeout for authentication handshake
77    pub auth_timeout: Duration,
78    /// Challenge validity duration
79    pub challenge_validity: Duration,
80    /// Whether to require authentication for all connections
81    pub require_authentication: bool,
82    /// Maximum number of authentication attempts
83    pub max_auth_attempts: u32,
84}
85
86impl Default for AuthConfig {
87    fn default() -> Self {
88        Self {
89            auth_timeout: Duration::from_secs(10),
90            challenge_validity: Duration::from_secs(60),
91            require_authentication: true,
92            max_auth_attempts: 3,
93        }
94    }
95}
96
97/// Authentication message types
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub enum AuthMessage {
100    /// Initial authentication request with public key
101    AuthRequest {
102        peer_id: PeerId,
103        public_key: [u8; 32],
104        timestamp: SystemTime,
105    },
106    /// Challenge to verify key ownership
107    Challenge {
108        nonce: [u8; 32],
109        timestamp: SystemTime,
110    },
111    /// Response to challenge with signature
112    ChallengeResponse {
113        nonce: [u8; 32],
114        signature: Vec<u8>,
115        timestamp: SystemTime,
116    },
117    /// Authentication successful
118    AuthSuccess {
119        session_id: [u8; 32],
120        timestamp: SystemTime,
121    },
122    /// Authentication failed
123    AuthFailure {
124        reason: String,
125    },
126}
127
128/// Authenticated peer information
129#[derive(Debug, Clone)]
130pub struct AuthenticatedPeer {
131    /// Peer ID derived from public key
132    pub peer_id: PeerId,
133    /// Ed25519 public key
134    pub public_key: Ed25519PublicKey,
135    /// When authentication was completed
136    pub authenticated_at: Instant,
137    /// Session ID for this connection
138    pub session_id: [u8; 32],
139}
140
141/// Authentication manager for handling peer authentication
142pub struct AuthManager {
143    /// Our Ed25519 secret key
144    secret_key: Ed25519SecretKey,
145    /// Our public key
146    public_key: Ed25519PublicKey,
147    /// Our peer ID
148    peer_id: PeerId,
149    /// Configuration
150    config: AuthConfig,
151    /// Authenticated peers
152    authenticated_peers: Arc<RwLock<HashMap<PeerId, AuthenticatedPeer>>>,
153    /// Pending challenges
154    pending_challenges: Arc<RwLock<HashMap<PeerId, PendingChallenge>>>,
155}
156
157/// Pending authentication challenge
158#[derive(Debug)]
159struct PendingChallenge {
160    nonce: [u8; 32],
161    created_at: Instant,
162    attempts: u32,
163}
164
165impl AuthManager {
166    /// Create a new authentication manager
167    pub fn new(secret_key: Ed25519SecretKey, config: AuthConfig) -> Self {
168        let public_key = secret_key.verifying_key();
169        let peer_id = derive_peer_id_from_public_key(&public_key);
170        
171        info!("Initialized AuthManager with peer ID: {:?}", peer_id);
172        
173        Self {
174            secret_key,
175            public_key,
176            peer_id,
177            config,
178            authenticated_peers: Arc::new(RwLock::new(HashMap::new())),
179            pending_challenges: Arc::new(RwLock::new(HashMap::new())),
180        }
181    }
182
183    /// Get our peer ID
184    pub fn peer_id(&self) -> PeerId {
185        self.peer_id
186    }
187
188    /// Get our public key bytes
189    pub fn public_key_bytes(&self) -> [u8; 32] {
190        public_key_to_bytes(&self.public_key)
191    }
192    
193    /// Get authentication configuration
194    pub fn config(&self) -> &AuthConfig {
195        &self.config
196    }
197
198    /// Create an authentication request
199    pub fn create_auth_request(&self) -> AuthMessage {
200        AuthMessage::AuthRequest {
201            peer_id: self.peer_id,
202            public_key: self.public_key_bytes(),
203            timestamp: SystemTime::now(),
204        }
205    }
206
207    /// Handle incoming authentication request
208    pub async fn handle_auth_request(
209        &self,
210        peer_id: PeerId,
211        public_key_bytes: [u8; 32],
212    ) -> Result<AuthMessage, AuthError> {
213        // Verify that the peer ID matches the public key
214        let public_key = public_key_from_bytes(&public_key_bytes)
215            .map_err(|e| AuthError::KeyError(e.to_string()))?;
216        
217        if !verify_peer_id(&peer_id, &public_key) {
218            return Err(AuthError::InvalidPeerId);
219        }
220
221        // Generate a challenge nonce
222        let nonce = {
223            use rand::Rng;
224            let mut nonce = [0u8; 32];
225            rand::thread_rng().fill(&mut nonce);
226            nonce
227        };
228
229        // Store the pending challenge
230        let mut challenges = self.pending_challenges.write().await;
231        challenges.insert(peer_id, PendingChallenge {
232            nonce,
233            created_at: Instant::now(),
234            attempts: 0,
235        });
236
237        debug!("Created challenge for peer {:?}", peer_id);
238
239        Ok(AuthMessage::Challenge {
240            nonce,
241            timestamp: SystemTime::now(),
242        })
243    }
244
245    /// Create a challenge response
246    pub fn create_challenge_response(&self, nonce: [u8; 32]) -> Result<AuthMessage, AuthError> {
247        // Sign the nonce with our private key
248        let signature = self.secret_key.sign(&nonce);
249        
250        Ok(AuthMessage::ChallengeResponse {
251            nonce,
252            signature: signature.to_vec(),
253            timestamp: SystemTime::now(),
254        })
255    }
256
257    /// Verify a challenge response
258    pub async fn verify_challenge_response(
259        &self,
260        peer_id: PeerId,
261        public_key_bytes: [u8; 32],
262        nonce: [u8; 32],
263        signature_bytes: &[u8],
264    ) -> Result<AuthMessage, AuthError> {
265        // Perform all operations to ensure constant timing
266        
267        // Step 1: Gather all data and perform checks without early returns
268        let mut challenges = self.pending_challenges.write().await;
269        let challenge_exists = challenges.contains_key(&peer_id);
270        let stored_nonce = challenges.get(&peer_id).map(|c| c.nonce).unwrap_or([0u8; 32]);
271        let created_at = challenges.get(&peer_id).map(|c| c.created_at).unwrap_or(Instant::now());
272        let attempts = challenges.get_mut(&peer_id).map(|c| {
273            c.attempts += 1;
274            c.attempts
275        }).unwrap_or(0);
276        
277        // Check conditions (but don't return early)
278        let nonce_matches = constant_time_eq(&stored_nonce, &nonce);
279        let not_expired = created_at.elapsed() <= self.config.challenge_validity;
280        let attempts_ok = attempts < self.config.max_auth_attempts;
281        
282        // Step 2: Parse keys and signature (always do this)
283        let public_key_result = public_key_from_bytes(&public_key_bytes);
284        let signature_result = Signature::from_slice(signature_bytes);
285        
286        // Step 3: Verify signature (always attempt this)
287        let verification_result = match (public_key_result, signature_result) {
288            (Ok(pk), Ok(sig)) => pk.verify(&nonce, &sig).is_ok(),
289            _ => false,
290        };
291        
292        // Step 4: Generate session data (always do this to maintain constant timing)
293        let session_id = {
294            use rand::Rng;
295            let mut id = [0u8; 32];
296            rand::thread_rng().fill(&mut id);
297            id
298        };
299        
300        // Step 5: Determine final result based on all checks
301        let all_valid = challenge_exists && nonce_matches && not_expired && 
302                       attempts_ok && verification_result;
303        
304        debug!("Verification results - exists: {}, nonce_matches: {}, not_expired: {}, attempts_ok: {}, verification: {}", 
305               challenge_exists, nonce_matches, not_expired, attempts_ok, verification_result);
306        
307        // Step 6: Clean up and store results based on validity
308        if all_valid {
309            // Remove the challenge
310            challenges.remove(&peer_id);
311            drop(challenges); // Release lock before acquiring peers lock
312            
313            // Store authenticated peer
314            if let Ok(public_key) = public_key_from_bytes(&public_key_bytes) {
315                let mut peers = self.authenticated_peers.write().await;
316                peers.insert(peer_id, AuthenticatedPeer {
317                    peer_id,
318                    public_key,
319                    authenticated_at: Instant::now(),
320                    session_id,
321                });
322                
323                info!("Successfully authenticated peer {:?}", peer_id);
324            }
325            
326            Ok(AuthMessage::AuthSuccess {
327                session_id,
328                timestamp: SystemTime::now(),
329            })
330        } else {
331            // Determine specific error (but after all operations complete)
332            let error = if !challenge_exists {
333                AuthError::PeerNotFound
334            } else if !not_expired {
335                challenges.remove(&peer_id);
336                AuthError::ChallengeExpired
337            } else if !attempts_ok {
338                challenges.remove(&peer_id);
339                AuthError::InvalidSignature
340            } else if !nonce_matches {
341                AuthError::InvalidSignature
342            } else {
343                AuthError::InvalidSignature
344            };
345            
346            Err(error)
347        }
348    }
349
350    /// Check if a peer is authenticated
351    pub async fn is_authenticated(&self, peer_id: &PeerId) -> bool {
352        let peers = self.authenticated_peers.read().await;
353        peers.contains_key(peer_id)
354    }
355
356    /// Get authenticated peer information
357    pub async fn get_authenticated_peer(&self, peer_id: &PeerId) -> Option<AuthenticatedPeer> {
358        let peers = self.authenticated_peers.read().await;
359        peers.get(peer_id).cloned()
360    }
361
362    /// Handle successful authentication from responder
363    pub async fn handle_auth_success(
364        &self,
365        peer_id: PeerId,
366        public_key_bytes: [u8; 32],
367        session_id: [u8; 32],
368    ) -> Result<(), AuthError> {
369        // Parse the public key
370        let public_key = public_key_from_bytes(&public_key_bytes)
371            .map_err(|e| AuthError::KeyError(e.to_string()))?;
372        
373        // Store the authenticated peer
374        let mut peers = self.authenticated_peers.write().await;
375        peers.insert(peer_id, AuthenticatedPeer {
376            peer_id,
377            public_key,
378            authenticated_at: Instant::now(),
379            session_id,
380        });
381        
382        info!("Marked peer {:?} as authenticated after receiving AuthSuccess", peer_id);
383        Ok(())
384    }
385
386    /// Remove an authenticated peer
387    pub async fn remove_peer(&self, peer_id: &PeerId) {
388        let mut peers = self.authenticated_peers.write().await;
389        if peers.remove(peer_id).is_some() {
390            info!("Removed authenticated peer {:?}", peer_id);
391        }
392    }
393
394    /// Clean up expired challenges
395    pub async fn cleanup_expired_challenges(&self) {
396        let mut challenges = self.pending_challenges.write().await;
397        let now = Instant::now();
398        
399        challenges.retain(|peer_id, challenge| {
400            let expired = now.duration_since(challenge.created_at) <= self.config.challenge_validity;
401            if !expired {
402                debug!("Removing expired challenge for peer {:?}", peer_id);
403            }
404            expired
405        });
406    }
407
408    /// Get list of authenticated peers
409    pub async fn list_authenticated_peers(&self) -> Vec<PeerId> {
410        let peers = self.authenticated_peers.read().await;
411        peers.keys().cloned().collect()
412    }
413
414    /// Serialize an auth message
415    pub fn serialize_message(msg: &AuthMessage) -> Result<Vec<u8>, AuthError> {
416        serde_json::to_vec(msg)
417            .map_err(|e| AuthError::SerializationError(e.to_string()))
418    }
419
420    /// Deserialize an auth message
421    pub fn deserialize_message(data: &[u8]) -> Result<AuthMessage, AuthError> {
422        serde_json::from_slice(data)
423            .map_err(|e| AuthError::SerializationError(e.to_string()))
424    }
425}
426
427/// Authentication protocol handler for integration with QuicP2PNode
428pub struct AuthProtocol {
429    auth_manager: Arc<AuthManager>,
430    /// Temporary storage for public keys during authentication
431    pending_auth: Arc<tokio::sync::RwLock<HashMap<PeerId, [u8; 32]>>>,
432}
433
434impl AuthProtocol {
435    /// Create a new authentication protocol handler
436    pub fn new(auth_manager: Arc<AuthManager>) -> Self {
437        Self { 
438            auth_manager,
439            pending_auth: Arc::new(tokio::sync::RwLock::new(HashMap::new())),
440        }
441    }
442
443    /// Handle incoming authentication message
444    pub async fn handle_message(
445        &self,
446        peer_id: PeerId,
447        message: AuthMessage,
448    ) -> Result<Option<AuthMessage>, AuthError> {
449        match message {
450            AuthMessage::AuthRequest { peer_id: req_peer_id, public_key, .. } => {
451                if req_peer_id != peer_id {
452                    return Err(AuthError::InvalidPeerId);
453                }
454                // Store the public key for later verification
455                self.pending_auth.write().await.insert(peer_id, public_key);
456                let response = self.auth_manager.handle_auth_request(peer_id, public_key).await?;
457                Ok(Some(response))
458            }
459            AuthMessage::Challenge { nonce, .. } => {
460                let response = self.auth_manager.create_challenge_response(nonce)?;
461                Ok(Some(response))
462            }
463            AuthMessage::ChallengeResponse { nonce, signature, .. } => {
464                // Get the public key from the initial auth request
465                let public_key_bytes = match self.pending_auth.read().await.get(&peer_id) {
466                    Some(key) => *key,
467                    None => return Err(AuthError::PeerNotFound),
468                };
469                
470                let response = self.auth_manager.verify_challenge_response(
471                    peer_id,
472                    public_key_bytes,
473                    nonce,
474                    &signature,
475                ).await?;
476                
477                // Remove the pending auth entry on success
478                if matches!(response, AuthMessage::AuthSuccess { .. }) {
479                    self.pending_auth.write().await.remove(&peer_id);
480                }
481                
482                Ok(Some(response))
483            }
484            AuthMessage::AuthSuccess { session_id, .. } => {
485                info!("Authentication successful with peer {:?}, session: {:?}", 
486                      peer_id, hex::encode(&session_id));
487                Ok(None)
488            }
489            AuthMessage::AuthFailure { reason } => {
490                warn!("Authentication failed with peer {:?}: {}", peer_id, reason);
491                Err(AuthError::InvalidSignature)
492            }
493        }
494    }
495
496    /// Initiate authentication with a peer
497    pub async fn initiate_auth(&self) -> AuthMessage {
498        self.auth_manager.create_auth_request()
499    }
500}
501
502#[cfg(test)]
503mod tests {
504    use super::*;
505    use crate::crypto::raw_public_keys::key_utils::generate_ed25519_keypair;
506
507    #[tokio::test]
508    async fn test_auth_manager_creation() {
509        let (secret_key, _) = generate_ed25519_keypair();
510        let config = AuthConfig::default();
511        let auth_manager = AuthManager::new(secret_key, config);
512        
513        // Verify peer ID is derived correctly
514        let peer_id = auth_manager.peer_id();
515        assert_eq!(peer_id.0.len(), 32);
516    }
517
518    #[tokio::test]
519    async fn test_authentication_flow() {
520        // Create two auth managers (simulating two peers)
521        let (secret_key1, public_key1) = generate_ed25519_keypair();
522        let (secret_key2, _) = generate_ed25519_keypair();
523        
524        let auth1 = AuthManager::new(secret_key1, AuthConfig::default());
525        let auth2 = AuthManager::new(secret_key2, AuthConfig::default());
526        
527        // Peer 1 creates auth request
528        let auth_request = auth1.create_auth_request();
529        
530        // Peer 2 handles the request and creates a challenge
531        let challenge = match &auth_request {
532            AuthMessage::AuthRequest { peer_id, public_key, .. } => {
533                auth2.handle_auth_request(*peer_id, *public_key).await.unwrap()
534            }
535            _ => panic!("Expected AuthRequest"),
536        };
537        
538        // Peer 1 responds to the challenge
539        let response = match &challenge {
540            AuthMessage::Challenge { nonce, .. } => {
541                auth1.create_challenge_response(*nonce).unwrap()
542            }
543            _ => panic!("Expected Challenge"),
544        };
545        
546        // Peer 2 verifies the response
547        let result = match &response {
548            AuthMessage::ChallengeResponse { nonce, signature, .. } => {
549                auth2.verify_challenge_response(
550                    auth1.peer_id(),
551                    public_key_to_bytes(&public_key1),
552                    *nonce,
553                    signature,
554                ).await
555            }
556            _ => panic!("Expected ChallengeResponse"),
557        };
558        
559        // Should be successful
560        assert!(matches!(result, Ok(AuthMessage::AuthSuccess { .. })));
561        
562        // Peer 1 should now be authenticated by peer 2
563        assert!(auth2.is_authenticated(&auth1.peer_id()).await);
564    }
565
566    #[tokio::test]
567    async fn test_invalid_signature() {
568        let (secret_key1, _) = generate_ed25519_keypair();
569        let (secret_key2, public_key2) = generate_ed25519_keypair();
570        
571        let auth1 = AuthManager::new(secret_key1, AuthConfig::default());
572        let _auth2 = AuthManager::new(secret_key2, AuthConfig::default());
573        
574        // Create a challenge
575        let peer_id2 = derive_peer_id_from_public_key(&public_key2);
576        let challenge = auth1.handle_auth_request(peer_id2, public_key_to_bytes(&public_key2))
577            .await.unwrap();
578        
579        // Create an invalid response (wrong signature)
580        let invalid_signature = vec![0u8; 64];
581        let nonce = match &challenge {
582            AuthMessage::Challenge { nonce, .. } => *nonce,
583            _ => panic!("Expected Challenge"),
584        };
585        
586        // Verification should fail
587        let result = auth1.verify_challenge_response(
588            peer_id2,
589            public_key_to_bytes(&public_key2),
590            nonce,
591            &invalid_signature,
592        ).await;
593        
594        assert!(matches!(result, Err(AuthError::InvalidSignature)));
595    }
596
597    #[tokio::test]
598    async fn test_challenge_expiry() {
599        let (secret_key, public_key) = generate_ed25519_keypair();
600        let mut config = AuthConfig::default();
601        config.challenge_validity = Duration::from_millis(100); // Very short for testing
602        
603        let auth = AuthManager::new(secret_key, config);
604        let peer_id = derive_peer_id_from_public_key(&public_key);
605        
606        // Create a challenge
607        let _challenge = auth.handle_auth_request(peer_id, public_key_to_bytes(&public_key))
608            .await.unwrap();
609        
610        // Wait for it to expire
611        tokio::time::sleep(Duration::from_millis(200)).await;
612        
613        // Try to verify - should fail due to expiry
614        let result = auth.verify_challenge_response(
615            peer_id,
616            public_key_to_bytes(&public_key),
617            [0u8; 32], // dummy nonce
618            &[0u8; 64], // dummy signature
619        ).await;
620        
621        assert!(matches!(result, Err(AuthError::ChallengeExpired)));
622    }
623
624    #[tokio::test]
625    async fn test_message_serialization() {
626        let (_, public_key) = generate_ed25519_keypair();
627        let peer_id = derive_peer_id_from_public_key(&public_key);
628        
629        let msg = AuthMessage::AuthRequest {
630            peer_id,
631            public_key: public_key_to_bytes(&public_key),
632            timestamp: SystemTime::now(),
633        };
634        
635        // Serialize and deserialize
636        let serialized = AuthManager::serialize_message(&msg).unwrap();
637        let deserialized = AuthManager::deserialize_message(&serialized).unwrap();
638        
639        match (msg, deserialized) {
640            (
641                AuthMessage::AuthRequest { peer_id: p1, public_key: k1, .. },
642                AuthMessage::AuthRequest { peer_id: p2, public_key: k2, .. }
643            ) => {
644                assert_eq!(p1, p2);
645                assert_eq!(k1, k2);
646            }
647            _ => panic!("Message mismatch"),
648        }
649    }
650}