use super::traits::Encryptor;
use crate::common::error::{FlareError, Result};
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub enum EncryptionAlgorithm {
None,
Aes256Gcm,
Custom(String),
}
impl EncryptionAlgorithm {
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"none" | "" => Some(EncryptionAlgorithm::None),
"aes256gcm" | "aes-256-gcm" => Some(EncryptionAlgorithm::Aes256Gcm),
custom => Some(EncryptionAlgorithm::Custom(custom.to_string())),
}
}
pub fn as_str(&self) -> String {
match self {
EncryptionAlgorithm::None => "none".to_string(),
EncryptionAlgorithm::Aes256Gcm => "aes256gcm".to_string(),
EncryptionAlgorithm::Custom(name) => name.clone(),
}
}
pub fn is_custom(&self) -> bool {
matches!(self, EncryptionAlgorithm::Custom(_))
}
pub fn custom_name(&self) -> Option<&str> {
match self {
EncryptionAlgorithm::Custom(name) => Some(name),
_ => None,
}
}
}
pub struct NoEncryptor;
impl Encryptor for NoEncryptor {
fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
Ok(data.to_vec())
}
fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
Ok(data.to_vec())
}
fn algorithm(&self) -> EncryptionAlgorithm {
EncryptionAlgorithm::None
}
fn name(&self) -> &'static str {
"none"
}
}
pub struct Aes256GcmEncryptor {
#[cfg(feature = "encryption-aes-gcm")]
key: [u8; 32], }
impl Aes256GcmEncryptor {
pub fn new(key: &[u8]) -> Result<Self> {
#[cfg(not(feature = "encryption-aes-gcm"))]
{
let _ = key;
return Err(FlareError::operation_not_supported(
"aes-256-gcm encryption feature is disabled",
));
}
#[cfg(feature = "encryption-aes-gcm")]
{
if key.len() != 32 {
return Err(FlareError::protocol_error(format!(
"AES-256-GCM requires a 32-byte key, got {} bytes",
key.len()
)));
}
let mut key_array = [0u8; 32];
key_array.copy_from_slice(key);
Ok(Self { key: key_array })
}
}
pub fn from_password(password: &[u8], salt: Option<&[u8]>) -> Result<Self> {
#[cfg(not(feature = "encryption-aes-gcm"))]
{
let _ = (password, salt);
return Err(FlareError::operation_not_supported(
"aes-256-gcm encryption feature is disabled",
));
}
#[cfg(feature = "encryption-aes-gcm")]
{
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(password);
if let Some(s) = salt {
hasher.update(s);
}
let key = hasher.finalize();
Self::new(&key)
}
}
}
impl Encryptor for Aes256GcmEncryptor {
fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
#[cfg(not(feature = "encryption-aes-gcm"))]
{
let _ = data;
return Err(FlareError::operation_not_supported(
"aes-256-gcm encryption feature is disabled",
));
}
#[cfg(feature = "encryption-aes-gcm")]
{
use aes_gcm::{
Aes256Gcm as AesGcm,
aead::{Aead, AeadCore, KeyInit, OsRng},
};
tracing::debug!("Encrypting data: {:?}", data);
let cipher = AesGcm::new_from_slice(&self.key).map_err(|e| {
FlareError::encoding_error(format!("Failed to create AES-GCM cipher: {}", e))
})?;
let nonce = AesGcm::generate_nonce(&mut OsRng);
let ciphertext = cipher.encrypt(&nonce, data).map_err(|e| {
FlareError::encoding_error(format!("AES-GCM encryption failed: {}", e))
})?;
let nonce_bytes: [u8; 12] = nonce.into();
let mut result = Vec::with_capacity(12 + ciphertext.len());
result.extend_from_slice(&nonce_bytes);
result.extend_from_slice(&ciphertext);
Ok(result)
}
}
fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
#[cfg(not(feature = "encryption-aes-gcm"))]
{
let _ = data;
return Err(FlareError::operation_not_supported(
"aes-256-gcm encryption feature is disabled",
));
}
#[cfg(feature = "encryption-aes-gcm")]
{
use aes_gcm::{
Aes256Gcm as AesGcm, Nonce,
aead::{Aead, KeyInit},
};
tracing::debug!("Decrypting data: {:?}", data);
if data.len() < 12 {
return Err(FlareError::deserialization_error(format!(
"Encrypted data too short: expected at least 12 bytes, got {}",
data.len()
)));
}
let (nonce_bytes, ciphertext) = data.split_at(12);
let nonce_array: [u8; 12] = nonce_bytes.try_into().map_err(|_| {
FlareError::deserialization_error(
"Failed to convert nonce bytes to array".to_string(),
)
})?;
let nonce = Nonce::from(nonce_array);
let cipher = AesGcm::new_from_slice(&self.key).map_err(|e| {
FlareError::encoding_error(format!("Failed to create AES-GCM cipher: {}", e))
})?;
let plaintext = cipher.decrypt(&nonce, ciphertext).map_err(|e| {
FlareError::deserialization_error(format!("AES-GCM decryption failed: {}", e))
})?;
Ok(plaintext)
}
}
fn algorithm(&self) -> EncryptionAlgorithm {
EncryptionAlgorithm::Aes256Gcm
}
fn name(&self) -> &'static str {
"aes256gcm"
}
}