use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use rand::RngCore;
use crate::{error::CryptoError, session_key::SessionKey};
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
pub fn encrypt_message(session_key: &SessionKey, plaintext: &[u8]) -> Result<Vec<u8>, CryptoError> {
let mut iv = [0u8; 16];
rand::rng().fill_bytes(&mut iv);
let block_size = 16;
let padded_len = ((plaintext.len() / block_size) + 1) * block_size;
let mut buffer = vec![0u8; padded_len];
buffer[..plaintext.len()].copy_from_slice(plaintext);
let cipher = Aes256CbcEnc::new(session_key.as_bytes().into(), &iv.into());
let ciphertext = cipher.encrypt_padded_mut::<Pkcs7>(&mut buffer, plaintext.len()).map_err(|e| CryptoError::EncryptionFailed(e.to_string()))?;
let mut result = iv.to_vec();
result.extend_from_slice(ciphertext);
Ok(result)
}
pub fn decrypt_message(session_key: &SessionKey, ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
if ciphertext.len() < 16 {
return Err(CryptoError::DecryptionFailed("Ciphertext too short".into()));
}
let iv: [u8; 16] = ciphertext[..16].try_into().unwrap();
let mut encrypted = ciphertext[16..].to_vec();
let cipher = Aes256CbcDec::new(session_key.as_bytes().into(), &iv.into());
let plaintext = cipher.decrypt_padded_mut::<Pkcs7>(&mut encrypted).map_err(|e| CryptoError::DecryptionFailed(e.to_string()))?;
Ok(plaintext.to_vec())
}
pub fn encrypt_with_hmac_iv(session_key: &SessionKey, plaintext: &[u8]) -> Result<Vec<u8>, CryptoError> {
use hmac::{Hmac, Mac};
use sha1::Sha1;
let mut mac = Hmac::<Sha1>::new_from_slice(session_key.hmac_key()).map_err(|e| CryptoError::EncryptionFailed(format!("HMAC init failed: {}", e)))?;
mac.update(plaintext);
let hmac_result = mac.finalize().into_bytes();
let mut iv = [0u8; 16];
iv[..13].copy_from_slice(&hmac_result[..13]);
rand::rng().fill_bytes(&mut iv[13..]);
let block_size = 16;
let padded_len = ((plaintext.len() / block_size) + 1) * block_size;
let mut buffer = vec![0u8; padded_len];
buffer[..plaintext.len()].copy_from_slice(plaintext);
let cipher = Aes256CbcEnc::new(session_key.as_bytes().into(), &iv.into());
let ciphertext = cipher.encrypt_padded_mut::<Pkcs7>(&mut buffer, plaintext.len()).map_err(|e| CryptoError::EncryptionFailed(e.to_string()))?;
let mut result = iv.to_vec();
result.extend_from_slice(ciphertext);
Ok(result)
}
pub fn decrypt_with_hmac_iv(session_key: &SessionKey, ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
use hmac::{Hmac, Mac};
use sha1::Sha1;
if ciphertext.len() < 16 {
return Err(CryptoError::DecryptionFailed("Ciphertext too short".into()));
}
let iv: [u8; 16] = ciphertext[..16].try_into().unwrap();
let mut encrypted = ciphertext[16..].to_vec();
let cipher = Aes256CbcDec::new(session_key.as_bytes().into(), &iv.into());
let plaintext = cipher.decrypt_padded_mut::<Pkcs7>(&mut encrypted).map_err(|e| CryptoError::DecryptionFailed(e.to_string()))?;
let mut mac = Hmac::<Sha1>::new_from_slice(session_key.hmac_key()).map_err(|e| CryptoError::DecryptionFailed(format!("HMAC init failed: {}", e)))?;
mac.update(plaintext);
let hmac_result = mac.finalize().into_bytes();
if iv[..13] != hmac_result[..13] {
return Err(CryptoError::DecryptionFailed("HMAC verification failed".into()));
}
Ok(plaintext.to_vec())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encrypt_decrypt_roundtrip() {
let key = SessionKey::generate();
let plaintext = b"Hello, Steam!";
let encrypted = encrypt_message(&key, plaintext).unwrap();
let decrypted = decrypt_message(&key, &encrypted).unwrap();
assert_eq!(decrypted, plaintext);
}
}