flare_core/common/encryption/
algorithms.rs1use super::traits::Encryptor;
6use crate::common::error::{FlareError, Result};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
12pub enum EncryptionAlgorithm {
13 None,
15 Aes256Gcm,
17 Custom(String),
37}
38
39impl EncryptionAlgorithm {
40 #[allow(clippy::should_implement_trait)]
57 pub fn from_str(s: &str) -> Option<Self> {
58 match s.to_lowercase().as_str() {
59 "none" | "" => Some(EncryptionAlgorithm::None),
60 "aes256gcm" | "aes-256-gcm" => Some(EncryptionAlgorithm::Aes256Gcm),
61 custom => Some(EncryptionAlgorithm::Custom(custom.to_string())),
62 }
63 }
64
65 pub fn as_str(&self) -> String {
78 match self {
79 EncryptionAlgorithm::None => "none".to_string(),
80 EncryptionAlgorithm::Aes256Gcm => "aes256gcm".to_string(),
81 EncryptionAlgorithm::Custom(name) => name.clone(),
82 }
83 }
84
85 pub fn is_custom(&self) -> bool {
87 matches!(self, EncryptionAlgorithm::Custom(_))
88 }
89
90 pub fn custom_name(&self) -> Option<&str> {
92 match self {
93 EncryptionAlgorithm::Custom(name) => Some(name),
94 _ => None,
95 }
96 }
97}
98
99pub struct NoEncryptor;
101
102impl Encryptor for NoEncryptor {
103 fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
104 Ok(data.to_vec())
105 }
106
107 fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
108 Ok(data.to_vec())
109 }
110
111 fn algorithm(&self) -> EncryptionAlgorithm {
112 EncryptionAlgorithm::None
113 }
114
115 fn name(&self) -> &'static str {
116 "none"
117 }
118}
119
120pub struct Aes256GcmEncryptor {
130 #[cfg(feature = "encryption-aes-gcm")]
131 key: [u8; 32], }
133
134impl Aes256GcmEncryptor {
135 pub fn new(key: &[u8]) -> Result<Self> {
143 #[cfg(not(feature = "encryption-aes-gcm"))]
144 {
145 let _ = key;
146 return Err(FlareError::operation_not_supported(
147 "aes-256-gcm encryption feature is disabled",
148 ));
149 }
150
151 #[cfg(feature = "encryption-aes-gcm")]
152 {
153 if key.len() != 32 {
154 return Err(FlareError::protocol_error(format!(
155 "AES-256-GCM requires a 32-byte key, got {} bytes",
156 key.len()
157 )));
158 }
159
160 let mut key_array = [0u8; 32];
161 key_array.copy_from_slice(key);
162
163 Ok(Self { key: key_array })
164 }
165 }
166
167 pub fn from_password(password: &[u8], salt: Option<&[u8]>) -> Result<Self> {
176 #[cfg(not(feature = "encryption-aes-gcm"))]
177 {
178 let _ = (password, salt);
179 return Err(FlareError::operation_not_supported(
180 "aes-256-gcm encryption feature is disabled",
181 ));
182 }
183
184 #[cfg(feature = "encryption-aes-gcm")]
185 {
186 use sha2::{Digest, Sha256};
187
188 let mut hasher = Sha256::new();
189 hasher.update(password);
190 if let Some(s) = salt {
191 hasher.update(s);
192 }
193 let key = hasher.finalize();
194
195 Self::new(&key)
196 }
197 }
198}
199
200impl Encryptor for Aes256GcmEncryptor {
201 fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
202 #[cfg(not(feature = "encryption-aes-gcm"))]
203 {
204 let _ = data;
205 return Err(FlareError::operation_not_supported(
206 "aes-256-gcm encryption feature is disabled",
207 ));
208 }
209
210 #[cfg(feature = "encryption-aes-gcm")]
211 {
212 use aes_gcm::{
213 Aes256Gcm as AesGcm,
214 aead::{Aead, AeadCore, KeyInit, OsRng},
215 };
216 tracing::debug!("Encrypting data: {:?}", data);
217 let cipher = AesGcm::new_from_slice(&self.key).map_err(|e| {
219 FlareError::encoding_error(format!("Failed to create AES-GCM cipher: {}", e))
220 })?;
221
222 let nonce = AesGcm::generate_nonce(&mut OsRng);
224
225 let ciphertext = cipher.encrypt(&nonce, data).map_err(|e| {
227 FlareError::encoding_error(format!("AES-GCM encryption failed: {}", e))
228 })?;
229
230 let nonce_bytes: [u8; 12] = nonce.into();
234 let mut result = Vec::with_capacity(12 + ciphertext.len());
235 result.extend_from_slice(&nonce_bytes);
236 result.extend_from_slice(&ciphertext);
237
238 Ok(result)
239 }
240 }
241
242 fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
243 #[cfg(not(feature = "encryption-aes-gcm"))]
244 {
245 let _ = data;
246 return Err(FlareError::operation_not_supported(
247 "aes-256-gcm encryption feature is disabled",
248 ));
249 }
250
251 #[cfg(feature = "encryption-aes-gcm")]
252 {
253 use aes_gcm::{
254 Aes256Gcm as AesGcm, Nonce,
255 aead::{Aead, KeyInit},
256 };
257
258 tracing::debug!("Decrypting data: {:?}", data);
259 if data.len() < 12 {
261 return Err(FlareError::deserialization_error(format!(
262 "Encrypted data too short: expected at least 12 bytes, got {}",
263 data.len()
264 )));
265 }
266
267 let (nonce_bytes, ciphertext) = data.split_at(12);
269
270 let nonce_array: [u8; 12] = nonce_bytes.try_into().map_err(|_| {
272 FlareError::deserialization_error(
273 "Failed to convert nonce bytes to array".to_string(),
274 )
275 })?;
276
277 let nonce = Nonce::from(nonce_array);
279
280 let cipher = AesGcm::new_from_slice(&self.key).map_err(|e| {
282 FlareError::encoding_error(format!("Failed to create AES-GCM cipher: {}", e))
283 })?;
284
285 let plaintext = cipher.decrypt(&nonce, ciphertext).map_err(|e| {
287 FlareError::deserialization_error(format!("AES-GCM decryption failed: {}", e))
288 })?;
289
290 Ok(plaintext)
291 }
292 }
293
294 fn algorithm(&self) -> EncryptionAlgorithm {
295 EncryptionAlgorithm::Aes256Gcm
296 }
297
298 fn name(&self) -> &'static str {
299 "aes256gcm"
300 }
301}