use aes::Aes128;
use cbc::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use cbc::{Decryptor, Encryptor};
#[cfg(feature = "legacy-crypto")]
use des::{Des, TdesEde3};
#[cfg(feature = "legacy-crypto")]
use md5::{Digest as Md5Digest, Md5};
use pbkdf2::pbkdf2_hmac;
#[cfg(feature = "legacy-crypto")]
use sha1::{Digest as Sha1Digest, Sha1};
use sha2::{Digest as Sha2Digest, Sha256, Sha384, Sha512};
type Aes128CbcEnc = Encryptor<Aes128>;
type Aes128CbcDec = Decryptor<Aes128>;
type Aes192CbcEnc = Encryptor<aes::Aes192>;
type Aes192CbcDec = Decryptor<aes::Aes192>;
type Aes256CbcEnc = Encryptor<aes::Aes256>;
type Aes256CbcDec = Decryptor<aes::Aes256>;
#[cfg(feature = "legacy-crypto")]
type DesCbcEnc = Encryptor<Des>;
#[cfg(feature = "legacy-crypto")]
type DesCbcDec = Decryptor<Des>;
#[cfg(feature = "legacy-crypto")]
type TdesCbcEnc = Encryptor<TdesEde3>;
#[cfg(feature = "legacy-crypto")]
type TdesCbcDec = Decryptor<TdesEde3>;
#[cfg(feature = "legacy-crypto")]
#[must_use]
pub fn compute_md5(data: &[u8]) -> Vec<u8> {
let mut hasher = Md5::new();
Md5Digest::update(&mut hasher, data);
hasher.finalize().to_vec()
}
#[cfg(feature = "legacy-crypto")]
#[must_use]
pub fn compute_sha1(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha1::new();
Sha1Digest::update(&mut hasher, data);
hasher.finalize().to_vec()
}
#[must_use]
pub fn compute_sha256(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha256::new();
Sha2Digest::update(&mut hasher, data);
hasher.finalize().to_vec()
}
#[must_use]
pub fn compute_sha384(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha384::new();
Sha2Digest::update(&mut hasher, data);
hasher.finalize().to_vec()
}
#[must_use]
pub fn compute_sha512(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha512::new();
Sha2Digest::update(&mut hasher, data);
hasher.finalize().to_vec()
}
pub fn derive_pbkdf2_key(
password: &[u8],
salt: &[u8],
iterations: u32,
key_len: usize,
hash_algorithm: &str,
) -> Vec<u8> {
let mut key = vec![0u8; key_len];
match hash_algorithm.to_uppercase().as_str() {
"SHA256" => {
pbkdf2_hmac::<sha2::Sha256>(password, salt, iterations, &mut key);
}
"SHA384" => {
pbkdf2_hmac::<sha2::Sha384>(password, salt, iterations, &mut key);
}
"SHA512" => {
pbkdf2_hmac::<sha2::Sha512>(password, salt, iterations, &mut key);
}
#[cfg(feature = "legacy-crypto")]
_ => {
pbkdf2_hmac::<sha1::Sha1>(password, salt, iterations, &mut key);
}
#[cfg(not(feature = "legacy-crypto"))]
_ => {
pbkdf2_hmac::<sha2::Sha256>(password, salt, iterations, &mut key);
}
}
key
}
#[cfg(feature = "legacy-crypto")]
pub fn derive_pbkdf1_key(password: &[u8], salt: &[u8], iterations: u32, key_len: usize) -> Vec<u8> {
let mut hasher = sha1::Sha1::new();
hasher.update(password);
hasher.update(salt);
let mut t = hasher.finalize();
for _ in 1..iterations {
let mut hasher = sha1::Sha1::new();
hasher.update(t);
t = hasher.finalize();
}
let base_key: [u8; 20] = t.into();
if key_len <= 20 {
base_key[..key_len].to_vec()
} else {
let mut result = base_key.to_vec();
let mut counter = 1u32;
while result.len() < key_len {
let mut hasher = sha1::Sha1::new();
hasher.update(counter.to_string().as_bytes());
hasher.update(base_key);
let block = hasher.finalize();
result.extend_from_slice(&block);
counter += 1;
}
result.truncate(key_len);
result
}
}
pub fn apply_crypto_transform(
algorithm: &str,
key: &[u8],
iv: &[u8],
is_encryptor: bool,
data: &[u8],
) -> Option<Vec<u8>> {
let alg_upper = algorithm.to_uppercase();
if alg_upper.contains("AES") || alg_upper.contains("RIJNDAEL") {
return match key.len() {
16 => {
if is_encryptor {
aes_encrypt::<Aes128CbcEnc>(key, iv, data)
} else {
aes_decrypt::<Aes128CbcDec>(key, iv, data)
}
}
24 => {
if is_encryptor {
aes_encrypt::<Aes192CbcEnc>(key, iv, data)
} else {
aes_decrypt::<Aes192CbcDec>(key, iv, data)
}
}
32 => {
if is_encryptor {
aes_encrypt::<Aes256CbcEnc>(key, iv, data)
} else {
aes_decrypt::<Aes256CbcDec>(key, iv, data)
}
}
_ => None, };
}
#[cfg(feature = "legacy-crypto")]
if alg_upper.contains("TRIPLEDES") || alg_upper.contains("3DES") {
if key.len() == 24 && iv.len() >= 8 {
return if is_encryptor {
tdes_encrypt(key, iv, data)
} else {
tdes_decrypt(key, iv, data)
};
}
return None;
}
#[cfg(feature = "legacy-crypto")]
if alg_upper.contains("DES") {
if key.len() == 8 && iv.len() >= 8 {
return if is_encryptor {
des_encrypt(key, iv, data)
} else {
des_decrypt(key, iv, data)
};
}
return None;
}
#[cfg(not(feature = "legacy-crypto"))]
if alg_upper.contains("DES") {
return None;
}
None
}
fn aes_encrypt<E: BlockEncryptMut + KeyIvInit>(
key: &[u8],
iv: &[u8],
data: &[u8],
) -> Option<Vec<u8>> {
if iv.len() < 16 {
return None;
}
let cipher = E::new_from_slices(key, &iv[..16]).ok()?;
let block_size = 16;
let padded_len = ((data.len() / block_size) + 1) * block_size;
let mut buf = vec![0u8; padded_len];
buf[..data.len()].copy_from_slice(data);
let result = cipher
.encrypt_padded_mut::<Pkcs7>(&mut buf, data.len())
.ok()?;
Some(result.to_vec())
}
fn aes_decrypt<D: BlockDecryptMut + KeyIvInit>(
key: &[u8],
iv: &[u8],
data: &[u8],
) -> Option<Vec<u8>> {
if iv.len() < 16 || data.is_empty() {
return None;
}
let cipher = D::new_from_slices(key, &iv[..16]).ok()?;
let mut buf = data.to_vec();
let result = cipher.decrypt_padded_mut::<Pkcs7>(&mut buf).ok()?;
Some(result.to_vec())
}
#[cfg(feature = "legacy-crypto")]
fn des_encrypt(key: &[u8], iv: &[u8], data: &[u8]) -> Option<Vec<u8>> {
let cipher = DesCbcEnc::new_from_slices(key, &iv[..8]).ok()?;
let block_size = 8;
let padded_len = ((data.len() / block_size) + 1) * block_size;
let mut buf = vec![0u8; padded_len];
buf[..data.len()].copy_from_slice(data);
let result = cipher
.encrypt_padded_mut::<Pkcs7>(&mut buf, data.len())
.ok()?;
Some(result.to_vec())
}
#[cfg(feature = "legacy-crypto")]
fn des_decrypt(key: &[u8], iv: &[u8], data: &[u8]) -> Option<Vec<u8>> {
if data.is_empty() {
return None;
}
let cipher = DesCbcDec::new_from_slices(key, &iv[..8]).ok()?;
let mut buf = data.to_vec();
let result = cipher.decrypt_padded_mut::<Pkcs7>(&mut buf).ok()?;
Some(result.to_vec())
}
#[cfg(feature = "legacy-crypto")]
fn tdes_encrypt(key: &[u8], iv: &[u8], data: &[u8]) -> Option<Vec<u8>> {
let cipher = TdesCbcEnc::new_from_slices(key, &iv[..8]).ok()?;
let block_size = 8;
let padded_len = ((data.len() / block_size) + 1) * block_size;
let mut buf = vec![0u8; padded_len];
buf[..data.len()].copy_from_slice(data);
let result = cipher
.encrypt_padded_mut::<Pkcs7>(&mut buf, data.len())
.ok()?;
Some(result.to_vec())
}
#[cfg(feature = "legacy-crypto")]
fn tdes_decrypt(key: &[u8], iv: &[u8], data: &[u8]) -> Option<Vec<u8>> {
if data.is_empty() {
return None;
}
let cipher = TdesCbcDec::new_from_slices(key, &iv[..8]).ok()?;
let mut buf = data.to_vec();
let result = cipher.decrypt_padded_mut::<Pkcs7>(&mut buf).ok()?;
Some(result.to_vec())
}
#[cfg(test)]
mod tests {
use crate::utils::crypto::derive_pbkdf2_key;
#[test]
fn test_pbkdf2_sha256_basic() {
let password = b"password";
let salt = b"salt";
let key = derive_pbkdf2_key(password, salt, 1, 32, "SHA256");
assert_eq!(key.len(), 32);
let key2 = derive_pbkdf2_key(password, salt, 1, 32, "SHA256");
assert_eq!(key, key2);
}
#[test]
fn test_pbkdf2_sha384() {
let password = b"test";
let salt = b"salt123";
let key = derive_pbkdf2_key(password, salt, 1000, 48, "SHA384");
assert_eq!(key.len(), 48);
}
#[test]
fn test_pbkdf2_sha512() {
let password = b"test";
let salt = b"salt123";
let key = derive_pbkdf2_key(password, salt, 1000, 64, "SHA512");
assert_eq!(key.len(), 64);
}
#[test]
fn test_pbkdf2_iterations_affect_output() {
let password = b"password";
let salt = b"salt";
let key_1 = derive_pbkdf2_key(password, salt, 1, 20, "SHA256");
let key_1000 = derive_pbkdf2_key(password, salt, 1000, 20, "SHA256");
assert_ne!(key_1, key_1000);
}
#[test]
fn test_pbkdf2_salt_affects_output() {
let password = b"password";
let salt1 = b"salt1";
let salt2 = b"salt2";
let key1 = derive_pbkdf2_key(password, salt1, 1000, 20, "SHA256");
let key2 = derive_pbkdf2_key(password, salt2, 1000, 20, "SHA256");
assert_ne!(key1, key2);
}
#[test]
fn test_pbkdf2_case_insensitive_algorithm() {
let password = b"test";
let salt = b"salt";
let key_upper = derive_pbkdf2_key(password, salt, 1, 32, "SHA256");
let key_lower = derive_pbkdf2_key(password, salt, 1, 32, "sha256");
let key_mixed = derive_pbkdf2_key(password, salt, 1, 32, "Sha256");
assert_eq!(key_upper, key_lower);
assert_eq!(key_upper, key_mixed);
}
#[test]
fn test_pbkdf2_empty_password() {
let password = b"";
let salt = b"salt";
let key = derive_pbkdf2_key(password, salt, 1000, 16, "SHA256");
assert_eq!(key.len(), 16);
assert!(key.iter().any(|&b| b != 0));
}
#[test]
fn test_pbkdf2_empty_salt() {
let password = b"password";
let salt = b"";
let key = derive_pbkdf2_key(password, salt, 1000, 16, "SHA256");
assert_eq!(key.len(), 16);
}
#[test]
fn test_pbkdf2_variable_key_length() {
let password = b"password";
let salt = b"salt";
for len in [1, 16, 20, 32, 48, 64, 100] {
let key = derive_pbkdf2_key(password, salt, 1, len, "SHA256");
assert_eq!(key.len(), len);
}
}
use super::apply_crypto_transform;
#[test]
fn test_aes_128_encrypt_decrypt() {
let key = [0u8; 16]; let iv = [0u8; 16]; let plaintext = b"Hello, World!";
let ciphertext = apply_crypto_transform("AES", &key, &iv, true, plaintext);
assert!(ciphertext.is_some(), "Encryption should succeed");
let ciphertext = ciphertext.unwrap();
assert_ne!(
ciphertext.as_slice(),
plaintext,
"Ciphertext should differ from plaintext"
);
let decrypted = apply_crypto_transform("AES", &key, &iv, false, &ciphertext);
assert!(decrypted.is_some(), "Decryption should succeed");
assert_eq!(
decrypted.unwrap(),
plaintext,
"Decrypted should match original"
);
}
#[test]
fn test_aes_192_encrypt_decrypt() {
let key = [0u8; 24]; let iv = [0u8; 16];
let plaintext = b"Test message for AES-192";
let ciphertext = apply_crypto_transform("AES", &key, &iv, true, plaintext).unwrap();
let decrypted = apply_crypto_transform("AES", &key, &iv, false, &ciphertext).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_aes_256_encrypt_decrypt() {
let key = [0u8; 32]; let iv = [0u8; 16];
let plaintext = b"Test message for AES-256";
let ciphertext = apply_crypto_transform("AES", &key, &iv, true, plaintext).unwrap();
let decrypted = apply_crypto_transform("AES", &key, &iv, false, &ciphertext).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_rijndael_alias() {
let key = [0u8; 16];
let iv = [0u8; 16];
let plaintext = b"Test for Rijndael";
let ciphertext = apply_crypto_transform("Rijndael", &key, &iv, true, plaintext).unwrap();
let decrypted = apply_crypto_transform("Rijndael", &key, &iv, false, &ciphertext).unwrap();
assert_eq!(decrypted, plaintext);
let aes_ciphertext = apply_crypto_transform("AES", &key, &iv, true, plaintext).unwrap();
assert_eq!(ciphertext, aes_ciphertext);
}
#[test]
fn test_unsupported_key_size() {
let bad_key = [0u8; 15]; let iv = [0u8; 16];
let plaintext = b"test";
let result = apply_crypto_transform("AES", &bad_key, &iv, true, plaintext);
assert!(result.is_none(), "Should fail with invalid key size");
}
#[test]
fn test_unsupported_algorithm() {
let key = [0u8; 16];
let iv = [0u8; 16];
let plaintext = b"test";
let result = apply_crypto_transform("UNKNOWN_ALGO", &key, &iv, true, plaintext);
assert!(result.is_none(), "Should return None for unknown algorithm");
}
#[test]
fn test_case_insensitive_algorithm() {
let key = [0u8; 16];
let iv = [0u8; 16];
let plaintext = b"case test";
let ct1 = apply_crypto_transform("AES", &key, &iv, true, plaintext).unwrap();
let ct2 = apply_crypto_transform("aes", &key, &iv, true, plaintext).unwrap();
let ct3 = apply_crypto_transform("Aes", &key, &iv, true, plaintext).unwrap();
assert_eq!(ct1, ct2);
assert_eq!(ct1, ct3);
}
#[test]
fn test_different_keys_produce_different_ciphertext() {
let key1 = [0u8; 16];
let key2 = [1u8; 16];
let iv = [0u8; 16];
let plaintext = b"test";
let ct1 = apply_crypto_transform("AES", &key1, &iv, true, plaintext).unwrap();
let ct2 = apply_crypto_transform("AES", &key2, &iv, true, plaintext).unwrap();
assert_ne!(ct1, ct2);
}
#[test]
fn test_different_ivs_produce_different_ciphertext() {
let key = [0u8; 16];
let iv1 = [0u8; 16];
let iv2 = [1u8; 16];
let plaintext = b"test";
let ct1 = apply_crypto_transform("AES", &key, &iv1, true, plaintext).unwrap();
let ct2 = apply_crypto_transform("AES", &key, &iv2, true, plaintext).unwrap();
assert_ne!(ct1, ct2);
}
#[test]
fn test_empty_plaintext() {
let key = [0u8; 16];
let iv = [0u8; 16];
let plaintext = b"";
let ciphertext = apply_crypto_transform("AES", &key, &iv, true, plaintext).unwrap();
assert_eq!(ciphertext.len(), 16);
let decrypted = apply_crypto_transform("AES", &key, &iv, false, &ciphertext).unwrap();
assert!(decrypted.is_empty());
}
#[test]
fn test_block_aligned_plaintext() {
let key = [0u8; 16];
let iv = [0u8; 16];
let plaintext = [0u8; 32];
let ciphertext = apply_crypto_transform("AES", &key, &iv, true, &plaintext).unwrap();
assert_eq!(ciphertext.len(), 48);
let decrypted = apply_crypto_transform("AES", &key, &iv, false, &ciphertext).unwrap();
assert_eq!(decrypted, plaintext);
}
}
#[cfg(test)]
#[cfg(feature = "legacy-crypto")]
mod legacy_tests {
use crate::utils::crypto::{derive_pbkdf1_key, derive_pbkdf2_key};
use sha1::Digest;
#[test]
fn test_pbkdf2_sha1_basic() {
let password = b"password";
let salt = b"salt";
let key = derive_pbkdf2_key(password, salt, 1, 20, "SHA1");
assert_eq!(key.len(), 20);
let key2 = derive_pbkdf2_key(password, salt, 1, 20, "SHA1");
assert_eq!(key, key2);
}
#[test]
fn test_pbkdf2_sha256_vs_sha1() {
let password = b"password";
let salt = b"salt";
let key_sha256 = derive_pbkdf2_key(password, salt, 1, 32, "SHA256");
let key_sha1 = derive_pbkdf2_key(password, salt, 1, 32, "SHA1");
assert_ne!(key_sha256, key_sha1);
}
#[test]
fn test_pbkdf2_unknown_algorithm_defaults_to_sha1() {
let password = b"test";
let salt = b"salt";
let key_unknown = derive_pbkdf2_key(password, salt, 1000, 20, "UNKNOWN");
let key_sha1 = derive_pbkdf2_key(password, salt, 1000, 20, "SHA1");
assert_eq!(key_unknown, key_sha1);
}
#[test]
fn test_pbkdf1_basic() {
let password = b"password";
let salt = b"salt";
let key = derive_pbkdf1_key(password, salt, 100, 16);
assert_eq!(key.len(), 16);
let key2 = derive_pbkdf1_key(password, salt, 100, 16);
assert_eq!(key, key2);
}
#[test]
fn test_pbkdf1_max_natural_length() {
let password = b"password";
let salt = b"salt";
let key = derive_pbkdf1_key(password, salt, 100, 20);
assert_eq!(key.len(), 20);
}
#[test]
fn test_pbkdf1_extended_length() {
let password = b"password";
let salt = b"salt";
let key = derive_pbkdf1_key(password, salt, 100, 32);
assert_eq!(key.len(), 32);
}
#[test]
fn test_pbkdf1_very_long_key() {
let password = b"password";
let salt = b"salt";
let key = derive_pbkdf1_key(password, salt, 100, 100);
assert_eq!(key.len(), 100);
}
#[test]
fn test_pbkdf1_iterations_affect_output() {
let password = b"password";
let salt = b"salt";
let key_1 = derive_pbkdf1_key(password, salt, 1, 20);
let key_100 = derive_pbkdf1_key(password, salt, 100, 20);
assert_ne!(key_1, key_100);
}
#[test]
fn test_pbkdf1_salt_affects_output() {
let password = b"password";
let salt1 = b"salt1";
let salt2 = b"salt2";
let key1 = derive_pbkdf1_key(password, salt1, 100, 20);
let key2 = derive_pbkdf1_key(password, salt2, 100, 20);
assert_ne!(key1, key2);
}
#[test]
fn test_pbkdf1_password_affects_output() {
let password1 = b"password1";
let password2 = b"password2";
let salt = b"salt";
let key1 = derive_pbkdf1_key(password1, salt, 100, 20);
let key2 = derive_pbkdf1_key(password2, salt, 100, 20);
assert_ne!(key1, key2);
}
#[test]
fn test_pbkdf1_empty_password() {
let password = b"";
let salt = b"salt";
let key = derive_pbkdf1_key(password, salt, 100, 16);
assert_eq!(key.len(), 16);
}
#[test]
fn test_pbkdf1_empty_salt() {
let password = b"password";
let salt = b"";
let key = derive_pbkdf1_key(password, salt, 100, 16);
assert_eq!(key.len(), 16);
}
#[test]
fn test_pbkdf1_single_iteration() {
let password = b"test";
let salt = b"salt";
let key = derive_pbkdf1_key(password, salt, 1, 20);
let mut hasher = sha1::Sha1::new();
hasher.update(password);
hasher.update(salt);
let expected: [u8; 20] = hasher.finalize().into();
assert_eq!(key, expected);
}
#[test]
fn test_pbkdf1_variable_key_length() {
let password = b"password";
let salt = b"salt";
for len in [1, 8, 16, 20, 32, 48, 64] {
let key = derive_pbkdf1_key(password, salt, 100, len);
assert_eq!(key.len(), len);
}
}
#[test]
fn test_pbkdf1_different_from_pbkdf2() {
let password = b"password";
let salt = b"salt";
let key1 = derive_pbkdf1_key(password, salt, 100, 20);
let key2 = derive_pbkdf2_key(password, salt, 100, 20, "SHA1");
assert_ne!(key1, key2);
}
use super::apply_crypto_transform;
#[test]
fn test_des_encrypt_decrypt() {
let key = [0u8; 8]; let iv = [0u8; 8]; let plaintext = b"DES test";
let ciphertext = apply_crypto_transform("DES", &key, &iv, true, plaintext).unwrap();
let decrypted = apply_crypto_transform("DES", &key, &iv, false, &ciphertext).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_tripledes_encrypt_decrypt() {
let key = [0u8; 24]; let iv = [0u8; 8]; let plaintext = b"TripleDES test message";
let ciphertext = apply_crypto_transform("TripleDES", &key, &iv, true, plaintext).unwrap();
let decrypted = apply_crypto_transform("TripleDES", &key, &iv, false, &ciphertext).unwrap();
assert_eq!(decrypted, plaintext);
let ciphertext_3des = apply_crypto_transform("3DES", &key, &iv, true, plaintext).unwrap();
assert_eq!(ciphertext, ciphertext_3des);
}
}