//! Authentication simulation example //! //! This example simulates the authentication flow without requiring a running server. //! It demonstrates: //! 1. Key generation and management //! 2. Nonce request simulation //! 3. Message signing and verification //! 4. Credential management //! 5. Authentication state checking use log::{error, info}; use std::time::{SystemTime, UNIX_EPOCH}; // Import authentication modules use circle_client_ws::CircleWsClientBuilder; #[cfg(feature = "crypto")] use circle_client_ws::auth::{ derive_public_key, generate_private_key, sign_message, verify_signature, AuthCredentials, NonceResponse, }; #[tokio::main] async fn main() -> Result<(), Box> { // Initialize logging env_logger::init(); info!("🔐 Starting authentication simulation example"); // Step 1: Generate cryptographic keys info!("🔑 Generating cryptographic keys..."); #[cfg(feature = "crypto")] let (private_key, public_key) = { let private_key = generate_private_key()?; let public_key = derive_public_key(&private_key)?; info!("✅ Generated private key: {}...", &private_key[..10]); info!("✅ Derived public key: {}...", &public_key[..20]); (private_key, public_key) }; #[cfg(not(feature = "crypto"))] let (private_key, _public_key) = { let private_key = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef".to_string(); let public_key = "04abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890".to_string(); info!("📝 Using fallback keys (crypto feature disabled)"); (private_key, public_key) }; // Step 2: Simulate nonce request and response info!("📡 Simulating nonce request..."); let current_time = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); let simulated_nonce = format!("nonce_{}_{}", current_time, "abcdef123456"); let expires_at = current_time + 300; // 5 minutes from now #[cfg(feature = "crypto")] let nonce_response = NonceResponse { nonce: simulated_nonce.clone(), expires_at, }; info!("✅ Simulated nonce response:"); info!(" Nonce: {}", simulated_nonce); info!(" Expires at: {}", expires_at); // Step 3: Sign the nonce info!("✍️ Signing nonce with private key..."); #[cfg(feature = "crypto")] let signature = { match sign_message(&private_key, &simulated_nonce) { Ok(sig) => { info!("✅ Signature created: {}...", &sig[..20]); sig } Err(e) => { error!("❌ Failed to sign message: {}", e); return Err(e.into()); } } }; #[cfg(not(feature = "crypto"))] let _signature = { info!("📝 Using fallback signature (crypto feature disabled)"); "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890".to_string() }; // Step 4: Verify the signature info!("🔍 Verifying signature..."); #[cfg(feature = "crypto")] { match verify_signature(&public_key, &simulated_nonce, &signature) { Ok(true) => info!("✅ Signature verification successful!"), Ok(false) => { error!("❌ Signature verification failed!"); return Err("Signature verification failed".into()); } Err(e) => { error!("❌ Signature verification error: {}", e); return Err(e.into()); } } } #[cfg(not(feature = "crypto"))] { info!("📝 Skipping signature verification (crypto feature disabled)"); } // Step 5: Create authentication credentials info!("📋 Creating authentication credentials..."); #[cfg(feature = "crypto")] let credentials = AuthCredentials::new( public_key.clone(), signature.clone(), nonce_response.nonce.clone(), expires_at, ); #[cfg(feature = "crypto")] { info!("✅ Credentials created:"); info!(" Public key: {}...", &credentials.public_key()[..20]); info!(" Signature: {}...", &credentials.signature()[..20]); info!(" Nonce: {}", credentials.nonce()); info!(" Expires at: {}", credentials.expires_at); info!(" Is expired: {}", credentials.is_expired()); info!(" Expires within 60s: {}", credentials.expires_within(60)); info!( " Expires within 400s: {}", credentials.expires_within(400) ); } // Step 6: Create client with authentication info!("🔌 Creating WebSocket client with authentication..."); let _client = CircleWsClientBuilder::new("ws://localhost:8080/ws".to_string()) .with_keypair(private_key.clone()) .build(); info!("✅ Client created"); // Step 7: Demonstrate key rotation info!("🔄 Demonstrating key rotation..."); #[cfg(feature = "crypto")] { let new_private_key = generate_private_key()?; let new_public_key = derive_public_key(&new_private_key)?; info!("✅ Generated new keys:"); info!(" New private key: {}...", &new_private_key[..10]); info!(" New public key: {}...", &new_public_key[..20]); // Create new client with rotated keys let _new_client = CircleWsClientBuilder::new("ws://localhost:8080/ws".to_string()) .with_keypair(new_private_key) .build(); info!("✅ Created client with rotated keys"); } #[cfg(not(feature = "crypto"))] { info!("📝 Skipping key rotation (crypto feature disabled)"); } // Step 8: Demonstrate credential expiration info!("⏰ Demonstrating credential expiration..."); // Create credentials that expire soon #[cfg(feature = "crypto")] let short_lived_credentials = AuthCredentials::new( public_key, signature, nonce_response.nonce, current_time + 5, // Expires in 5 seconds ); #[cfg(feature = "crypto")] { info!("✅ Created short-lived credentials:"); info!(" Expires at: {}", short_lived_credentials.expires_at); info!(" Is expired: {}", short_lived_credentials.is_expired()); info!( " Expires within 10s: {}", short_lived_credentials.expires_within(10) ); // Wait a moment and check again tokio::time::sleep(std::time::Duration::from_secs(1)).await; info!("⏳ After 1 second:"); info!(" Is expired: {}", short_lived_credentials.is_expired()); info!( " Expires within 5s: {}", short_lived_credentials.expires_within(5) ); } info!("🎉 Authentication simulation completed successfully!"); Ok(()) } #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_key_generation() { #[cfg(feature = "crypto")] { let private_key = generate_private_key().unwrap(); assert!(private_key.starts_with("0x")); assert_eq!(private_key.len(), 66); // 0x + 64 hex chars let public_key = derive_public_key(&private_key).unwrap(); assert!(public_key.starts_with("04")); assert_eq!(public_key.len(), 130); // 04 + 128 hex chars (uncompressed) } } #[tokio::test] async fn test_signature_flow() { #[cfg(feature = "crypto")] { let private_key = generate_private_key().unwrap(); let public_key = derive_public_key(&private_key).unwrap(); let message = "test_nonce_12345"; let signature = sign_message(&private_key, message).unwrap(); let is_valid = verify_signature(&public_key, message, &signature).unwrap(); assert!(is_valid); } } #[test] fn test_credentials() { let current_time = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); #[cfg(feature = "crypto")] let credentials = AuthCredentials::new( "04abcdef...".to_string(), "0x123456...".to_string(), "nonce_123".to_string(), current_time + 300, ); #[cfg(feature = "crypto")] { assert!(!credentials.is_expired()); assert!(credentials.expires_within(400)); assert!(!credentials.expires_within(100)); } } }