use crate::module::traits::ModuleError;
use aes_gcm::{
aead::{Aead, AeadCore, KeyInit, OsRng},
Aes256Gcm, Nonce,
};
use hkdf::Hkdf;
use sha2::{Digest, Sha256};
use std::path::{Path, PathBuf};
pub struct ModuleEncryption;
impl ModuleEncryption {
pub fn new() -> Self {
Self
}
pub fn encrypt_module(
&self,
module_binary: &[u8],
payment_id: &str,
module_hash: &[u8; 32],
) -> Result<(Vec<u8>, Vec<u8>), ModuleError> {
let key = Self::derive_key(payment_id, module_hash);
let cipher = Aes256Gcm::new(&key.into());
let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
let encrypted = cipher
.encrypt(&nonce, module_binary)
.map_err(|e| ModuleError::CryptoError(format!("Encryption failed: {e}")))?;
Ok((encrypted, nonce.to_vec()))
}
pub fn decrypt_module(
&self,
encrypted_data: &[u8],
nonce: &[u8],
payment_id: &str,
module_hash: &[u8; 32],
) -> Result<Vec<u8>, ModuleError> {
let key = Self::derive_key(payment_id, module_hash);
let cipher = Aes256Gcm::new(&key.into());
let nonce = Nonce::from_slice(nonce);
let decrypted = cipher
.decrypt(nonce, encrypted_data)
.map_err(|e| ModuleError::CryptoError(format!("Decryption failed: {e}")))?;
Ok(decrypted)
}
fn derive_key(payment_id: &str, module_hash: &[u8; 32]) -> [u8; 32] {
let mut input = Vec::with_capacity(payment_id.len() + 32);
input.extend_from_slice(payment_id.as_bytes());
input.extend_from_slice(module_hash);
let hk = Hkdf::<Sha256>::new(None, &input);
let mut key = [0u8; 32];
hk.expand(b"module_encryption_key", &mut key)
.expect("HKDF expansion failed");
key
}
}
impl Default for ModuleEncryption {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EncryptedModuleMetadata {
pub payment_id: String,
#[serde(with = "serde_bytes")]
pub module_hash: Vec<u8>,
#[serde(with = "serde_bytes")]
pub nonce: Vec<u8>,
pub encrypted_at: u64,
pub payment_method: String, }
pub async fn store_encrypted_module(
modules_dir: &Path,
module_name: &str,
encrypted_binary: &[u8],
metadata: &EncryptedModuleMetadata,
) -> Result<PathBuf, ModuleError> {
use std::fs;
use tokio::io::AsyncWriteExt;
let encrypted_dir = modules_dir.join("encrypted");
fs::create_dir_all(&encrypted_dir)
.map_err(|e| ModuleError::op_err("Failed to create encrypted modules directory", e))?;
let module_dir = encrypted_dir.join(module_name);
fs::create_dir_all(&module_dir)
.map_err(|e| ModuleError::op_err("Failed to create module directory", e))?;
let binary_path = module_dir.join("encrypted_binary");
let mut file = tokio::fs::File::create(&binary_path)
.await
.map_err(|e| ModuleError::op_err("Failed to create encrypted binary file", e))?;
file.write_all(encrypted_binary)
.await
.map_err(|e| ModuleError::op_err("Failed to write encrypted binary", e))?;
file.flush()
.await
.map_err(|e| ModuleError::op_err("Failed to flush encrypted binary", e))?;
drop(file);
let metadata_path = module_dir.join("metadata.json");
let metadata_json = serde_json::to_string_pretty(metadata)
.map_err(|e| ModuleError::op_err("Failed to serialize metadata", e))?;
fs::write(&metadata_path, metadata_json)
.map_err(|e| ModuleError::op_err("Failed to write metadata", e))?;
Ok(binary_path)
}
pub async fn load_encrypted_module(
modules_dir: &Path,
module_name: &str,
) -> Result<(Vec<u8>, EncryptedModuleMetadata), ModuleError> {
use std::fs;
let module_dir = modules_dir.join("encrypted").join(module_name);
let metadata_path = module_dir.join("metadata.json");
let metadata_json = fs::read_to_string(&metadata_path)
.map_err(|e| ModuleError::op_err("Failed to read metadata", e))?;
let metadata: EncryptedModuleMetadata = serde_json::from_str(&metadata_json)
.map_err(|e| ModuleError::op_err("Failed to parse metadata", e))?;
let binary_path = module_dir.join("encrypted_binary");
let encrypted_binary = fs::read(&binary_path)
.map_err(|e| ModuleError::op_err("Failed to read encrypted binary", e))?;
Ok((encrypted_binary, metadata))
}
pub async fn store_decrypted_module(
modules_dir: &Path,
module_name: &str,
decrypted_binary: &[u8],
manifest: &[u8],
) -> Result<PathBuf, ModuleError> {
use std::fs;
use tokio::io::AsyncWriteExt;
let decrypted_dir = modules_dir.join("decrypted");
fs::create_dir_all(&decrypted_dir)
.map_err(|e| ModuleError::op_err("Failed to create decrypted modules directory", e))?;
let module_dir = decrypted_dir.join(module_name);
fs::create_dir_all(&module_dir)
.map_err(|e| ModuleError::op_err("Failed to create module directory", e))?;
let manifest_path = module_dir.join("module.toml");
fs::write(&manifest_path, manifest)
.map_err(|e| ModuleError::op_err("Failed to write manifest", e))?;
let binary_path = module_dir.join(module_name);
let mut file = tokio::fs::File::create(&binary_path)
.await
.map_err(|e| ModuleError::op_err("Failed to create decrypted binary file", e))?;
file.write_all(decrypted_binary)
.await
.map_err(|e| ModuleError::op_err("Failed to write decrypted binary", e))?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = file
.metadata()
.await
.map_err(|e| ModuleError::op_err("Failed to get file metadata", e))?
.permissions();
perms.set_mode(0o755);
tokio::fs::set_permissions(&binary_path, perms)
.await
.map_err(|e| ModuleError::op_err("Failed to set file permissions", e))?;
}
Ok(binary_path)
}