use super::{KeyPair, SeaError};
use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm, Nonce,
};
use base64::{engine::general_purpose, Engine as _};
use rand::RngCore;
use serde_json::Value;
pub async fn encrypt(
data: &Value,
pair: &KeyPair,
their_epub: Option<&str>,
) -> Result<Value, SeaError> {
let msg = serde_json::to_string(data)
.map_err(|e| SeaError::Encryption(format!("Serialization error: {}", e)))?;
let mut salt_bytes = [0u8; 9];
let mut iv_bytes = [0u8; 12]; rand::thread_rng().fill_bytes(&mut salt_bytes);
rand::thread_rng().fill_bytes(&mut iv_bytes);
let aes_key = if let Some(their_pub) = their_epub {
let our_epriv = pair
.epriv_key
.as_ref()
.ok_or_else(|| SeaError::Encryption("Missing epriv key".to_string()))?;
let our_epub = pair
.epub_key
.as_ref()
.ok_or_else(|| SeaError::Encryption("Missing epub key".to_string()))?;
let shared_secret =
crate::sea::secret::derive_secret(their_pub, our_epriv, our_epub).await?;
derive_aes_key(&shared_secret, &salt_bytes).await?
} else {
let epriv = pair
.epriv_key
.as_ref()
.ok_or_else(|| SeaError::Encryption("Missing epriv key".to_string()))?;
derive_aes_key(epriv, &salt_bytes).await?
};
let cipher = Aes256Gcm::new_from_slice(&aes_key)
.map_err(|e| SeaError::Encryption(format!("Failed to create cipher: {}", e)))?;
#[allow(deprecated)] let nonce = Nonce::from_slice(&iv_bytes);
let ciphertext = cipher
.encrypt(nonce, msg.as_bytes())
.map_err(|e| SeaError::Encryption(format!("Encryption failed: {}", e)))?;
let ct_b64 = general_purpose::STANDARD_NO_PAD.encode(ciphertext);
let iv_b64 = general_purpose::STANDARD_NO_PAD.encode(iv_bytes);
let s_b64 = general_purpose::STANDARD_NO_PAD.encode(salt_bytes);
Ok(serde_json::json!({
"ct": ct_b64,
"iv": iv_b64,
"s": s_b64
}))
}
pub(crate) async fn derive_aes_key(secret: &str, salt: &[u8]) -> Result<Vec<u8>, SeaError> {
use pbkdf2::pbkdf2_hmac;
use sha2::Sha256;
let secret_bytes = general_purpose::STANDARD_NO_PAD
.decode(secret)
.unwrap_or_else(|_| secret.as_bytes().to_vec());
let mut key = vec![0u8; 32]; pbkdf2_hmac::<Sha256>(&secret_bytes, salt, 100000, &mut key);
Ok(key)
}