use aes::cipher::block_padding::Pkcs7;
use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyInit, KeyIvInit};
type Aes128EcbEnc = ecb::Encryptor<aes::Aes128>;
type Aes128EcbDec = ecb::Decryptor<aes::Aes128>;
#[allow(dead_code)]
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
pub fn aes128_ecb_encrypt(key: &[u8; 16], plaintext: &[u8]) -> Vec<u8> {
Aes128EcbEnc::new(key.into()).encrypt_padded_vec_mut::<Pkcs7>(plaintext)
}
pub fn aes128_ecb_decrypt(key: &[u8; 16], ciphertext: &[u8]) -> Result<Vec<u8>, &'static str> {
Aes128EcbDec::new(key.into())
.decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
.map_err(|_| "AES-128-ECB decryption failed")
}
pub fn aes256_cbc_decrypt(key: &[u8; 32], ciphertext: &[u8]) -> Result<Vec<u8>, &'static str> {
let iv: &[u8; 16] = key[..16].try_into().map_err(|_| "IV extraction failed")?;
Aes256CbcDec::new(key.into(), iv.into())
.decrypt_padded_vec_mut::<Pkcs7>(ciphertext)
.map_err(|_| "AES-256-CBC decryption failed")
}
pub fn parse_aes128_key(input: &[u8]) -> Result<[u8; 16], &'static str> {
if input.len() == 16 {
let mut key = [0u8; 16];
key.copy_from_slice(input);
Ok(key)
} else if input.len() == 32 {
let hex_str = std::str::from_utf8(input).map_err(|_| "invalid hex key")?;
let bytes = hex_decode(hex_str)?;
if bytes.len() != 16 {
return Err("hex key does not decode to 16 bytes");
}
let mut key = [0u8; 16];
key.copy_from_slice(&bytes);
Ok(key)
} else {
Err("AES key must be 16 bytes or 32 hex chars")
}
}
fn hex_decode(s: &str) -> Result<Vec<u8>, &'static str> {
if !s.len().is_multiple_of(2) {
return Err("odd-length hex string");
}
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|_| "invalid hex char"))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn aes128_ecb_roundtrip() {
let key = [0x42u8; 16];
let plaintext = b"Hello, WeChat CDN!";
let ct = aes128_ecb_encrypt(&key, plaintext);
let pt = aes128_ecb_decrypt(&key, &ct).unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn aes128_ecb_roundtrip_empty() {
let key = [0xAAu8; 16];
let ct = aes128_ecb_encrypt(&key, b"");
let pt = aes128_ecb_decrypt(&key, &ct).unwrap();
assert_eq!(pt, b"");
}
#[test]
fn aes128_ecb_wrong_key() {
let key = [0x42u8; 16];
let ct = aes128_ecb_encrypt(&key, b"secret data");
let wrong_key = [0x99u8; 16];
let result = aes128_ecb_decrypt(&wrong_key, &ct);
match result {
Err(_) => {} Ok(pt) => assert_ne!(pt, b"secret data"),
}
}
#[test]
fn aes256_cbc_decrypt_basic() {
let key = [0x55u8; 32];
let iv: [u8; 16] = key[..16].try_into().unwrap();
let plaintext = b"WeCom media data";
let ct_vec =
Aes256CbcEnc::new(&key.into(), &iv.into()).encrypt_padded_vec_mut::<Pkcs7>(plaintext);
let pt = aes256_cbc_decrypt(&key, &ct_vec).unwrap();
assert_eq!(pt, plaintext);
}
#[test]
fn parse_key_raw_16() {
let raw = [0x42u8; 16];
let key = parse_aes128_key(&raw).unwrap();
assert_eq!(key, raw);
}
#[test]
fn parse_key_hex_32() {
let hex = b"4242424242424242424242424242424242424242424242424242424242424242";
assert!(parse_aes128_key(hex).is_err());
let hex16 = b"42424242424242424242424242424242";
let key = parse_aes128_key(hex16).unwrap();
assert_eq!(key, [0x42u8; 16]);
}
#[test]
fn parse_key_invalid_length() {
assert!(parse_aes128_key(b"short").is_err());
}
}