use super::algorithms::NoEncryptor;
use super::traits::Encryptor;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::RwLock;
lazy_static::lazy_static! {
static ref ENCRYPTION_REGISTRY: EncryptionRegistry = {
let registry = EncryptionRegistry::new();
registry.register_defaults();
registry
};
}
pub struct EncryptionRegistry {
encryptors: Arc<RwLock<HashMap<String, Arc<dyn Encryptor>>>>,
}
impl Default for EncryptionRegistry {
fn default() -> Self {
Self {
encryptors: Arc::new(RwLock::new(HashMap::new())),
}
}
}
impl EncryptionRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register_defaults(&self) {
self.register("none", Arc::new(NoEncryptor));
}
pub fn register(&self, name: &str, encryptor: Arc<dyn Encryptor>) {
if let Ok(mut encryptors) = self.encryptors.write() {
encryptors.insert(name.to_string(), encryptor);
}
}
pub fn find(&self, name: &str) -> Option<Arc<dyn Encryptor>> {
if let Ok(encryptors) = self.encryptors.read() {
encryptors.get(name).cloned()
} else {
None
}
}
pub fn is_registered(&self, name: &str) -> bool {
if let Ok(encryptors) = self.encryptors.read() {
encryptors.contains_key(name)
} else {
false
}
}
pub fn list_registered(&self) -> Vec<String> {
if let Ok(encryptors) = self.encryptors.read() {
encryptors.keys().cloned().collect()
} else {
Vec::new()
}
}
}
impl EncryptionRegistry {
pub fn global() -> &'static EncryptionRegistry {
&ENCRYPTION_REGISTRY
}
}
pub struct EncryptionUtil;
impl EncryptionUtil {
pub fn find(name: &str) -> Option<Arc<dyn Encryptor>> {
EncryptionRegistry::global().find(name)
}
pub fn is_registered(name: &str) -> bool {
EncryptionRegistry::global().is_registered(name)
}
pub fn register_custom(encryptor: Arc<dyn Encryptor>) {
let name = encryptor.name();
if EncryptionUtil::is_registered(name) {
tracing::warn!(
"Encryptor '{}' is already registered. Skipping registration to avoid key mismatch.",
name
);
return;
}
EncryptionRegistry::global().register(name, encryptor);
tracing::debug!("Registered encryptor: {}", name);
}
pub fn list_registered() -> Vec<String> {
EncryptionRegistry::global().list_registered()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encryption_registry() {
let registry = EncryptionRegistry::new();
registry.register_defaults();
assert!(registry.find("none").is_some());
assert!(registry.find("unknown").is_none());
}
#[test]
fn test_no_encryptor() {
let encryptor = NoEncryptor;
let data = b"hello world";
let encrypted = encryptor.encrypt(data).unwrap();
let decrypted = encryptor.decrypt(&encrypted).unwrap();
assert_eq!(data, decrypted.as_slice());
}
#[test]
#[cfg(feature = "encryption-aes-gcm")]
fn test_aes256gcm_encryptor() {
use crate::common::encryption::Aes256GcmEncryptor;
let key = b"01234567890123456789012345678901"; let encryptor = Aes256GcmEncryptor::new(key).unwrap();
let plaintext = b"Hello, World! This is a test message for AES-256-GCM encryption.";
let ciphertext = encryptor.encrypt(plaintext).unwrap();
assert!(ciphertext.len() >= plaintext.len() + 12 + 16);
let decrypted = encryptor.decrypt(&ciphertext).unwrap();
assert_eq!(plaintext, decrypted.as_slice());
let ciphertext2 = encryptor.encrypt(plaintext).unwrap();
assert_ne!(ciphertext, ciphertext2);
let decrypted2 = encryptor.decrypt(&ciphertext2).unwrap();
assert_eq!(plaintext, decrypted2.as_slice());
}
#[test]
#[cfg(feature = "encryption-aes-gcm")]
fn test_aes256gcm_from_password() {
use crate::common::encryption::Aes256GcmEncryptor;
let password = b"my_secret_password";
let salt = b"some_salt";
let encryptor = Aes256GcmEncryptor::from_password(password, Some(salt)).unwrap();
let plaintext = b"Test message";
let ciphertext = encryptor.encrypt(plaintext).unwrap();
let decrypted = encryptor.decrypt(&ciphertext).unwrap();
assert_eq!(plaintext, decrypted.as_slice());
}
#[test]
#[cfg(feature = "encryption-aes-gcm")]
fn test_aes256gcm_invalid_key_length() {
use crate::common::encryption::Aes256GcmEncryptor;
let short_key = b"short"; assert!(Aes256GcmEncryptor::new(short_key).is_err());
let long_key = b"0123456789012345678901234567890123456789"; assert!(Aes256GcmEncryptor::new(long_key).is_err());
}
#[test]
#[cfg(feature = "encryption-aes-gcm")]
fn test_aes256gcm_invalid_ciphertext() {
use crate::common::encryption::Aes256GcmEncryptor;
let key = b"01234567890123456789012345678901";
let encryptor = Aes256GcmEncryptor::new(key).unwrap();
let short_data = b"short";
assert!(encryptor.decrypt(short_data).is_err());
let invalid_data = vec![0u8; 20]; assert!(encryptor.decrypt(&invalid_data).is_err());
}
#[test]
#[cfg(not(feature = "encryption-aes-gcm"))]
fn test_aes256gcm_constructor_reports_disabled_feature() {
use crate::common::encryption::Aes256GcmEncryptor;
use crate::common::error::ErrorCode;
match Aes256GcmEncryptor::new(b"01234567890123456789012345678901") {
Ok(_) => panic!("AES-GCM should report disabled feature"),
Err(err) => assert_eq!(err.code(), Some(ErrorCode::OperationNotSupported)),
}
}
}