mirror of
https://github.com/kristoferssolo/cipher-workshop.git
synced 2025-12-20 11:04:38 +00:00
feat(factory): add cipher/algorithm helper functions
This commit is contained in:
parent
051bba33a8
commit
46a47102b9
@ -19,6 +19,12 @@ impl Aes {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn from_key(key: impl Into<Key>) -> 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<cipher_core::Output> {
|
||||
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);
|
||||
|
||||
|
||||
@ -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<u128> for Key {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Block128> for Key {
|
||||
fn from(key: Block128) -> Self {
|
||||
key.to_be_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Key> for [u8; 16] {
|
||||
fn from(key: Key) -> Self {
|
||||
key.0
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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<Output> {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -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<Box<dyn BlockCipher>, 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<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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Algorithm {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let s = match self {
|
||||
|
||||
@ -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<OutputFormat>,
|
||||
) -> Result<()> {
|
||||
|
||||
@ -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<Key>) -> 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<cipher_core::Output> {
|
||||
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);
|
||||
|
||||
|
||||
@ -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<u64> for Key {
|
||||
fn from(key: u64) -> Self {
|
||||
Self(key.to_be_bytes())
|
||||
key.to_be_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Block64> for Key {
|
||||
fn from(key: Block64) -> Self {
|
||||
key.to_be_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user