use anyhow::{Result, anyhow};
use ring::aead::{AES_256_GCM, LessSafeKey, UnboundKey, Nonce, NONCE_LEN};
use ring::rand::{SecureRandom, SystemRandom};
use ring::pbkdf2;
use ring::digest::{Context, SHA256};
use serde::{Serialize, Deserialize};
use std::sync::{Arc, RwLock};
use std::collections::HashMap;
use base64::{Engine as _, engine::general_purpose};
use keyring::{Entry, Error as KeyringError};
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::fs;
use uuid::Uuid;
use crate::types::EncryptionConfig;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EncryptedData {
pub data: String, pub nonce: String, pub algorithm: String,
pub version: String,
pub timestamp: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EncryptedEmbedding {
pub data: Vec<f32>,
pub dimension: usize,
pub embedding_type: String,
pub algorithm: String,
pub timestamp: String,
}
#[derive(Debug, Clone)]
pub struct DerivedKey {
pub key: Vec<u8>,
pub salt: String,
}
pub struct EncryptionService {
config: EncryptionConfig,
main_key: RwLock<Arc<LessSafeKey>>,
embedding_key: RwLock<Arc<LessSafeKey>>,
sync_key: RwLock<Arc<LessSafeKey>>,
rng: SystemRandom,
service_name: String,
key_name: String,
base_path: String,
initialized: RwLock<bool>,
}
impl EncryptionService {
pub async fn new(config: &EncryptionConfig, base_path: &str) -> Result<Self> {
let service = Self {
config: config.clone(),
base_path: base_path.to_string(),
main_key: RwLock::new(Arc::new(Self::create_dummy_key()?)),
embedding_key: RwLock::new(Arc::new(Self::create_dummy_key()?)),
sync_key: RwLock::new(Arc::new(Self::create_dummy_key()?)),
rng: SystemRandom::new(),
service_name: "cloudops-storage-service".to_string(),
key_name: "encryption-key".to_string(),
initialized: RwLock::new(false),
};
Ok(service)
}
pub async fn initialize(&self) -> Result<()> {
match self.get_or_create_keys().await {
Ok((main_key, embedding_key, sync_key)) => {
*self.main_key.write().unwrap() = Arc::new(main_key);
*self.embedding_key.write().unwrap() = Arc::new(embedding_key);
*self.sync_key.write().unwrap() = Arc::new(sync_key);
*self.initialized.write().unwrap() = true;
println!("✅ EncryptionService initialized with OS Keychain integration");
Ok(())
}
Err(e) => {
eprintln!("❌ Failed to initialize EncryptionService: {}", e);
Err(anyhow!("Failed to initialize encryption service: {}", e))
}
}
}
async fn get_or_create_keys(&self) -> Result<(LessSafeKey, LessSafeKey, LessSafeKey)> {
let main_key = self.get_or_create_key("main").await?;
let embedding_key = self.get_or_create_key("embedding").await?;
let sync_key = self.get_or_create_key("sync").await?;
Ok((main_key, embedding_key, sync_key))
}
async fn get_or_create_key(&self, key_type: &str) -> Result<LessSafeKey> {
let key_name = format!("{}-{}", self.key_name, key_type);
match self.get_key_from_keychain(&key_name).await {
Ok(key_bytes) => {
if key_bytes.len() == 32 {
println!("🔑 Retrieved existing {} key from OS Keychain", key_type);
return Self::create_key_from_bytes(&key_bytes);
}
}
Err(_) => {
if let Ok(key_bytes) = self.get_key_from_backup_file(key_type).await {
if key_bytes.len() == 32 {
println!("🔑 Retrieved {} key from backup file", key_type);
let _ = self.store_key_in_keychain(&key_name, &key_bytes).await;
return Self::create_key_from_bytes(&key_bytes);
}
}
}
}
let key_bytes = Self::generate_key()?;
if let Err(e) = self.store_key_in_keychain(&key_name, &key_bytes).await {
eprintln!("⚠️ Keychain storage failed for {}: {}. Using session-only key.", key_type, e);
} else {
println!("🔑 Created new {} key and stored in OS Keychain", key_type);
}
if let Err(e) = self.store_key_backup_file(key_type, &key_bytes).await {
eprintln!("⚠️ Backup file storage failed for {}: {}", key_type, e);
} else {
println!("💾 Backed up {} key to file", key_type);
}
Self::create_key_from_bytes(&key_bytes)
}
async fn get_key_from_keychain(&self, key_name: &str) -> Result<Vec<u8>> {
let entry = Entry::new(&self.service_name, key_name)?;
let key_b64 = entry.get_password()?;
let key_bytes = general_purpose::STANDARD.decode(key_b64)?;
Ok(key_bytes)
}
async fn store_key_in_keychain(&self, key_name: &str, key_bytes: &[u8]) -> Result<()> {
let entry = Entry::new(&self.service_name, key_name)?;
let key_b64 = general_purpose::STANDARD.encode(key_bytes);
entry.set_password(&key_b64)?;
Ok(())
}
fn get_backup_keys_dir(&self) -> std::path::PathBuf {
std::path::PathBuf::from(&self.base_path).join("keys")
}
async fn store_key_backup_file(&self, key_type: &str, key_bytes: &[u8]) -> Result<()> {
let keys_dir = self.get_backup_keys_dir();
tokio::fs::create_dir_all(&keys_dir).await?;
let key_file = keys_dir.join(format!("{}_key.enc", key_type));
let key_b64 = general_purpose::STANDARD.encode(key_bytes);
tokio::fs::write(&key_file, key_b64).await?;
Ok(())
}
async fn get_key_from_backup_file(&self, key_type: &str) -> Result<Vec<u8>> {
let keys_dir = self.get_backup_keys_dir();
let key_file = keys_dir.join(format!("{}_key.enc", key_type));
if !key_file.exists() {
return Err(anyhow!("Backup key file not found"));
}
let key_b64 = tokio::fs::read_to_string(&key_file).await?;
let key_bytes = general_purpose::STANDARD.decode(key_b64.trim())?;
Ok(key_bytes)
}
fn create_key_from_bytes(key_bytes: &[u8]) -> Result<LessSafeKey> {
let unbound_key = UnboundKey::new(&AES_256_GCM, key_bytes)
.map_err(|_| anyhow!("Failed to create encryption key"))?;
Ok(LessSafeKey::new(unbound_key))
}
fn create_dummy_key() -> Result<LessSafeKey> {
let key_bytes = Self::generate_key()?;
Self::create_key_from_bytes(&key_bytes)
}
fn generate_key() -> Result<[u8; 32]> {
let rng = SystemRandom::new();
let mut key_bytes = [0u8; 32];
rng.fill(&mut key_bytes)
.map_err(|_| anyhow!("Failed to generate random key"))?;
Ok(key_bytes)
}
pub async fn encrypt_content(&self, content: &str) -> Result<String> {
if !self.config.enable_content_encryption {
return Ok(content.to_string());
}
self.ensure_initialized()?;
let main_key = self.main_key.read().unwrap().clone();
let encrypted = self.encrypt_with_key(content.as_bytes(), &main_key).await?;
Ok(encrypted)
}
pub async fn decrypt_content(&self, encrypted_content: &str) -> Result<String> {
if !self.config.enable_content_encryption {
return Ok(encrypted_content.to_string());
}
self.ensure_initialized()?;
let main_key = self.main_key.read().unwrap().clone();
if let Ok(decrypted_bytes) = self.decrypt_with_key_base64(encrypted_content, &main_key).await {
return String::from_utf8(decrypted_bytes)
.map_err(|e| anyhow!("Invalid UTF-8 in decrypted content: {}", e));
}
if let Ok(encrypted_data) = serde_json::from_str::<EncryptedData>(encrypted_content) {
let decrypted_bytes = self.decrypt_with_key(&encrypted_data, &main_key).await?;
String::from_utf8(decrypted_bytes).map_err(|e| anyhow!("Invalid UTF-8 in decrypted content: {}", e))
} else {
Ok(encrypted_content.to_string())
}
}
pub async fn encrypt(&self, plaintext: &str) -> Result<String> {
self.encrypt_content(plaintext).await
}
pub async fn decrypt(&self, encrypted_data: &str) -> Result<String> {
self.decrypt_content(encrypted_data).await
}
pub fn encrypt_sync(&self, plaintext: &str) -> Result<String> {
if !self.config.enable_content_encryption {
return Ok(plaintext.to_string());
}
self.ensure_initialized()?;
let main_key = self.main_key.read().unwrap().clone();
self.encrypt_with_key_sync(plaintext.as_bytes(), &main_key)
}
pub fn decrypt_sync(&self, encrypted_content: &str) -> Result<String> {
if !self.config.enable_content_encryption {
return Ok(encrypted_content.to_string());
}
self.ensure_initialized()?;
let main_key = self.main_key.read().unwrap().clone();
if let Ok(decrypted_bytes) = self.decrypt_with_key_base64_sync(encrypted_content, &main_key) {
return String::from_utf8(decrypted_bytes)
.map_err(|e| anyhow!("Invalid UTF-8 in decrypted content: {}", e));
}
if let Ok(encrypted_data) = serde_json::from_str::<EncryptedData>(encrypted_content) {
let decrypted_bytes = self.decrypt_with_key_sync(&encrypted_data, &main_key)?;
String::from_utf8(decrypted_bytes).map_err(|e| anyhow!("Invalid UTF-8 in decrypted content: {}", e))
} else {
Ok(encrypted_content.to_string())
}
}
pub async fn encrypt_metadata(&self, metadata: &serde_json::Value) -> Result<serde_json::Value> {
if !self.config.enable_metadata_encryption {
return Ok(metadata.clone());
}
let metadata_str = serde_json::to_string(metadata)?;
let encrypted = self.encrypt(&metadata_str).await?;
let mut result = serde_json::json!({});
result["_encrypted_metadata"] = serde_json::Value::String(encrypted); result["_encrypted"] = serde_json::Value::Bool(true);
Ok(result)
}
pub async fn decrypt_metadata(&self, metadata: &serde_json::Value) -> Result<serde_json::Value> {
if !self.config.enable_metadata_encryption {
return Ok(metadata.clone());
}
if let Some(encrypted_str) = metadata.get("_encrypted_metadata") {
if let Some(encrypted_str) = encrypted_str.as_str() {
let decrypted_str = self.decrypt(encrypted_str).await?; let decrypted_metadata: serde_json::Value = serde_json::from_str(&decrypted_str)?;
return Ok(decrypted_metadata);
}
}
Ok(metadata.clone())
}
pub async fn encrypt_embedding(&self, embedding: &[f32]) -> Result<String> {
self.ensure_initialized()?;
let embedding_data = EncryptedEmbedding {
data: embedding.to_vec(),
dimension: embedding.len(),
embedding_type: "float32".to_string(),
algorithm: self.config.algorithm.clone(),
timestamp: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
.to_string(),
};
let embedding_json = serde_json::to_string(&embedding_data)?;
let embedding_key = self.embedding_key.read().unwrap().clone();
let encrypted = self.encrypt_with_key(embedding_json.as_bytes(), &embedding_key).await?;
Ok(encrypted)
}
pub async fn decrypt_embedding(&self, encrypted_embedding: &str) -> Result<Vec<f32>> {
self.ensure_initialized()?;
let encrypted_data: EncryptedData = serde_json::from_str(encrypted_embedding)?;
let embedding_key = self.embedding_key.read().unwrap().clone();
let decrypted_bytes = self.decrypt_with_key(&encrypted_data, &embedding_key).await?;
let decrypted_str = String::from_utf8(decrypted_bytes)?;
let embedding_data: EncryptedEmbedding = serde_json::from_str(&decrypted_str)?;
Ok(embedding_data.data)
}
pub async fn encrypt_for_sync(&self, data: &serde_json::Value) -> Result<String> {
self.ensure_initialized()?;
let data_str = serde_json::to_string(data)?;
let sync_key = self.sync_key.read().unwrap().clone();
let encrypted = self.encrypt_with_key(data_str.as_bytes(), &sync_key).await?;
Ok(encrypted)
}
pub async fn decrypt_from_sync(&self, encrypted_data: &str) -> Result<serde_json::Value> {
self.ensure_initialized()?;
let sync_key = self.sync_key.read().unwrap().clone();
if let Ok(decrypted_bytes) = self.decrypt_with_key_base64(encrypted_data, &sync_key).await {
let decrypted_str = String::from_utf8(decrypted_bytes)?;
let data: serde_json::Value = serde_json::from_str(&decrypted_str)?;
return Ok(data);
}
let encrypted: EncryptedData = serde_json::from_str(encrypted_data)?;
let decrypted_bytes = self.decrypt_with_key(&encrypted, &sync_key).await?;
let decrypted_str = String::from_utf8(decrypted_bytes)?;
let data: serde_json::Value = serde_json::from_str(&decrypted_str)?;
Ok(data)
}
async fn encrypt_with_key(&self, data: &[u8], key: &LessSafeKey) -> Result<String> {
let mut nonce_bytes = [0u8; NONCE_LEN];
self.rng.fill(&mut nonce_bytes)
.map_err(|_| anyhow!("Failed to generate nonce"))?;
let nonce = Nonce::assume_unique_for_key(nonce_bytes);
let mut in_out = data.to_vec();
key.seal_in_place_append_tag(nonce, ring::aead::Aad::empty(), &mut in_out)
.map_err(|_| anyhow!("Failed to encrypt data"))?;
let mut combined = Vec::new();
combined.extend_from_slice(&nonce_bytes); combined.extend_from_slice(&in_out);
Ok(general_purpose::STANDARD.encode(&combined))
}
async fn decrypt_with_key_base64(&self, encrypted_base64: &str, key: &LessSafeKey) -> Result<Vec<u8>> {
let combined = general_purpose::STANDARD.decode(encrypted_base64)
.map_err(|e| anyhow!("Failed to decode base64 encrypted data: {}", e))?;
if combined.len() < NONCE_LEN + 16 { return Err(anyhow!("Invalid encrypted data format"));
}
let nonce_bytes = &combined[0..NONCE_LEN];
let mut encrypted_with_tag = combined[NONCE_LEN..].to_vec();
let mut nonce_array = [0u8; NONCE_LEN];
nonce_array.copy_from_slice(nonce_bytes);
let nonce = Nonce::assume_unique_for_key(nonce_array);
let plaintext = key.open_in_place(nonce, ring::aead::Aad::empty(), &mut encrypted_with_tag)
.map_err(|_| anyhow!("Failed to decrypt data"))?;
Ok(plaintext.to_vec())
}
async fn decrypt_with_key(&self, encrypted: &EncryptedData, key: &LessSafeKey) -> Result<Vec<u8>> {
let encrypted_data = general_purpose::STANDARD.decode(&encrypted.data)
.map_err(|e| anyhow!("Failed to decode encrypted data: {}", e))?;
let nonce_bytes = general_purpose::STANDARD.decode(&encrypted.nonce)
.map_err(|e| anyhow!("Failed to decode nonce: {}", e))?;
if nonce_bytes.len() != NONCE_LEN {
return Err(anyhow!("Invalid nonce length"));
}
let mut nonce_array = [0u8; NONCE_LEN];
nonce_array.copy_from_slice(&nonce_bytes);
let nonce = Nonce::assume_unique_for_key(nonce_array);
let mut in_out = encrypted_data;
let plaintext = key.open_in_place(nonce, ring::aead::Aad::empty(), &mut in_out)
.map_err(|_| anyhow!("Failed to decrypt data"))?;
Ok(plaintext.to_vec())
}
pub async fn derive_key_from_password(&self, password: &str, salt: Option<&str>) -> Result<DerivedKey> {
let salt_bytes = if let Some(salt) = salt {
hex::decode(salt)?
} else {
let mut salt_bytes = [0u8; 16];
self.rng.fill(&mut salt_bytes).map_err(|e| anyhow::anyhow!("Failed to generate salt: {}", e))?;
salt_bytes.to_vec()
};
let mut key_bytes = [0u8; 32];
pbkdf2::derive(
pbkdf2::PBKDF2_HMAC_SHA256,
std::num::NonZeroU32::new(100_000).unwrap(),
&salt_bytes,
password.as_bytes(),
&mut key_bytes,
);
Ok(DerivedKey {
key: key_bytes.to_vec(),
salt: hex::encode(&salt_bytes),
})
}
pub fn hash(&self, data: &[u8]) -> String {
let mut context = Context::new(&SHA256);
context.update(data);
let digest = context.finish();
hex::encode(digest.as_ref())
}
pub fn generate_secure_id(&self, length: Option<usize>) -> Result<String> {
let len = length.unwrap_or(32);
let mut bytes = vec![0u8; len];
self.rng.fill(&mut bytes).map_err(|e| anyhow::anyhow!("Failed to generate secure ID: {}", e))?;
Ok(hex::encode(&bytes))
}
pub async fn encrypt_file(&self, file_path: &str) -> Result<String> {
self.ensure_initialized()?;
let file_data = fs::read(file_path).await?;
let file_value = serde_json::json!({
"data": general_purpose::STANDARD.encode(&file_data),
"filename": std::path::Path::new(file_path)
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("unknown"),
"size": file_data.len(),
"timestamp": SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
});
self.encrypt_for_sync(&file_value).await
}
pub async fn decrypt_file(&self, encrypted_data: &str, output_path: &str) -> Result<bool> {
self.ensure_initialized()?;
let decrypted_value = self.decrypt_from_sync(encrypted_data).await?;
if let Some(data_b64) = decrypted_value.get("data").and_then(|v| v.as_str()) {
let file_data = general_purpose::STANDARD.decode(data_b64)?;
fs::write(output_path, file_data).await?;
Ok(true)
} else {
Err(anyhow!("Invalid file data format"))
}
}
pub async fn shutdown(&self) -> Result<()> {
Ok(())
}
pub fn is_content_encryption_enabled(&self) -> bool {
self.config.enable_content_encryption
}
pub fn is_metadata_encryption_enabled(&self) -> bool {
self.config.enable_metadata_encryption
}
pub fn is_embedding_encryption_enabled(&self) -> bool {
self.config.enable_embedding_encryption
}
pub fn is_initialized(&self) -> bool {
*self.initialized.read().unwrap()
}
pub fn get_status(&self) -> HashMap<String, serde_json::Value> {
let mut status = HashMap::new();
status.insert("initialized".to_string(), serde_json::Value::Bool(self.is_initialized()));
status.insert("algorithm".to_string(), serde_json::Value::String("aes-256-gcm".to_string()));
status.insert("key_length".to_string(), serde_json::Value::Number(serde_json::Number::from(256)));
status.insert("keychain_service".to_string(), serde_json::Value::String(self.service_name.clone()));
status.insert("content_encryption".to_string(), serde_json::Value::Bool(self.config.enable_content_encryption));
status.insert("metadata_encryption".to_string(), serde_json::Value::Bool(self.config.enable_metadata_encryption));
status.insert("embedding_encryption".to_string(), serde_json::Value::Bool(self.config.enable_embedding_encryption));
status
}
fn ensure_initialized(&self) -> Result<()> {
if !*self.initialized.read().unwrap() {
return Err(anyhow!("Encryption service must be initialized before use"));
}
Ok(())
}
fn encrypt_with_key_sync(&self, data: &[u8], key: &LessSafeKey) -> Result<String> {
let mut nonce_bytes = [0u8; NONCE_LEN];
self.rng.fill(&mut nonce_bytes)
.map_err(|_| anyhow!("Failed to generate nonce"))?;
let nonce = Nonce::assume_unique_for_key(nonce_bytes);
let mut in_out = data.to_vec();
key.seal_in_place_append_tag(nonce, ring::aead::Aad::empty(), &mut in_out)
.map_err(|_| anyhow!("Failed to encrypt data"))?;
let mut combined = Vec::new();
combined.extend_from_slice(&nonce_bytes); combined.extend_from_slice(&in_out);
Ok(general_purpose::STANDARD.encode(&combined))
}
fn decrypt_with_key_base64_sync(&self, encrypted_base64: &str, key: &LessSafeKey) -> Result<Vec<u8>> {
let combined = general_purpose::STANDARD.decode(encrypted_base64)
.map_err(|e| anyhow!("Failed to decode base64 encrypted data: {}", e))?;
if combined.len() < NONCE_LEN + 16 { return Err(anyhow!("Invalid encrypted data format"));
}
let nonce_bytes = &combined[0..NONCE_LEN];
let mut encrypted_with_tag = combined[NONCE_LEN..].to_vec();
let mut nonce_array = [0u8; NONCE_LEN];
nonce_array.copy_from_slice(nonce_bytes);
let nonce = Nonce::assume_unique_for_key(nonce_array);
let plaintext = key.open_in_place(nonce, ring::aead::Aad::empty(), &mut encrypted_with_tag)
.map_err(|_| anyhow!("Failed to decrypt data"))?;
Ok(plaintext.to_vec())
}
fn decrypt_with_key_sync(&self, encrypted: &EncryptedData, key: &LessSafeKey) -> Result<Vec<u8>> {
let encrypted_data = general_purpose::STANDARD.decode(&encrypted.data)
.map_err(|e| anyhow!("Failed to decode encrypted data: {}", e))?;
let nonce_bytes = general_purpose::STANDARD.decode(&encrypted.nonce)
.map_err(|e| anyhow!("Failed to decode nonce: {}", e))?;
if nonce_bytes.len() != NONCE_LEN {
return Err(anyhow!("Invalid nonce length"));
}
let mut nonce_array = [0u8; NONCE_LEN];
nonce_array.copy_from_slice(&nonce_bytes);
let nonce = Nonce::assume_unique_for_key(nonce_array);
let mut in_out = encrypted_data;
let plaintext = key.open_in_place(nonce, ring::aead::Aad::empty(), &mut in_out)
.map_err(|_| anyhow!("Failed to decrypt data"))?;
Ok(plaintext.to_vec())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_content_encryption() {
let config = EncryptionConfig {
enable_content_encryption: true,
..Default::default()
};
let mut service = EncryptionService::new(&config, "./test").await.unwrap();
service.initialize().await.unwrap();
let original = "Hello, World!";
let encrypted = service.encrypt_content(original).await.unwrap();
assert_ne!(encrypted, original);
let decrypted = service.decrypt_content(&encrypted).await.unwrap();
assert_eq!(decrypted, original);
}
#[tokio::test]
async fn test_metadata_encryption() {
let config = EncryptionConfig {
enable_metadata_encryption: true,
..Default::default()
};
let mut service = EncryptionService::new(&config, "./test").await.unwrap();
service.initialize().await.unwrap();
let original = serde_json::json!({"key": "value", "number": 42});
let encrypted = service.encrypt_metadata(&original).await.unwrap();
assert!(encrypted.get("_encrypted_metadata").is_some());
let decrypted = service.decrypt_metadata(&encrypted).await.unwrap();
assert_eq!(decrypted, original);
}
#[tokio::test]
async fn test_embedding_encryption() {
let config = EncryptionConfig {
enable_embedding_encryption: true,
..Default::default()
};
let mut service = EncryptionService::new(&config, "./test").await.unwrap();
service.initialize().await.unwrap();
let original_embedding = vec![0.1, 0.2, 0.3, 0.4, 0.5];
let encrypted = service.encrypt_embedding(&original_embedding).await.unwrap();
assert!(!encrypted.is_empty());
let decrypted = service.decrypt_embedding(&encrypted).await.unwrap();
assert_eq!(decrypted, original_embedding);
}
#[tokio::test]
async fn test_sync_encryption() {
let config = EncryptionConfig {
enable_content_encryption: true,
..Default::default()
};
let mut service = EncryptionService::new(&config, "./test").await.unwrap();
service.initialize().await.unwrap();
let original = serde_json::json!({"test": "data", "number": 123});
let encrypted = service.encrypt_for_sync(&original).await.unwrap();
assert!(!encrypted.is_empty());
let decrypted = service.decrypt_from_sync(&encrypted).await.unwrap();
assert_eq!(decrypted, original);
}
#[tokio::test]
async fn test_key_derivation() {
let config = EncryptionConfig::default();
let service = EncryptionService::new(&config, "./test").await.unwrap();
let password = "test_password_123";
let result = service.derive_key_from_password(password, None).await.unwrap();
assert_eq!(result.key.len(), 32);
assert!(!result.salt.is_empty());
let result2 = service.derive_key_from_password(password, Some(&result.salt)).await.unwrap();
assert_eq!(result.key, result2.key);
}
#[tokio::test]
async fn test_hashing() {
let config = EncryptionConfig::default();
let service = EncryptionService::new(&config, "./test").await.unwrap();
let data = b"Hello, World!";
let hash1 = service.hash(data);
let hash2 = service.hash(data);
assert_eq!(hash1, hash2);
assert_eq!(hash1.len(), 64); }
#[tokio::test]
async fn test_secure_id_generation() {
let config = EncryptionConfig::default();
let service = EncryptionService::new(&config, "./test").await.unwrap();
let id1 = service.generate_secure_id(None).unwrap();
let id2 = service.generate_secure_id(Some(16)).unwrap();
assert_eq!(id1.len(), 64); assert_eq!(id2.len(), 32); assert_ne!(id1, id2);
}
#[tokio::test]
async fn test_disabled_encryption() {
let config = EncryptionConfig {
enable_content_encryption: false,
enable_metadata_encryption: false,
enable_embedding_encryption: false,
..Default::default()
};
let service = EncryptionService::new(&config, "./test").await.unwrap();
let content = "Hello, World!";
let encrypted_content = service.encrypt_content(content).await.unwrap();
assert_eq!(encrypted_content, content);
let metadata = serde_json::json!({"key": "value"});
let encrypted_metadata = service.encrypt_metadata(&metadata).await.unwrap();
assert_eq!(encrypted_metadata, metadata);
}
#[tokio::test]
async fn test_service_status() {
let config = EncryptionConfig {
enable_content_encryption: true,
enable_metadata_encryption: true,
..Default::default()
};
let mut service = EncryptionService::new(&config, "./test").await.unwrap();
let status = service.get_status();
assert_eq!(status.get("initialized"), Some(&serde_json::Value::Bool(false)));
service.initialize().await.unwrap();
let status = service.get_status();
assert_eq!(status.get("initialized"), Some(&serde_json::Value::Bool(true)));
assert_eq!(status.get("algorithm"), Some(&serde_json::Value::String("aes-256-gcm".to_string())));
}
}