Files
mylinspirer/src/crypto.rs

78 lines
2.7 KiB
Rust

use std::fmt;
use aes::Aes128;
use base64::{Engine, engine::general_purpose::STANDARD};
use cbc::{
Decryptor, Encryptor,
cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, block_padding::Pkcs7},
};
type Aes128CbcEnc = Encryptor<Aes128>;
type Aes128CbcDec = Decryptor<Aes128>;
#[derive(Debug)]
pub enum CryptoError {
Base64(base64::DecodeError),
Aes(cbc::cipher::InvalidLength),
Unpad(cbc::cipher::block_padding::UnpadError),
Pad(aes::cipher::inout::PadError),
}
impl fmt::Display for CryptoError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CryptoError::Base64(e) => write!(f, "Base64 decode failed: {}", e),
CryptoError::Aes(e) => write!(f, "AES decryption failed: {}", e),
CryptoError::Unpad(e) => write!(f, "PKCS7 unpadding failed: {}", e),
CryptoError::Pad(e) => write!(f, "PKCS7 padding failed: {}", e),
}
}
}
impl std::error::Error for CryptoError {}
pub fn decrypt(ciphertext_b64: &str, key: &str, iv: &str) -> Result<String, CryptoError> {
// 1. Base64 decode the ciphertext
let ciphertext = STANDARD
.decode(ciphertext_b64)
.map_err(CryptoError::Base64)?;
// 2. Initialize AES-128 in CBC mode
let key_bytes = key.as_bytes();
let iv_bytes = iv.as_bytes();
let decryptor = Aes128CbcDec::new_from_slices(key_bytes, iv_bytes).map_err(CryptoError::Aes)?;
// 3. Decrypt the ciphertext, handling padding
let decrypted_len = ciphertext.len();
let mut plaintext = vec![0u8; decrypted_len];
let copy_len = ciphertext.len();
plaintext[..copy_len].copy_from_slice(&ciphertext);
let plaintext_slice = decryptor
.decrypt_padded_mut::<Pkcs7>(&mut plaintext[..decrypted_len])
.map_err(CryptoError::Unpad)?;
// 4. Convert plaintext to a UTF-8 string
Ok(String::from_utf8_lossy(plaintext_slice).to_string())
}
pub fn encrypt(plaintext: &str, key: &str, iv: &str) -> Result<String, CryptoError> {
// 1. Initialize AES-128 in CBC mode
let key_bytes = key.as_bytes();
let iv_bytes = iv.as_bytes();
let encryptor = Aes128CbcEnc::new_from_slices(key_bytes, iv_bytes).map_err(CryptoError::Aes)?;
// 2. Encrypt the plaintext with PKCS7 padding
// Allocate buffer with extra space for padding (AES block size is 16 bytes)
let plaintext_bytes = plaintext.as_bytes();
let mut buffer = vec![0u8; plaintext_bytes.len() + 16];
buffer[..plaintext_bytes.len()].copy_from_slice(plaintext_bytes);
let ciphertext = encryptor
.encrypt_padded_mut::<Pkcs7>(&mut buffer, plaintext_bytes.len())
.map_err(CryptoError::Pad)?;
// 3. Base64 encode the ciphertext
Ok(STANDARD.encode(ciphertext))
}