mirror of
https://github.com/kristoferssolo/cipher-workshop.git
synced 2025-12-31 13:52:29 +00:00
feat(cipher-factory): add AesCbc algorithm variant
Add AES-CBC support to Algorithm enum:
- Add AesCbc variant with clap name "aes-cbc"
- Add requires_iv() method to check if algorithm needs IV
- Add new_cbc_cipher() for creating AesCbc instances
- Add encrypt_cbc() and decrypt_cbc() helper methods
- Update parse_text() and Display for AesCbc
This commit is contained in:
parent
454d1d6011
commit
6c5cd9b78a
@ -1,7 +1,7 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use aes::{Aes, Block128};
|
||||
use cipher_core::{BlockCipher, BlockError, CipherError};
|
||||
use aes::{Aes, AesCbc, Block128, Iv};
|
||||
use cipher_core::{BlockCipher, BlockError, CipherError, CipherResult};
|
||||
use des::{Block64, Des};
|
||||
use std::str::FromStr;
|
||||
|
||||
@ -10,24 +10,27 @@ use std::str::FromStr;
|
||||
pub enum Algorithm {
|
||||
Des,
|
||||
Aes,
|
||||
#[cfg_attr(feature = "clap", clap(name = "aes-cbc"))]
|
||||
AesCbc,
|
||||
}
|
||||
|
||||
impl Algorithm {
|
||||
/// Creates a new cipher instance for the specified algorithm.
|
||||
/// Returns whether this algorithm requires an IV (Initialization Vector).
|
||||
#[must_use]
|
||||
pub const fn requires_iv(&self) -> bool {
|
||||
matches!(self, Self::AesCbc)
|
||||
}
|
||||
|
||||
/// Creates a new ECB-mode cipher instance for the specified algorithm.
|
||||
///
|
||||
/// Parses the key string and instantiates either DES or AES based on the algorithm choice.
|
||||
/// The key format depends on the algorithm:
|
||||
/// - DES: 64-bit key (hex string, e.g., "0x1334577999bcdff1")
|
||||
/// - AES: 128-bit key (hex string, e.g., "0x2b7e151628aed2a6abf7158809cf4f3c")
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A boxed cipher instance implementing `BlockCipher`, or a `CipherError` if parsing fails.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `CipherError` if the key cannot be parsed (invalid format, wrong length, etc.).
|
||||
///
|
||||
/// Returns `CipherError` if the key cannot be parsed or if called with a CBC algorithm.
|
||||
pub fn new_cipher(&self, key: &str) -> Result<Box<dyn BlockCipher>, CipherError> {
|
||||
match self {
|
||||
Self::Des => {
|
||||
@ -40,9 +43,50 @@ impl Algorithm {
|
||||
let cipher = Aes::from_key(key);
|
||||
Ok(Box::new(cipher))
|
||||
}
|
||||
Self::AesCbc => Err(CipherError::InvalidPadding(
|
||||
"AES-CBC requires an IV; use new_cbc_cipher instead".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new AES-CBC cipher instance with the given key and IV.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `CipherError` if the key or IV cannot be parsed.
|
||||
pub fn new_cbc_cipher(&self, key: &str, iv: &str) -> Result<AesCbc, CipherError> {
|
||||
match self {
|
||||
Self::AesCbc => {
|
||||
let key = Block128::from_str(key)?;
|
||||
let iv = Iv::from_str(iv)?;
|
||||
Ok(AesCbc::new(key, iv))
|
||||
}
|
||||
_ => Err(CipherError::InvalidPadding(format!(
|
||||
"{self} does not support CBC mode"
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encrypts data using CBC mode with PKCS#7 padding.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `CipherError` if encryption fails.
|
||||
pub fn encrypt_cbc(&self, key: &str, iv: &str, plaintext: &[u8]) -> CipherResult<Vec<u8>> {
|
||||
let cipher = self.new_cbc_cipher(key, iv)?;
|
||||
cipher.encrypt(plaintext)
|
||||
}
|
||||
|
||||
/// Decrypts data using CBC mode and removes PKCS#7 padding.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `CipherError` if decryption fails or padding is invalid.
|
||||
pub fn decrypt_cbc(&self, key: &str, iv: &str, ciphertext: &[u8]) -> CipherResult<Vec<u8>> {
|
||||
let cipher = self.new_cbc_cipher(key, iv)?;
|
||||
cipher.decrypt(ciphertext)
|
||||
}
|
||||
|
||||
/// Parses plaintext or ciphertext according to the specified algorithm's block size.
|
||||
///
|
||||
/// Converts a text string into a byte vector using the appropriate block size:
|
||||
@ -63,10 +107,13 @@ impl Algorithm {
|
||||
/// - The text length doesn't match the block size
|
||||
/// - The text contains invalid characters for the given format
|
||||
///
|
||||
/// Parses text for ECB-mode algorithms (single block).
|
||||
///
|
||||
/// For CBC mode, use raw bytes directly instead of this method.
|
||||
pub fn parse_text(&self, text: &str) -> Result<Vec<u8>, BlockError> {
|
||||
match self {
|
||||
Self::Des => Ok(Block64::from_str(text)?.to_be_bytes().to_vec()),
|
||||
Self::Aes => Ok(Block128::from_str(text)?.to_be_bytes().to_vec()),
|
||||
Self::Aes | Self::AesCbc => Ok(Block128::from_str(text)?.to_be_bytes().to_vec()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,6 +123,7 @@ impl Display for Algorithm {
|
||||
let s = match self {
|
||||
Self::Des => "DES",
|
||||
Self::Aes => "AES",
|
||||
Self::AesCbc => "AES-CBC",
|
||||
};
|
||||
f.write_str(s)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user