use des::TdesEde3;
use des::cipher::{BlockEncrypt, BlockDecrypt, KeyInit, generic_array::GenericArray};
use log::debug;
#[derive(Debug)]
pub enum TDesError {
InvalidKey(String),
InvalidData(String),
EncryptionFailed(String),
DecryptionFailed(String),
}
pub struct TDesOperations;
impl TDesOperations {
pub const BLOCK_SIZE: usize = 8;
pub const KEY_SIZE: usize = 24;
pub fn encrypt_block(key: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, TDesError> {
if key.len() != Self::KEY_SIZE {
return Err(TDesError::InvalidKey(
format!("Invalid key length: expected {}, got {}", Self::KEY_SIZE, key.len())
));
}
if plaintext.len() != Self::BLOCK_SIZE {
return Err(TDesError::InvalidData(
format!("Invalid block length: expected {}, got {}", Self::BLOCK_SIZE, plaintext.len())
));
}
let cipher = TdesEde3::new(GenericArray::from_slice(key));
let mut block = GenericArray::clone_from_slice(plaintext);
cipher.encrypt_block(&mut block);
Ok(block.to_vec())
}
pub fn decrypt_block(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, TDesError> {
if key.len() != Self::KEY_SIZE {
return Err(TDesError::InvalidKey(
format!("Invalid key length: expected {}, got {}", Self::KEY_SIZE, key.len())
));
}
if ciphertext.len() != Self::BLOCK_SIZE {
return Err(TDesError::InvalidData(
format!("Invalid block length: expected {}, got {}", Self::BLOCK_SIZE, ciphertext.len())
));
}
let cipher = TdesEde3::new(GenericArray::from_slice(key));
let mut block = GenericArray::clone_from_slice(ciphertext);
cipher.decrypt_block(&mut block);
Ok(block.to_vec())
}
pub fn encrypt_ecb(key: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, TDesError> {
if key.len() != Self::KEY_SIZE {
return Err(TDesError::InvalidKey(
format!("Invalid key length: expected {}, got {}", Self::KEY_SIZE, key.len())
));
}
if !plaintext.len().is_multiple_of(Self::BLOCK_SIZE) {
return Err(TDesError::InvalidData(
format!("Data length {} is not a multiple of block size {}",
plaintext.len(), Self::BLOCK_SIZE)
));
}
debug!("3DES-ECB encrypting {} bytes", plaintext.len());
let cipher = TdesEde3::new(GenericArray::from_slice(key));
let mut result = Vec::with_capacity(plaintext.len());
for chunk in plaintext.chunks(Self::BLOCK_SIZE) {
let mut block = GenericArray::clone_from_slice(chunk);
cipher.encrypt_block(&mut block);
result.extend_from_slice(&block);
}
Ok(result)
}
pub fn decrypt_ecb(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, TDesError> {
if key.len() != Self::KEY_SIZE {
return Err(TDesError::InvalidKey(
format!("Invalid key length: expected {}, got {}", Self::KEY_SIZE, key.len())
));
}
if !ciphertext.len().is_multiple_of(Self::BLOCK_SIZE) {
return Err(TDesError::InvalidData(
format!("Data length {} is not a multiple of block size {}",
ciphertext.len(), Self::BLOCK_SIZE)
));
}
debug!("3DES-ECB decrypting {} bytes", ciphertext.len());
let cipher = TdesEde3::new(GenericArray::from_slice(key));
let mut result = Vec::with_capacity(ciphertext.len());
for chunk in ciphertext.chunks(Self::BLOCK_SIZE) {
let mut block = GenericArray::clone_from_slice(chunk);
cipher.decrypt_block(&mut block);
result.extend_from_slice(&block);
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encrypt_decrypt_block() {
let key = [0x01u8; 24];
let plaintext = [0x00u8; 8];
let ciphertext = TDesOperations::encrypt_block(&key, &plaintext).unwrap();
assert_eq!(ciphertext.len(), 8);
let decrypted = TDesOperations::decrypt_block(&key, &ciphertext).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_encrypt_decrypt_ecb() {
let key = [0x01u8; 24];
let plaintext = [0x00u8; 16];
let ciphertext = TDesOperations::encrypt_ecb(&key, &plaintext).unwrap();
assert_eq!(ciphertext.len(), 16);
let decrypted = TDesOperations::decrypt_ecb(&key, &ciphertext).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_invalid_key_length() {
let key = [0x01u8; 16]; let plaintext = [0x00u8; 8];
let result = TDesOperations::encrypt_block(&key, &plaintext);
assert!(result.is_err());
}
#[test]
fn test_invalid_block_length() {
let key = [0x01u8; 24];
let plaintext = [0x00u8; 7];
let result = TDesOperations::encrypt_block(&key, &plaintext);
assert!(result.is_err());
}
}