gestura_core_security/
encryption.rs1use crate::SecureStorage;
13use gestura_core_foundation::AppError;
14use ring::aead::{AES_256_GCM, Aad, LessSafeKey, Nonce, UnboundKey};
15use ring::rand::{SecureRandom, SystemRandom};
16
17pub struct Encryptor {
19 key: LessSafeKey,
20 rng: SystemRandom,
21}
22
23impl Encryptor {
24 pub fn new() -> Result<Self, AppError> {
26 let rng = SystemRandom::new();
27 let mut key_bytes = [0u8; 32];
28 rng.fill(&mut key_bytes)
29 .map_err(|_| AppError::Internal("failed to generate encryption key".to_string()))?;
30 Self::from_key(&key_bytes)
31 }
32
33 pub fn from_key(key_bytes: &[u8; 32]) -> Result<Self, AppError> {
35 let rng = SystemRandom::new();
36 let unbound_key = UnboundKey::new(&AES_256_GCM, key_bytes)
37 .map_err(|_| AppError::Internal("failed to create encryption key".to_string()))?;
38 let key = LessSafeKey::new(unbound_key);
39 Ok(Self { key, rng })
40 }
41
42 pub fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>, AppError> {
44 let mut nonce_bytes = [0u8; 12];
45 self.rng
46 .fill(&mut nonce_bytes)
47 .map_err(|_| AppError::Internal("failed to generate nonce".to_string()))?;
48 let nonce = Nonce::assume_unique_for_key(nonce_bytes);
49
50 let mut in_out = data.to_vec();
51 self.key
52 .seal_in_place_append_tag(nonce, Aad::empty(), &mut in_out)
53 .map_err(|_| AppError::Internal("encryption failed".to_string()))?;
54
55 let mut result = nonce_bytes.to_vec();
56 result.extend_from_slice(&in_out);
57 Ok(result)
58 }
59
60 pub fn decrypt(&self, encrypted_data: &[u8]) -> Result<Vec<u8>, AppError> {
62 if encrypted_data.len() < 12 {
63 return Err(AppError::InvalidInput(
64 "encrypted data too short".to_string(),
65 ));
66 }
67
68 let (nonce_bytes, ciphertext) = encrypted_data.split_at(12);
69 let nonce = Nonce::try_assume_unique_for_key(nonce_bytes)
70 .map_err(|_| AppError::InvalidInput("invalid nonce".to_string()))?;
71
72 let mut in_out = ciphertext.to_vec();
73 let plaintext = self
74 .key
75 .open_in_place(nonce, Aad::empty(), &mut in_out)
76 .map_err(|_| AppError::InvalidInput("decryption failed".to_string()))?;
77 Ok(plaintext.to_vec())
78 }
79}
80
81pub struct SecureConfigManager {
83 encryptor: Encryptor,
84 #[allow(dead_code)]
85 storage: Box<dyn SecureStorage>,
86}
87
88impl SecureConfigManager {
89 pub async fn new(storage: Box<dyn SecureStorage>) -> Result<Self, AppError> {
93 let encryptor = match storage.get_secret("config_encryption_key").await? {
94 Some(key_hex) => {
95 let key_bytes = hex::decode(key_hex)
96 .map_err(|_| AppError::InvalidInput("invalid key format".to_string()))?;
97 if key_bytes.len() != 32 {
98 return Err(AppError::InvalidInput("invalid key length".to_string()));
99 }
100 let mut key_array = [0u8; 32];
101 key_array.copy_from_slice(&key_bytes);
102 Encryptor::from_key(&key_array)?
103 }
104 None => {
105 let rng = SystemRandom::new();
106 let mut key_bytes = [0u8; 32];
107 rng.fill(&mut key_bytes)
108 .map_err(|_| AppError::Internal("failed to generate key".to_string()))?;
109 let key_hex = hex::encode(key_bytes);
110 storage
111 .store_secret("config_encryption_key", &key_hex)
112 .await?;
113 Encryptor::from_key(&key_bytes)?
114 }
115 };
116
117 Ok(Self { encryptor, storage })
118 }
119
120 pub async fn with_storage(storage: Box<dyn SecureStorage>) -> Result<Self, AppError> {
122 Self::new(storage).await
123 }
124
125 pub fn encrypt<T: serde::Serialize>(&self, data: &T) -> Result<Vec<u8>, AppError> {
127 let json = serde_json::to_string(data)?;
128 self.encryptor.encrypt(json.as_bytes())
129 }
130
131 pub fn decrypt<T: serde::de::DeserializeOwned>(&self, encrypted: &[u8]) -> Result<T, AppError> {
133 let decrypted = self.encryptor.decrypt(encrypted)?;
134 let json = String::from_utf8(decrypted)
135 .map_err(|_| AppError::InvalidInput("invalid UTF-8".to_string()))?;
136 serde_json::from_str(&json).map_err(AppError::Json)
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use crate::MockSecureStorage;
144
145 #[test]
146 fn test_encryptor_encrypt_decrypt() {
147 let encryptor = Encryptor::new().unwrap();
148 let plaintext = b"Hello, World!";
149 let encrypted = encryptor.encrypt(plaintext).unwrap();
150 let decrypted = encryptor.decrypt(&encrypted).unwrap();
151 assert_eq!(decrypted, plaintext);
152 }
153
154 #[test]
155 fn test_encryptor_from_key() {
156 let key = [0u8; 32];
157 let encryptor = Encryptor::from_key(&key).unwrap();
158 let plaintext = b"test data";
159 let encrypted = encryptor.encrypt(plaintext).unwrap();
160
161 let encryptor2 = Encryptor::from_key(&key).unwrap();
163 let decrypted = encryptor2.decrypt(&encrypted).unwrap();
164 assert_eq!(decrypted, plaintext);
165 }
166
167 #[test]
168 fn test_decrypt_invalid_data() {
169 let encryptor = Encryptor::new().unwrap();
170 let result = encryptor.decrypt(&[0u8; 5]);
171 assert!(result.is_err());
172 }
173
174 #[tokio::test]
175 async fn test_secure_config_manager() {
176 let storage = Box::new(MockSecureStorage::new());
177 let manager = SecureConfigManager::with_storage(storage).await.unwrap();
178
179 #[derive(serde::Serialize, serde::Deserialize, PartialEq, Debug)]
180 struct TestConfig {
181 api_key: String,
182 enabled: bool,
183 }
184
185 let config = TestConfig {
186 api_key: "secret123".to_string(),
187 enabled: true,
188 };
189
190 let encrypted = manager.encrypt(&config).unwrap();
191 let decrypted: TestConfig = manager.decrypt(&encrypted).unwrap();
192 assert_eq!(decrypted, config);
193 }
194}