//! Authentication and authorization for the networking layer use sha2::{ Digest, Sha256, }; use crate::networking::error::{ NetworkingError, Result, }; /// Validate session secret using constant-time comparison /// /// This function uses SHA-256 hash comparison to perform constant-time /// comparison and prevent timing attacks. The session secret is a pre-shared /// key that controls access to the gossip network. /// /// # Arguments /// * `provided` - The session secret provided by the joining peer /// * `expected` - The expected session secret configured for this node /// /// # Returns /// * `Ok(())` - Session secret is valid /// * `Err(NetworkingError::SecurityError)` - Session secret is invalid /// /// # Examples /// ``` /// use libmarathon::networking::auth::validate_session_secret; /// /// let secret = b"my_secret_key"; /// assert!(validate_session_secret(secret, secret).is_ok()); /// /// let wrong_secret = b"wrong_key"; /// assert!(validate_session_secret(wrong_secret, secret).is_err()); /// ``` pub fn validate_session_secret(provided: &[u8], expected: &[u8]) -> Result<()> { // Different lengths = definitely not equal, fail fast if provided.len() != expected.len() { return Err(NetworkingError::SecurityError( "Invalid session secret".to_string(), )); } // Hash both secrets for constant-time comparison let provided_hash = hash_secret(provided); let expected_hash = hash_secret(expected); // Compare hashes using constant-time comparison // This prevents timing attacks that could leak information about the secret if provided_hash != expected_hash { return Err(NetworkingError::SecurityError( "Invalid session secret".to_string(), )); } Ok(()) } /// Hash a secret using SHA-256 /// /// This is used internally for constant-time comparison of session secrets. fn hash_secret(secret: &[u8]) -> Vec { let mut hasher = Sha256::new(); hasher.update(secret); hasher.finalize().to_vec() } #[cfg(test)] mod tests { use super::*; #[test] fn test_valid_secret() { let secret = b"my_secret_key"; assert!(validate_session_secret(secret, secret).is_ok()); } #[test] fn test_invalid_secret() { let secret1 = b"my_secret_key"; let secret2 = b"wrong_secret_key"; let result = validate_session_secret(secret1, secret2); assert!(result.is_err()); match result { | Err(NetworkingError::SecurityError(_)) => {}, // Expected | _ => panic!("Expected SecurityError"), } } #[test] fn test_different_lengths() { let secret1 = b"short"; let secret2 = b"much_longer_secret"; let result = validate_session_secret(secret1, secret2); assert!(result.is_err()); } #[test] fn test_empty_secrets() { let empty = b""; assert!(validate_session_secret(empty, empty).is_ok()); } #[test] fn test_hash_is_deterministic() { let secret = b"test_secret"; let hash1 = hash_secret(secret); let hash2 = hash_secret(secret); assert_eq!(hash1, hash2); } #[test] fn test_different_secrets_have_different_hashes() { let secret1 = b"secret1"; let secret2 = b"secret2"; let hash1 = hash_secret(secret1); let hash2 = hash_secret(secret2); assert_ne!(hash1, hash2); } }