diff --git a/aes/src/aes.rs b/aes/src/aes.rs index 8c18a42..c8df15a 100644 --- a/aes/src/aes.rs +++ b/aes/src/aes.rs @@ -19,6 +19,12 @@ impl Aes { } } + #[inline] + #[must_use] + pub fn from_key(key: impl Into) -> Self { + Self::new(key) + } + #[cfg(test)] #[inline] #[must_use] @@ -66,9 +72,8 @@ impl Aes { } impl BlockCipher for Aes { - const BLOCK_SIZE: usize = 16; - fn from_key(key: &[u8]) -> Self { - Self::new(key) + fn block_size(&self) -> usize { + 16 } fn transform_impl( @@ -76,9 +81,10 @@ impl BlockCipher for Aes { block: &[u8], action: cipher_core::CipherAction, ) -> cipher_core::CipherResult { - let block_arr: [u8; Self::BLOCK_SIZE] = block + let block_size = self.block_size(); + let block_arr: [u8; 16] = block .try_into() - .map_err(|_| CipherError::invalid_block_size(Self::BLOCK_SIZE, block.len()))?; + .map_err(|_| CipherError::invalid_block_size(block_size, block.len()))?; let block128 = Block128::from_be_bytes(block_arr); diff --git a/aes/src/key/aes_key.rs b/aes/src/key/aes_key.rs index d84a3a5..04d5cc9 100644 --- a/aes/src/key/aes_key.rs +++ b/aes/src/key/aes_key.rs @@ -1,6 +1,8 @@ use std::fmt::Debug; use zeroize::ZeroizeOnDrop; +use crate::Block128; + /// 128-bit Key for AES #[derive(ZeroizeOnDrop)] pub struct Key([u8; 16]); @@ -67,6 +69,12 @@ impl From for Key { } } +impl From for Key { + fn from(key: Block128) -> Self { + key.to_be_bytes().into() + } +} + impl From for [u8; 16] { fn from(key: Key) -> Self { key.0 diff --git a/cipher-core/src/error.rs b/cipher-core/src/error.rs index d97e325..ac22771 100644 --- a/cipher-core/src/error.rs +++ b/cipher-core/src/error.rs @@ -10,6 +10,10 @@ pub enum CipherError { /// Input data doesn't match the cipher's block size #[error("Invalid block size: expected {expected} bytes, got {actual}")] InvalidBlockSize { expected: usize, actual: usize }, + + /// Error parsing block from string + #[error("Error parsing block from string: {0}")] + BlockParseError(#[from] BlockError), } impl CipherError { diff --git a/cipher-core/src/traits/block_cipher.rs b/cipher-core/src/traits/block_cipher.rs index 7a79773..b964de0 100644 --- a/cipher-core/src/traits/block_cipher.rs +++ b/cipher-core/src/traits/block_cipher.rs @@ -6,9 +6,7 @@ use crate::{CipherAction, CipherError, CipherResult, Output}; /// Implementers define `transform_impl` to handle the core algorithm, /// while `transform` provides validation and convenience wrappers. pub trait BlockCipher { - const BLOCK_SIZE: usize; - - fn from_key(key: &[u8]) -> Self; + fn block_size(&self) -> usize; /// Core cipher transformation (must be implemented by concrete types). /// @@ -24,13 +22,11 @@ pub trait BlockCipher { /// /// # Errors /// - /// Returns `CipherError::InvalidBlockSize` if `block.len() != BLOCK_SIZE`. + /// Returns `CipherError::InvalidBlockSize` if `block.len() != self.block_size()`. fn transform(&self, block: &[u8], action: CipherAction) -> CipherResult { - if block.len() != Self::BLOCK_SIZE { - return Err(CipherError::invalid_block_size( - Self::BLOCK_SIZE, - block.len(), - )); + let block_size = self.block_size(); + if block.len() != block_size { + return Err(CipherError::invalid_block_size(block_size, block.len())); } self.transform_impl(block, action) } diff --git a/cipher-factory/src/algorithm.rs b/cipher-factory/src/algorithm.rs index 1dbb7a1..5eb2c6e 100644 --- a/cipher-factory/src/algorithm.rs +++ b/cipher-factory/src/algorithm.rs @@ -1,5 +1,10 @@ use std::fmt::Display; +use aes::{Aes, Block128}; +use cipher_core::{BlockCipher, BlockError, CipherError}; +use des::{Block64, Des}; +use std::str::FromStr; + #[cfg_attr(feature = "clap", derive(clap::ValueEnum))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Algorithm { @@ -7,6 +12,65 @@ pub enum Algorithm { Aes, } +impl Algorithm { + /// Creates a new 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.). + /// + pub fn new_cipher(&self, key: &str) -> Result, CipherError> { + match self { + Self::Des => { + let key = Block64::from_str(key)?; + let cipher = Des::from_key(key); + Ok(Box::new(cipher)) + } + Self::Aes => { + let key = Block128::from_str(key)?; + let cipher = Aes::from_key(key); + Ok(Box::new(cipher)) + } + } + } + + /// 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: + /// - DES: 64-bit blocks (8 bytes) + /// - AES: 128-bit blocks (16 bytes) + /// + /// The input can be provided in various formats (hex, binary, ASCII, etc.) as supported + /// by the block type's `FromStr` implementation. + /// + /// # Returns + /// + /// A byte vector representing the parsed text, or a `BlockError` if parsing fails. + /// + /// # Errors + /// + /// Returns `BlockError` if: + /// - The text format is invalid + /// - The text length doesn't match the block size + /// - The text contains invalid characters for the given format + /// + pub fn parse_text(&self, text: &str) -> Result, 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()), + } + } +} + impl Display for Algorithm { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let s = match self { diff --git a/cli/src/main.rs b/cli/src/main.rs index bc01a55..008bcbe 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,13 +1,10 @@ mod args; use crate::args::Args; -use aes::{Aes, Block128}; use cipher_core::BlockCipher; -use cipher_factory::{Algorithm, OperationChoice, OutputFormat}; +use cipher_factory::{self, OperationChoice, OutputFormat}; use clap::Parser; use color_eyre::eyre::{Ok, Result}; -use des::{Block64, Des}; -use std::str::FromStr; fn main() -> Result<()> { color_eyre::install()?; @@ -19,26 +16,17 @@ fn main() -> Result<()> { output_format, } = Args::parse(); - match algorithm { - Algorithm::Des => { - let key = Block64::from_str(&key)?; - let text = Block64::from_str(&text)?; - let cipher = Des::from_key(&key.to_be_bytes()); - execute_cipher(operation, &cipher, &text.to_be_bytes(), output_format)?; - } - Algorithm::Aes => { - let key = Block128::from_str(&key)?; - let text = Block128::from_str(&text)?; - let cipher = Aes::from_key(&key.to_be_bytes()); - execute_cipher(operation, &cipher, &text.to_be_bytes(), output_format)?; - } - } + let text_bytes = algorithm.parse_text(&text)?; + let cipher = algorithm.new_cipher(&key)?; + + execute_cipher(operation, cipher.as_ref(), &text_bytes, output_format)?; + Ok(()) } fn execute_cipher( operation: OperationChoice, - cipher: &impl BlockCipher, + cipher: &dyn BlockCipher, text_bytes: &[u8], output_format: Option, ) -> Result<()> { diff --git a/des/src/des.rs b/des/src/des.rs index af70774..777040d 100644 --- a/des/src/des.rs +++ b/des/src/des.rs @@ -32,13 +32,17 @@ impl Des { let subkeys = Subkeys::from_key(&key.into()); Self { subkeys } } + + #[inline] + #[must_use] + pub fn from_key(key: impl Into) -> Self { + Self::new(key) + } } impl BlockCipher for Des { - const BLOCK_SIZE: usize = 8; - - fn from_key(key: &[u8]) -> Self { - Self::new(key) + fn block_size(&self) -> usize { + 8 } fn transform_impl( @@ -46,9 +50,10 @@ impl BlockCipher for Des { block: &[u8], action: cipher_core::CipherAction, ) -> cipher_core::CipherResult { - let block_arr: [u8; Self::BLOCK_SIZE] = block + let block_size = self.block_size(); + let block_arr: [u8; 8] = block .try_into() - .map_err(|_| CipherError::invalid_block_size(Self::BLOCK_SIZE, block.len()))?; + .map_err(|_| CipherError::invalid_block_size(block_size, block.len()))?; let block64 = Block64::from_be_bytes(block_arr); let permutated_block = ip(block64); diff --git a/des/src/key/des_key.rs b/des/src/key/des_key.rs index 5402cfa..cda5ab2 100644 --- a/des/src/key/des_key.rs +++ b/des/src/key/des_key.rs @@ -1,6 +1,8 @@ use std::fmt::Debug; use zeroize::ZeroizeOnDrop; +use crate::Block64; + /// 64-bit Key for DES #[derive(ZeroizeOnDrop)] pub struct Key([u8; 8]); @@ -36,13 +38,19 @@ impl From<&[u8]> for Key { let mut bytes = [0; 8]; let len = value.len().min(8); bytes[..len].copy_from_slice(&value[..len]); - Self(bytes) + bytes.into() } } impl From for Key { fn from(key: u64) -> Self { - Self(key.to_be_bytes()) + key.to_be_bytes().into() + } +} + +impl From for Key { + fn from(key: Block64) -> Self { + key.to_be_bytes().into() } }