rust_crypto_utils/
lib.rs

1//! # Rust Crypto Utils v2.0
2//!
3//! Production-ready, memory-safe cryptographic utilities for financial systems and secure applications.
4//!
5//! ## Features
6//!
7//! - **Memory Safety**: Automatic zeroization of sensitive data
8//! - **Secure Password Hashing**: Argon2id with configurable parameters
9//! - **AES-256-GCM Encryption**: Authenticated encryption with associated data
10//! - **ChaCha20-Poly1305**: Alternative AEAD cipher for cross-platform use (v2.0)
11//! - **Key Derivation**: PBKDF2, HKDF, and Argon2 (NIST SP 800-132, RFC 5869)
12//! - **Digital Signatures**: Ed25519 and HMAC-SHA256/SHA3
13//! - **Key Exchange**: X25519 Diffie-Hellman key exchange (v2.0)
14//! - **Key Management**: Secure key storage with rotation policies
15//! - **Secure Random Generation**: Cryptographically secure random number generation
16//! - **Hash Functions**: SHA-256, SHA-3, BLAKE3 for high-performance hashing (v2.0)
17//! - **Constant-Time Operations**: Timing attack resistant comparisons (v2.0)
18//! - **Key Wrapping**: Secure key wrapping for key hierarchies (v2.0)
19//!
20//! ## Alignment with Federal Guidance
21//!
22//! Implements cryptographic best practices recommended by NIST and aligns with
23//! 2024 CISA/FBI guidance for memory-safe cryptographic implementations.
24//!
25//! ## What's New in v2.0
26//!
27//! - **ChaCha20-Poly1305**: Cross-platform authenticated encryption
28//! - **X25519 Key Exchange**: Secure key agreement protocol
29//! - **BLAKE3 Hashing**: High-performance cryptographic hashing
30//! - **SHA-3 Support**: NIST-approved hash function family
31//! - **Key Wrapping**: AES-KW for secure key storage
32//! - **Enhanced Key Rotation**: Automatic key rotation with versioning
33//! - **Audit Logging**: Cryptographic operation audit trail
34
35pub mod keyderivation;
36pub mod keymanagement;
37pub mod signatures;
38pub mod keyexchange;
39pub mod hashing;
40pub mod keywrap;
41
42pub use keyderivation::{DerivedKey, Hkdf, PasswordStrength, Pbkdf2};
43pub use keymanagement::{KeyMetadata, KeyStore, RotationPolicy};
44pub use signatures::{Ed25519KeyPair, Ed25519PublicKey, HmacKey, SignatureSuite};
45pub use keyexchange::{X25519KeyPair, X25519PublicKey, SharedSecret};
46pub use hashing::{HashAlgorithm, Hasher, HashOutput};
47pub use keywrap::{KeyWrapper, WrappedKey};
48
49use aes_gcm::{
50    aead::{Aead, KeyInit, OsRng},
51    Aes256Gcm, Nonce,
52};
53use argon2::{
54    password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
55    Algorithm, Argon2, Params, Version,
56};
57use hmac::Hmac;
58use rand::RngCore;
59use sha2::Sha256;
60use thiserror::Error;
61use zeroize::{Zeroize, ZeroizeOnDrop};
62
63type HmacSha256 = Hmac<Sha256>;
64
65/// Cryptographic errors
66#[derive(Error, Debug)]
67pub enum CryptoError {
68    #[error("Password hashing failed: {0}")]
69    HashingError(String),
70
71    #[error("Password verification failed")]
72    VerificationError,
73
74    #[error("Encryption failed: {0}")]
75    EncryptionError(String),
76
77    #[error("Decryption failed: {0}")]
78    DecryptionError(String),
79
80    #[error("Invalid key length")]
81    InvalidKeyLength,
82
83    #[error("HMAC generation failed: {0}")]
84    HmacError(String),
85
86    #[error("Weak password: {0}")]
87    WeakPassword(String),
88}
89
90/// Secure password with automatic zeroization
91#[derive(Zeroize, ZeroizeOnDrop)]
92pub struct SecurePassword {
93    password: Vec<u8>,
94}
95
96impl SecurePassword {
97    /// Create a new secure password
98    pub fn new(password: impl Into<Vec<u8>>) -> Self {
99        Self {
100            password: password.into(),
101        }
102    }
103
104    /// Get password bytes
105    pub fn as_bytes(&self) -> &[u8] {
106        &self.password
107    }
108}
109
110/// Secure encryption key with automatic zeroization
111#[derive(Zeroize, ZeroizeOnDrop)]
112pub struct SecureKey {
113    key: Vec<u8>,
114}
115
116impl SecureKey {
117    /// Create a new secure key from bytes
118    pub fn new(key: Vec<u8>) -> Result<Self, CryptoError> {
119        if key.len() != 32 {
120            return Err(CryptoError::InvalidKeyLength);
121        }
122        Ok(Self { key })
123    }
124
125    /// Generate a new random 256-bit key
126    pub fn generate() -> Self {
127        let mut key = vec![0u8; 32];
128        OsRng.fill_bytes(&mut key);
129        Self { key }
130    }
131
132    /// Get key bytes
133    pub fn as_bytes(&self) -> &[u8] {
134        &self.key
135    }
136}
137
138/// Password hashing utilities using Argon2id
139pub mod password {
140    use super::*;
141
142    /// Hash a password using Argon2id
143    ///
144    /// Uses Argon2id with secure default parameters:
145    /// - Memory cost: 19 MiB
146    /// - Time cost: 2 iterations
147    /// - Parallelism: 1
148    pub fn hash_password(password: &SecurePassword) -> Result<String, CryptoError> {
149        let salt = SaltString::generate(&mut OsRng);
150        let argon2 = Argon2::default();
151
152        let password_hash = argon2
153            .hash_password(password.as_bytes(), &salt)
154            .map_err(|e| CryptoError::HashingError(e.to_string()))?;
155
156        Ok(password_hash.to_string())
157    }
158
159    /// Verify a password against a hash
160    pub fn verify_password(password: &SecurePassword, hash: &str) -> Result<bool, CryptoError> {
161        let parsed_hash =
162            PasswordHash::new(hash).map_err(|e| CryptoError::HashingError(e.to_string()))?;
163
164        let argon2 = Argon2::default();
165
166        match argon2.verify_password(password.as_bytes(), &parsed_hash) {
167            Ok(()) => Ok(true),
168            Err(_) => Ok(false),
169        }
170    }
171
172    /// Password strength requirements
173    #[derive(Debug, Clone)]
174    pub struct PasswordStrength {
175        pub min_length: usize,
176        pub require_uppercase: bool,
177        pub require_lowercase: bool,
178        pub require_digit: bool,
179        pub require_special: bool,
180    }
181
182    impl Default for PasswordStrength {
183        fn default() -> Self {
184            Self {
185                min_length: 12,
186                require_uppercase: true,
187                require_lowercase: true,
188                require_digit: true,
189                require_special: true,
190            }
191        }
192    }
193
194    /// Validate password strength for financial systems
195    pub fn validate_password_strength(
196        password: &SecurePassword,
197        requirements: &PasswordStrength,
198    ) -> Result<(), CryptoError> {
199        let password_str = std::str::from_utf8(password.as_bytes())
200            .map_err(|_| CryptoError::WeakPassword("Invalid UTF-8".to_string()))?;
201
202        if password_str.len() < requirements.min_length {
203            return Err(CryptoError::WeakPassword(format!(
204                "Password must be at least {} characters",
205                requirements.min_length
206            )));
207        }
208
209        if requirements.require_uppercase && !password_str.chars().any(|c| c.is_uppercase()) {
210            return Err(CryptoError::WeakPassword(
211                "Password must contain uppercase letters".to_string(),
212            ));
213        }
214
215        if requirements.require_lowercase && !password_str.chars().any(|c| c.is_lowercase()) {
216            return Err(CryptoError::WeakPassword(
217                "Password must contain lowercase letters".to_string(),
218            ));
219        }
220
221        if requirements.require_digit && !password_str.chars().any(|c| c.is_ascii_digit()) {
222            return Err(CryptoError::WeakPassword(
223                "Password must contain digits".to_string(),
224            ));
225        }
226
227        if requirements.require_special
228            && !password_str
229                .chars()
230                .any(|c| !c.is_alphanumeric() && !c.is_whitespace())
231        {
232            return Err(CryptoError::WeakPassword(
233                "Password must contain special characters".to_string(),
234            ));
235        }
236
237        Ok(())
238    }
239
240    /// Derive an encryption key from a password using Argon2
241    pub fn derive_key_from_password(
242        password: &SecurePassword,
243        salt: &[u8],
244    ) -> Result<SecureKey, CryptoError> {
245        if salt.len() < 16 {
246            return Err(CryptoError::HashingError(
247                "Salt must be at least 16 bytes".to_string(),
248            ));
249        }
250
251        // Use high-security parameters for key derivation
252        let params = Params::new(65536, 3, 1, Some(32))
253            .map_err(|e| CryptoError::HashingError(e.to_string()))?;
254
255        let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
256
257        let mut key_bytes = vec![0u8; 32];
258        argon2
259            .hash_password_into(password.as_bytes(), salt, &mut key_bytes)
260            .map_err(|e| CryptoError::HashingError(e.to_string()))?;
261
262        Ok(SecureKey { key: key_bytes })
263    }
264}
265
266/// Encryption utilities using AES-256-GCM
267pub mod encryption {
268    use super::*;
269
270    /// Encrypted data with nonce
271    pub struct EncryptedData {
272        pub ciphertext: Vec<u8>,
273        pub nonce: [u8; 12],
274    }
275
276    /// Encrypt data using AES-256-GCM
277    pub fn encrypt(key: &SecureKey, plaintext: &[u8]) -> Result<EncryptedData, CryptoError> {
278        let cipher = Aes256Gcm::new_from_slice(key.as_bytes())
279            .map_err(|e| CryptoError::EncryptionError(e.to_string()))?;
280
281        let mut nonce_bytes = [0u8; 12];
282        OsRng.fill_bytes(&mut nonce_bytes);
283        let nonce = Nonce::from_slice(&nonce_bytes);
284
285        let ciphertext = cipher
286            .encrypt(nonce, plaintext)
287            .map_err(|e| CryptoError::EncryptionError(e.to_string()))?;
288
289        Ok(EncryptedData {
290            ciphertext,
291            nonce: nonce_bytes,
292        })
293    }
294
295    /// Decrypt data using AES-256-GCM
296    pub fn decrypt(key: &SecureKey, encrypted: &EncryptedData) -> Result<Vec<u8>, CryptoError> {
297        let cipher = Aes256Gcm::new_from_slice(key.as_bytes())
298            .map_err(|e| CryptoError::DecryptionError(e.to_string()))?;
299
300        let nonce = Nonce::from_slice(&encrypted.nonce);
301
302        let plaintext = cipher
303            .decrypt(nonce, encrypted.ciphertext.as_ref())
304            .map_err(|e| CryptoError::DecryptionError(e.to_string()))?;
305
306        Ok(plaintext)
307    }
308}
309
310/// Secure random number generation
311pub mod random {
312    use super::*;
313
314    /// Generate cryptographically secure random bytes
315    pub fn generate_random_bytes(length: usize) -> Vec<u8> {
316        let mut bytes = vec![0u8; length];
317        OsRng.fill_bytes(&mut bytes);
318        bytes
319    }
320
321    /// Generate a random hexadecimal string (for tokens, IDs, etc.)
322    pub fn generate_random_hex(length: usize) -> String {
323        let bytes = generate_random_bytes(length);
324        hex::encode(bytes)
325    }
326
327    /// Generate a random salt for password hashing/key derivation
328    pub fn generate_salt() -> Vec<u8> {
329        generate_random_bytes(32)
330    }
331}
332
333/// HMAC-SHA256 utilities for message authentication
334pub mod hmac_ops {
335    use super::*;
336    use hmac::Mac;
337
338    /// Compute HMAC-SHA256 for a message
339    pub fn compute_hmac(key: &SecureKey, message: &[u8]) -> Result<Vec<u8>, CryptoError> {
340        let mut mac = <HmacSha256 as Mac>::new_from_slice(key.as_bytes())
341            .map_err(|e| CryptoError::HmacError(e.to_string()))?;
342
343        mac.update(message);
344        Ok(mac.finalize().into_bytes().to_vec())
345    }
346
347    /// Verify HMAC-SHA256 for a message (constant-time comparison)
348    pub fn verify_hmac(
349        key: &SecureKey,
350        message: &[u8],
351        expected_hmac: &[u8],
352    ) -> Result<bool, CryptoError> {
353        let mut mac = <HmacSha256 as Mac>::new_from_slice(key.as_bytes())
354            .map_err(|e| CryptoError::HmacError(e.to_string()))?;
355
356        mac.update(message);
357
358        // Constant-time comparison to prevent timing attacks
359        match mac.verify_slice(expected_hmac) {
360            Ok(()) => Ok(true),
361            Err(_) => Ok(false),
362        }
363    }
364}
365
366/// Secure comparison utilities
367pub mod secure_compare {
368    /// Constant-time byte slice comparison to prevent timing attacks
369    pub fn constant_time_compare(a: &[u8], b: &[u8]) -> bool {
370        if a.len() != b.len() {
371            return false;
372        }
373
374        let mut result = 0u8;
375        for (byte_a, byte_b) in a.iter().zip(b.iter()) {
376            result |= byte_a ^ byte_b;
377        }
378
379        result == 0
380    }
381}
382
383#[cfg(test)]
384mod tests {
385    use super::*;
386
387    #[test]
388    fn test_password_hashing() {
389        let password = SecurePassword::new(b"MySecurePassword123!".to_vec());
390        let hash = password::hash_password(&password).unwrap();
391
392        assert!(password::verify_password(&password, &hash).unwrap());
393
394        let wrong_password = SecurePassword::new(b"WrongPassword".to_vec());
395        assert!(!password::verify_password(&wrong_password, &hash).unwrap());
396    }
397
398    #[test]
399    fn test_encryption_decryption() {
400        let key = SecureKey::generate();
401        let plaintext = b"Sensitive financial data: Account 123456, Balance: $50,000";
402
403        let encrypted = encryption::encrypt(&key, plaintext).unwrap();
404        let decrypted = encryption::decrypt(&key, &encrypted).unwrap();
405
406        assert_eq!(plaintext.as_slice(), decrypted.as_slice());
407    }
408
409    #[test]
410    fn test_encryption_with_wrong_key() {
411        let key1 = SecureKey::generate();
412        let key2 = SecureKey::generate();
413        let plaintext = b"Secret data";
414
415        let encrypted = encryption::encrypt(&key1, plaintext).unwrap();
416        let result = encryption::decrypt(&key2, &encrypted);
417
418        assert!(result.is_err());
419    }
420
421    #[test]
422    fn test_random_generation() {
423        let bytes1 = random::generate_random_bytes(32);
424        let bytes2 = random::generate_random_bytes(32);
425
426        assert_eq!(bytes1.len(), 32);
427        assert_eq!(bytes2.len(), 32);
428        assert_ne!(bytes1, bytes2); // Should be different
429    }
430
431    #[test]
432    fn test_random_hex() {
433        let hex = random::generate_random_hex(16);
434        assert_eq!(hex.len(), 32); // 16 bytes = 32 hex characters
435    }
436
437    #[test]
438    fn test_zeroization() {
439        let password_bytes = b"TestPassword123".to_vec();
440        {
441            let _secure_password = SecurePassword::new(password_bytes.clone());
442            // Password will be zeroized when it goes out of scope
443        }
444        // In a real scenario, you'd verify memory was zeroed
445        // This test confirms the ZeroizeOnDrop trait is applied
446    }
447
448    #[test]
449    fn test_key_length_validation() {
450        let short_key = vec![0u8; 16]; // Too short
451        let result = SecureKey::new(short_key);
452        assert!(result.is_err());
453
454        let valid_key = vec![0u8; 32];
455        let result = SecureKey::new(valid_key);
456        assert!(result.is_ok());
457    }
458
459    #[test]
460    fn test_password_strength_validation() {
461        // Strong password
462        let strong = SecurePassword::new(b"SecurePass123!@#".to_vec());
463        let requirements = password::PasswordStrength::default();
464        assert!(password::validate_password_strength(&strong, &requirements).is_ok());
465
466        // Too short
467        let short = SecurePassword::new(b"Short1!".to_vec());
468        let result = password::validate_password_strength(&short, &requirements);
469        assert!(result.is_err());
470
471        // No uppercase
472        let no_upper = SecurePassword::new(b"nouppercasehere123!".to_vec());
473        let result = password::validate_password_strength(&no_upper, &requirements);
474        assert!(result.is_err());
475
476        // No special characters
477        let no_special = SecurePassword::new(b"NoSpecialChars123".to_vec());
478        let result = password::validate_password_strength(&no_special, &requirements);
479        assert!(result.is_err());
480    }
481
482    #[test]
483    fn test_key_derivation_from_password() {
484        let password = SecurePassword::new(b"MyMasterPassword123!".to_vec());
485        let salt = random::generate_salt();
486
487        let key1 = password::derive_key_from_password(&password, &salt).unwrap();
488        let key2 = password::derive_key_from_password(&password, &salt).unwrap();
489
490        // Same password and salt should produce same key
491        assert_eq!(key1.as_bytes(), key2.as_bytes());
492
493        // Different salt should produce different key
494        let different_salt = random::generate_salt();
495        let key3 = password::derive_key_from_password(&password, &different_salt).unwrap();
496        assert_ne!(key1.as_bytes(), key3.as_bytes());
497    }
498
499    #[test]
500    fn test_hmac_generation_and_verification() {
501        let key = SecureKey::generate();
502        let message = b"Important financial transaction data";
503
504        let hmac_result = hmac_ops::compute_hmac(&key, message).unwrap();
505        assert_eq!(hmac_result.len(), 32); // SHA-256 produces 32 bytes
506
507        // Verify correct HMAC
508        assert!(hmac_ops::verify_hmac(&key, message, &hmac_result).unwrap());
509
510        // Verify fails with wrong key
511        let wrong_key = SecureKey::generate();
512        assert!(!hmac_ops::verify_hmac(&wrong_key, message, &hmac_result).unwrap());
513
514        // Verify fails with modified message
515        let modified_message = b"Modified transaction data";
516        assert!(!hmac_ops::verify_hmac(&key, modified_message, &hmac_result).unwrap());
517    }
518
519    #[test]
520    fn test_constant_time_compare() {
521        let data1 = b"secret_data";
522        let data2 = b"secret_data";
523        let data3 = b"different_data";
524
525        // Same data should match
526        assert!(secure_compare::constant_time_compare(data1, data2));
527
528        // Different data should not match
529        assert!(!secure_compare::constant_time_compare(data1, data3));
530
531        // Different lengths should not match
532        let short = b"short";
533        assert!(!secure_compare::constant_time_compare(data1, short));
534    }
535
536    #[test]
537    fn test_salt_generation() {
538        let salt1 = random::generate_salt();
539        let salt2 = random::generate_salt();
540
541        assert_eq!(salt1.len(), 32);
542        assert_eq!(salt2.len(), 32);
543        assert_ne!(salt1, salt2); // Should be different
544    }
545
546    #[test]
547    fn test_password_based_encryption() {
548        // Simulate real-world password-based encryption
549        let password = SecurePassword::new(b"UserMasterPassword123!".to_vec());
550        let salt = random::generate_salt();
551
552        // Derive encryption key from password
553        let key = password::derive_key_from_password(&password, &salt).unwrap();
554
555        // Encrypt data
556        let sensitive_data = b"Social Security Number: 123-45-6789";
557        let encrypted = encryption::encrypt(&key, sensitive_data).unwrap();
558
559        // Decrypt with same password-derived key
560        let decrypted = encryption::decrypt(&key, &encrypted).unwrap();
561
562        assert_eq!(sensitive_data.as_slice(), decrypted.as_slice());
563    }
564
565    #[test]
566    fn test_multiple_hmac_computations() {
567        let key = SecureKey::generate();
568        let message1 = b"Message 1";
569        let message2 = b"Message 2";
570
571        let hmac1 = hmac_ops::compute_hmac(&key, message1).unwrap();
572        let hmac2 = hmac_ops::compute_hmac(&key, message2).unwrap();
573
574        // Different messages should produce different HMACs
575        assert_ne!(hmac1, hmac2);
576
577        // Each HMAC should verify against its own message
578        assert!(hmac_ops::verify_hmac(&key, message1, &hmac1).unwrap());
579        assert!(hmac_ops::verify_hmac(&key, message2, &hmac2).unwrap());
580
581        // HMACs should not cross-verify
582        assert!(!hmac_ops::verify_hmac(&key, message1, &hmac2).unwrap());
583        assert!(!hmac_ops::verify_hmac(&key, message2, &hmac1).unwrap());
584    }
585
586    #[test]
587    fn test_encryption_with_derived_key() {
588        let password = SecurePassword::new(b"StrongPassword789!@#".to_vec());
589        let salt = random::generate_salt();
590        let key = password::derive_key_from_password(&password, &salt).unwrap();
591
592        let data = b"Encrypted with derived key";
593        let encrypted = encryption::encrypt(&key, data).unwrap();
594
595        // Verify we can decrypt
596        let decrypted = encryption::decrypt(&key, &encrypted).unwrap();
597        assert_eq!(data.as_slice(), decrypted.as_slice());
598
599        // Verify wrong password can't decrypt
600        let wrong_password = SecurePassword::new(b"WrongPassword123!".to_vec());
601        let wrong_key = password::derive_key_from_password(&wrong_password, &salt).unwrap();
602        let result = encryption::decrypt(&wrong_key, &encrypted);
603        assert!(result.is_err());
604    }
605}