mirror of
https://github.com/kristoferssolo/cipher-workshop.git
synced 2025-12-31 13:52:29 +00:00
feat(cipher-factory,cli): add CBC mode support to CipherContext and CLI
Update CipherContext:
- Add optional iv field for CBC mode
- Add process_cbc() for CBC-specific handling
- Add parse_hex() helper for decryption input
- Separate ECB and CBC processing paths
Update CLI:
- Add --iv argument for initialization vector
- Pass IV through to CipherContext
This commit is contained in:
parent
6c5cd9b78a
commit
1ffc0327b3
@ -1,11 +1,12 @@
|
|||||||
use crate::{Algorithm, OperationMode, OutputFormat};
|
use crate::{Algorithm, OperationMode, OutputFormat};
|
||||||
use cipher_core::{BlockCipher, CipherResult};
|
use cipher_core::{BlockCipher, CipherError, CipherResult, Output};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CipherContext {
|
pub struct CipherContext {
|
||||||
pub algorithm: Algorithm,
|
pub algorithm: Algorithm,
|
||||||
pub operation: OperationMode,
|
pub operation: OperationMode,
|
||||||
pub key: String,
|
pub key: String,
|
||||||
|
pub iv: Option<String>,
|
||||||
pub input_text: String,
|
pub input_text: String,
|
||||||
pub output_format: OutputFormat,
|
pub output_format: OutputFormat,
|
||||||
}
|
}
|
||||||
@ -17,6 +18,7 @@ impl CipherContext {
|
|||||||
algorithm: Algorithm,
|
algorithm: Algorithm,
|
||||||
operation: OperationMode,
|
operation: OperationMode,
|
||||||
key: String,
|
key: String,
|
||||||
|
iv: Option<String>,
|
||||||
input_text: String,
|
input_text: String,
|
||||||
output_format: OutputFormat,
|
output_format: OutputFormat,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -24,22 +26,57 @@ impl CipherContext {
|
|||||||
algorithm,
|
algorithm,
|
||||||
operation,
|
operation,
|
||||||
key,
|
key,
|
||||||
|
iv,
|
||||||
input_text,
|
input_text,
|
||||||
output_format,
|
output_format,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Processes the input text using the configured cipher algorithm and operation.
|
||||||
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns `Err` if parsing the input text or creating the cipher fails,
|
/// Returns `Err` if:
|
||||||
/// or if the encryption/decryption process encounters an error.
|
/// - Parsing the input text or creating the cipher fails
|
||||||
|
/// - The encryption/decryption process encounters an error
|
||||||
|
/// - CBC mode is used without providing an IV
|
||||||
pub fn process(&self) -> CipherResult<String> {
|
pub fn process(&self) -> CipherResult<String> {
|
||||||
let text_bytes = self.algorithm.parse_text(&self.input_text)?;
|
if self.algorithm.requires_iv() {
|
||||||
let cipher = self.algorithm.new_cipher(&self.key)?;
|
self.process_cbc()
|
||||||
self.execute(cipher.as_ref(), &text_bytes)
|
} else {
|
||||||
|
self.process_ecb()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&self, cipher: &dyn BlockCipher, text_bytes: &[u8]) -> CipherResult<String> {
|
fn process_ecb(&self) -> CipherResult<String> {
|
||||||
|
let text_bytes = self.algorithm.parse_text(&self.input_text)?;
|
||||||
|
let cipher = self.algorithm.new_cipher(&self.key)?;
|
||||||
|
self.execute_ecb(cipher.as_ref(), &text_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_cbc(&self) -> CipherResult<String> {
|
||||||
|
let iv = self.iv.as_ref().ok_or_else(|| {
|
||||||
|
CipherError::InvalidPadding("CBC mode requires an IV".into())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let cipher = self.algorithm.new_cbc_cipher(&self.key, iv)?;
|
||||||
|
|
||||||
|
match self.operation {
|
||||||
|
OperationMode::Encrypt => {
|
||||||
|
let ciphertext = cipher.encrypt(self.input_text.as_bytes())?;
|
||||||
|
Ok(format!("{:X}", Output::from(ciphertext)))
|
||||||
|
}
|
||||||
|
OperationMode::Decrypt => {
|
||||||
|
// Parse hex input for decryption
|
||||||
|
let ciphertext = parse_hex(&self.input_text)?;
|
||||||
|
let plaintext = cipher.decrypt(&ciphertext)?;
|
||||||
|
let output = self.output_format.format(&Output::from(plaintext));
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_ecb(&self, cipher: &dyn BlockCipher, text_bytes: &[u8]) -> CipherResult<String> {
|
||||||
match self.operation {
|
match self.operation {
|
||||||
OperationMode::Encrypt => {
|
OperationMode::Encrypt => {
|
||||||
let ciphertext = cipher.encrypt(text_bytes)?;
|
let ciphertext = cipher.encrypt(text_bytes)?;
|
||||||
@ -53,3 +90,26 @@ impl CipherContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses a hex string into bytes.
|
||||||
|
fn parse_hex(s: &str) -> CipherResult<Vec<u8>> {
|
||||||
|
let trimmed = s.trim();
|
||||||
|
let s = trimmed
|
||||||
|
.strip_prefix("0x")
|
||||||
|
.or_else(|| trimmed.strip_prefix("0X"))
|
||||||
|
.unwrap_or(trimmed);
|
||||||
|
|
||||||
|
if !s.len().is_multiple_of(2) {
|
||||||
|
return Err(CipherError::InvalidPadding(
|
||||||
|
"hex string must have even length".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
(0..s.len())
|
||||||
|
.step_by(2)
|
||||||
|
.map(|i| {
|
||||||
|
u8::from_str_radix(&s[i..i + 2], 16)
|
||||||
|
.map_err(|_| CipherError::InvalidPadding(format!("invalid hex at position {i}")))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|||||||
@ -12,11 +12,15 @@ pub struct Args {
|
|||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub algorithm: Algorithm,
|
pub algorithm: Algorithm,
|
||||||
|
|
||||||
/// Key used for encryption/decryption. Can be a string or a path to a file
|
/// Key used for encryption/decryption (hex string, e.g., 0x2b7e...)
|
||||||
#[arg(short, long, required = true)]
|
#[arg(short, long, required = true)]
|
||||||
pub key: String,
|
pub key: String,
|
||||||
|
|
||||||
/// The text to encrypt/decrypt. Can be a string or a path to a file
|
/// Initialization vector for CBC mode (hex string, e.g., 0x0001...)
|
||||||
|
#[arg(long)]
|
||||||
|
pub iv: Option<String>,
|
||||||
|
|
||||||
|
/// The text to encrypt/decrypt
|
||||||
#[arg(value_name = "TEXT", required = true)]
|
#[arg(value_name = "TEXT", required = true)]
|
||||||
pub text: String,
|
pub text: String,
|
||||||
|
|
||||||
@ -31,6 +35,7 @@ impl From<Args> for CipherContext {
|
|||||||
algorithm: args.algorithm,
|
algorithm: args.algorithm,
|
||||||
operation: args.operation,
|
operation: args.operation,
|
||||||
key: args.key,
|
key: args.key,
|
||||||
|
iv: args.iv,
|
||||||
input_text: args.text,
|
input_text: args.text,
|
||||||
output_format: args.output_format.unwrap_or_default(),
|
output_format: args.output_format.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user