use md5::{Md5, Digest};
use crate::{
aes::AesDecryptor,
utils::base64,
Mode,
Result,
CryptoError,
CryptoOperation,
};
pub fn derive_key_and_iv(password: &[u8], salt: &[u8]) -> ([u8; 32], [u8; 16]) {
let mut key = [0u8; 32];
let mut iv = [0u8; 16];
let mut hasher = Md5::new();
hasher.update(password);
hasher.update(salt);
let digest = hasher.finalize();
key[..16].copy_from_slice(&digest);
let mut hasher = Md5::new();
hasher.update(&digest);
hasher.update(password);
hasher.update(salt);
let digest = hasher.finalize();
key[16..].copy_from_slice(&digest);
let mut hasher = Md5::new();
hasher.update(&digest);
hasher.update(password);
hasher.update(salt);
let digest = hasher.finalize();
iv.copy_from_slice(&digest);
(key, iv)
}
pub fn parse_cryptojs_data(data: &str) -> Result<(Vec<u8>, Vec<u8>)> {
let encrypted_data = base64::decode(data)
.map_err(|_| CryptoError::InvalidData("Failed to decode base64".into()))?;
if encrypted_data.len() < 16 || &encrypted_data[0..8] != b"Salted__" {
return Err(CryptoError::InvalidData("Invalid CryptoJS format".into()));
}
let salt = encrypted_data[8..16].to_vec();
let ciphertext = encrypted_data[16..].to_vec();
Ok((salt, ciphertext))
}
pub fn decrypt(encrypted_data: &str, password: &[u8]) -> Result<String> {
let (salt, ciphertext) = parse_cryptojs_data(encrypted_data)?;
let (key, iv) = derive_key_and_iv(password, &salt);
let mut decryptor = AesDecryptor::new_256(&key, Mode::CBC, Some(&iv))?;
decryptor.update(&ciphertext)?;
let decrypted = decryptor.finalize()?;
let last_byte = decrypted.last()
.ok_or_else(|| CryptoError::InvalidData("Empty decrypted data".into()))?;
let padding_len = *last_byte as usize;
let decrypted = if padding_len > 0 && padding_len <= 16 {
&decrypted[..decrypted.len() - padding_len]
} else {
&decrypted
};
String::from_utf8(decrypted.to_vec())
.map_err(|_| CryptoError::InvalidData("Invalid UTF-8".into()))
}