sentinel_crypto/encrypt/
encryption_key.rs

1use crate::error::CryptoError;
2
3/// Encryption key management utilities
4pub struct EncryptionKeyManager;
5
6impl EncryptionKeyManager {
7    /// Generate a new random 256-bit encryption key
8    pub fn generate_key() -> [u8; 32] { rand::random() }
9
10    /// Rotate key: generate new key and return both old and new
11    /// For rotation, you might want to re-encrypt data with the new key
12    pub fn rotate_key(_old_key: &[u8; 32]) -> ([u8; 32], [u8; 32]) {
13        let new_key = Self::generate_key();
14        (*_old_key, new_key)
15    }
16
17    /// Export key as hex
18    pub fn export_key(key: &[u8; 32]) -> String { hex::encode(key) }
19
20    /// Import key from hex
21    pub fn import_key(hex: &str) -> Result<[u8; 32], CryptoError> {
22        let bytes = hex::decode(hex).map_err(CryptoError::Hex)?;
23        let array: [u8; 32] = bytes
24            .as_slice()
25            .try_into()
26            .map_err(|_| CryptoError::InvalidKeyLength)?;
27        Ok(array)
28    }
29
30    /// Generate a key from a passphrase using the default KDF
31    pub async fn derive_key_from_passphrase(passphrase: &str) -> Result<(Vec<u8>, [u8; 32]), CryptoError> {
32        crate::derive_key_from_passphrase(passphrase).await
33    }
34
35    /// Generate a key from a passphrase using the provided salt
36    pub async fn derive_key_from_passphrase_with_salt(passphrase: &str, salt: &[u8]) -> Result<[u8; 32], CryptoError> {
37        crate::derive_key_from_passphrase_with_salt(passphrase, salt).await
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn test_generate_key() {
47        let key = EncryptionKeyManager::generate_key();
48        assert_eq!(key.len(), 32);
49    }
50
51    #[test]
52    fn test_export_import_key() {
53        let key = EncryptionKeyManager::generate_key();
54        let hex = EncryptionKeyManager::export_key(&key);
55        let imported = EncryptionKeyManager::import_key(&hex).unwrap();
56        assert_eq!(key, imported);
57    }
58
59    #[test]
60    fn test_rotate_key() {
61        let old_key = EncryptionKeyManager::generate_key();
62        let (returned_old, new_key) = EncryptionKeyManager::rotate_key(&old_key);
63        assert_eq!(old_key, returned_old);
64        assert_ne!(old_key, new_key);
65    }
66
67    #[tokio::test]
68    #[serial_test::serial]
69    async fn test_derive_key() {
70        crate::crypto_config::reset_global_crypto_config_for_tests().await;
71        let config = crate::CryptoConfig {
72            hash_algorithm:           crate::HashAlgorithmChoice::Blake3,
73            signature_algorithm:      crate::SignatureAlgorithmChoice::Ed25519,
74            encryption_algorithm:     crate::EncryptionAlgorithmChoice::XChaCha20Poly1305,
75            key_derivation_algorithm: crate::KeyDerivationAlgorithmChoice::Argon2id,
76        };
77        let _ = crate::set_global_crypto_config(config).await;
78
79        let (salt1, key1) = EncryptionKeyManager::derive_key_from_passphrase("test")
80            .await
81            .unwrap();
82        assert_eq!(key1.len(), 32);
83        assert_eq!(salt1.len(), 32);
84
85        // Same passphrase with same salt should give same key
86        let key1_again = EncryptionKeyManager::derive_key_from_passphrase_with_salt("test", &salt1)
87            .await
88            .unwrap();
89        assert_eq!(key1, key1_again);
90
91        // Different passphrase should give different key
92        let (_salt2, key2) = EncryptionKeyManager::derive_key_from_passphrase("different")
93            .await
94            .unwrap();
95        assert_ne!(key1, key2);
96    }
97
98    #[tokio::test]
99    #[serial_test::serial]
100    async fn test_derive_key_pbkdf2() {
101        crate::crypto_config::reset_global_crypto_config_for_tests().await;
102        let config = crate::CryptoConfig {
103            hash_algorithm:           crate::HashAlgorithmChoice::Blake3,
104            signature_algorithm:      crate::SignatureAlgorithmChoice::Ed25519,
105            encryption_algorithm:     crate::EncryptionAlgorithmChoice::XChaCha20Poly1305,
106            key_derivation_algorithm: crate::KeyDerivationAlgorithmChoice::Pbkdf2,
107        };
108        let _ = crate::set_global_crypto_config(config).await;
109
110        let (salt1, key1) = EncryptionKeyManager::derive_key_from_passphrase("test")
111            .await
112            .unwrap();
113        assert_eq!(key1.len(), 32);
114        assert_eq!(salt1.len(), 32);
115
116        // Same passphrase with same salt should give same key
117        let key1_again = EncryptionKeyManager::derive_key_from_passphrase_with_salt("test", &salt1)
118            .await
119            .unwrap();
120        assert_eq!(key1, key1_again);
121
122        // Different passphrase should give different key
123        let (_salt2, key2) = EncryptionKeyManager::derive_key_from_passphrase("different")
124            .await
125            .unwrap();
126        assert_ne!(key1, key2);
127    }
128}