crypto.rs (2198B)
1 use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _}; 2 use rand::Rng; 3 4 /// Generate cryptographically secure random bytes 5 /// 6 /// Returns a vector of random bytes of the specified length 7 pub fn generate_random_bytes(length: usize) -> Vec<u8> { 8 let mut rng = rand::thread_rng(); 9 let mut bytes = vec![0u8; length]; 10 rng.fill(&mut bytes[..]); 11 bytes 12 } 13 14 /// Generate a cryptographically secure nonce 15 /// 16 /// Format: base64 encoded random bytes 17 /// 18 /// Example: "k7E9mZqYvXwPxR2nT8uL5sA6fH3jC1dG4bN0iM9oU2p" 19 pub fn generate_nonce(bytes_len: usize) -> String { 20 let bytes = generate_random_bytes(bytes_len); 21 URL_SAFE_NO_PAD.encode(bytes) 22 } 23 24 /// Generate a cryptographically secure access token 25 /// 26 /// Format: base64 encoded random bytes 27 /// 28 /// Example: "xR2nT8uL5sA6fH3jC1dG4bN0iM9oU2pk7E9mZqYvXwP" 29 pub fn generate_token(bytes_len: usize) -> String { 30 let bytes = generate_random_bytes(bytes_len); 31 URL_SAFE_NO_PAD.encode(bytes) 32 } 33 34 /// Generate a cryptographically secure authorization code 35 /// 36 /// Format: base64 encoded random bytes 37 /// 38 /// Example: "a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5P6q7R8s9T0u1V" 39 pub fn generate_authorization_code(bytes_len: usize) -> String { 40 let bytes = generate_random_bytes(bytes_len); 41 URL_SAFE_NO_PAD.encode(bytes) 42 } 43 44 #[cfg(test)] 45 mod tests { 46 use super::*; 47 use std::collections::HashSet; 48 49 #[test] 50 fn test_nonce_generation() { 51 let nonce = generate_nonce(32); 52 53 // Check length (32 bytes base64 = 43 chars without padding) 54 assert_eq!(nonce.len(), 43); 55 56 // Check it's URL-safe (only contains valid characters) 57 assert!(nonce.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '_')); 58 } 59 60 #[test] 61 fn test_nonces_are_unique() { 62 let mut nonces = HashSet::new(); 63 64 // Generate 1000 nonces, all should be unique 65 for _ in 0..1000 { 66 let nonce = generate_nonce(32); 67 assert!(nonces.insert(nonce), "Duplicate nonce generated!"); 68 } 69 } 70 71 #[test] 72 fn test_no_padding_in_tokens() { 73 // Ensure no '=' padding characters 74 let nonce = generate_nonce(32); 75 76 assert!(!nonce.contains('=')); 77 } 78 }