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)]
|
#[cfg(test)]
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -66,9 +72,8 @@ impl Aes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BlockCipher for Aes {
|
impl BlockCipher for Aes {
|
||||||
const BLOCK_SIZE: usize = 16;
|
fn block_size(&self) -> usize {
|
||||||
fn from_key(key: &[u8]) -> Self {
|
16
|
||||||
Self::new(key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform_impl(
|
fn transform_impl(
|
||||||
@ -76,9 +81,10 @@ impl BlockCipher for Aes {
|
|||||||
block: &[u8],
|
block: &[u8],
|
||||||
action: cipher_core::CipherAction,
|
action: cipher_core::CipherAction,
|
||||||
) -> cipher_core::CipherResult<cipher_core::Output> {
|
) -> 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()
|
.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);
|
let block128 = Block128::from_be_bytes(block_arr);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use zeroize::ZeroizeOnDrop;
|
use zeroize::ZeroizeOnDrop;
|
||||||
|
|
||||||
|
use crate::Block128;
|
||||||
|
|
||||||
/// 128-bit Key for AES
|
/// 128-bit Key for AES
|
||||||
#[derive(ZeroizeOnDrop)]
|
#[derive(ZeroizeOnDrop)]
|
||||||
pub struct Key([u8; 16]);
|
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] {
|
impl From<Key> for [u8; 16] {
|
||||||
fn from(key: Key) -> Self {
|
fn from(key: Key) -> Self {
|
||||||
key.0
|
key.0
|
||||||
|
|||||||
@ -10,6 +10,10 @@ pub enum CipherError {
|
|||||||
/// Input data doesn't match the cipher's block size
|
/// Input data doesn't match the cipher's block size
|
||||||
#[error("Invalid block size: expected {expected} bytes, got {actual}")]
|
#[error("Invalid block size: expected {expected} bytes, got {actual}")]
|
||||||
InvalidBlockSize { expected: usize, actual: usize },
|
InvalidBlockSize { expected: usize, actual: usize },
|
||||||
|
|
||||||
|
/// Error parsing block from string
|
||||||
|
#[error("Error parsing block from string: {0}")]
|
||||||
|
BlockParseError(#[from] BlockError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CipherError {
|
impl CipherError {
|
||||||
|
|||||||
@ -6,9 +6,7 @@ use crate::{CipherAction, CipherError, CipherResult, Output};
|
|||||||
/// Implementers define `transform_impl` to handle the core algorithm,
|
/// Implementers define `transform_impl` to handle the core algorithm,
|
||||||
/// while `transform` provides validation and convenience wrappers.
|
/// while `transform` provides validation and convenience wrappers.
|
||||||
pub trait BlockCipher {
|
pub trait BlockCipher {
|
||||||
const BLOCK_SIZE: usize;
|
fn block_size(&self) -> usize;
|
||||||
|
|
||||||
fn from_key(key: &[u8]) -> Self;
|
|
||||||
|
|
||||||
/// Core cipher transformation (must be implemented by concrete types).
|
/// Core cipher transformation (must be implemented by concrete types).
|
||||||
///
|
///
|
||||||
@ -24,13 +22,11 @@ pub trait BlockCipher {
|
|||||||
///
|
///
|
||||||
/// # Errors
|
/// # 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> {
|
fn transform(&self, block: &[u8], action: CipherAction) -> CipherResult<Output> {
|
||||||
if block.len() != Self::BLOCK_SIZE {
|
let block_size = self.block_size();
|
||||||
return Err(CipherError::invalid_block_size(
|
if block.len() != block_size {
|
||||||
Self::BLOCK_SIZE,
|
return Err(CipherError::invalid_block_size(block_size, block.len()));
|
||||||
block.len(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
self.transform_impl(block, action)
|
self.transform_impl(block, action)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
use std::fmt::Display;
|
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))]
|
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Algorithm {
|
pub enum Algorithm {
|
||||||
@ -7,6 +12,65 @@ pub enum Algorithm {
|
|||||||
Aes,
|
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 {
|
impl Display for Algorithm {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let s = match self {
|
let s = match self {
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
mod args;
|
mod args;
|
||||||
|
|
||||||
use crate::args::Args;
|
use crate::args::Args;
|
||||||
use aes::{Aes, Block128};
|
|
||||||
use cipher_core::BlockCipher;
|
use cipher_core::BlockCipher;
|
||||||
use cipher_factory::{Algorithm, OperationChoice, OutputFormat};
|
use cipher_factory::{self, OperationChoice, OutputFormat};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use color_eyre::eyre::{Ok, Result};
|
use color_eyre::eyre::{Ok, Result};
|
||||||
use des::{Block64, Des};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
@ -19,26 +16,17 @@ fn main() -> Result<()> {
|
|||||||
output_format,
|
output_format,
|
||||||
} = Args::parse();
|
} = Args::parse();
|
||||||
|
|
||||||
match algorithm {
|
let text_bytes = algorithm.parse_text(&text)?;
|
||||||
Algorithm::Des => {
|
let cipher = algorithm.new_cipher(&key)?;
|
||||||
let key = Block64::from_str(&key)?;
|
|
||||||
let text = Block64::from_str(&text)?;
|
execute_cipher(operation, cipher.as_ref(), &text_bytes, output_format)?;
|
||||||
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)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_cipher(
|
fn execute_cipher(
|
||||||
operation: OperationChoice,
|
operation: OperationChoice,
|
||||||
cipher: &impl BlockCipher,
|
cipher: &dyn BlockCipher,
|
||||||
text_bytes: &[u8],
|
text_bytes: &[u8],
|
||||||
output_format: Option<OutputFormat>,
|
output_format: Option<OutputFormat>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
|||||||
@ -32,13 +32,17 @@ impl Des {
|
|||||||
let subkeys = Subkeys::from_key(&key.into());
|
let subkeys = Subkeys::from_key(&key.into());
|
||||||
Self { subkeys }
|
Self { subkeys }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_key(key: impl Into<Key>) -> Self {
|
||||||
|
Self::new(key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockCipher for Des {
|
impl BlockCipher for Des {
|
||||||
const BLOCK_SIZE: usize = 8;
|
fn block_size(&self) -> usize {
|
||||||
|
8
|
||||||
fn from_key(key: &[u8]) -> Self {
|
|
||||||
Self::new(key)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform_impl(
|
fn transform_impl(
|
||||||
@ -46,9 +50,10 @@ impl BlockCipher for Des {
|
|||||||
block: &[u8],
|
block: &[u8],
|
||||||
action: cipher_core::CipherAction,
|
action: cipher_core::CipherAction,
|
||||||
) -> cipher_core::CipherResult<cipher_core::Output> {
|
) -> 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()
|
.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 block64 = Block64::from_be_bytes(block_arr);
|
||||||
let permutated_block = ip(block64);
|
let permutated_block = ip(block64);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use zeroize::ZeroizeOnDrop;
|
use zeroize::ZeroizeOnDrop;
|
||||||
|
|
||||||
|
use crate::Block64;
|
||||||
|
|
||||||
/// 64-bit Key for DES
|
/// 64-bit Key for DES
|
||||||
#[derive(ZeroizeOnDrop)]
|
#[derive(ZeroizeOnDrop)]
|
||||||
pub struct Key([u8; 8]);
|
pub struct Key([u8; 8]);
|
||||||
@ -36,13 +38,19 @@ impl From<&[u8]> for Key {
|
|||||||
let mut bytes = [0; 8];
|
let mut bytes = [0; 8];
|
||||||
let len = value.len().min(8);
|
let len = value.len().min(8);
|
||||||
bytes[..len].copy_from_slice(&value[..len]);
|
bytes[..len].copy_from_slice(&value[..len]);
|
||||||
Self(bytes)
|
bytes.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u64> for Key {
|
impl From<u64> for Key {
|
||||||
fn from(key: u64) -> Self {
|
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