refactor(des): use InputBlock trait

This commit is contained in:
2025-11-04 10:03:20 +02:00
parent 211a7cefe6
commit db53ae2ee7
16 changed files with 180 additions and 199 deletions

View File

@@ -1,5 +1,6 @@
use crate::{output::OutputFormat, value::Value};
use crate::output::OutputFormat;
use clap::{Parser, ValueEnum};
use des::Block64;
use std::str::FromStr;
#[derive(Debug, Clone, Parser)]
@@ -14,12 +15,12 @@ pub struct Args {
pub algorithm: AlgorithmChoice,
/// Key used for encryption/decryption. Can be a string or a path to a file
#[arg(short, long, value_parser = Value::from_str, required = true)]
pub key: Value,
#[arg(short, long, value_parser = Block64::from_str, required = true)]
pub key: Block64,
/// The text to encrypt/decrypt. Can be a string or a path to a file
#[arg(value_name = "TEXT", value_parser = Value::from_str, required = true)]
pub text: Value,
#[arg(value_name = "TEXT", value_parser = Block64::from_str, required = true)]
pub text: Block64,
/// Output format for decrypted data
#[arg(short = 'f', long)]

View File

@@ -1,13 +1,12 @@
use crate::{args::AlgorithmChoice, value::Value};
use cipher_core::BlockCipher;
use crate::args::AlgorithmChoice;
use cipher_core::{BlockCipher, InputBlock};
use des::Des;
impl AlgorithmChoice {
#[must_use]
pub fn get_cipher(&self, key: Value) -> impl BlockCipher {
let key = key.to_be_bytes();
pub fn get_cipher(&self, key: &impl InputBlock) -> impl BlockCipher {
match self {
Self::Des => Des::from_key(&key),
Self::Des => Des::from_key(key.as_bytes()),
Self::Aes => todo!("Must implement AES first"),
}
}

View File

@@ -1,26 +0,0 @@
use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ValueError {
#[error("String contains no content")]
EmptyString,
#[error("File '{0}' contains no content")]
EmptyFile(PathBuf),
#[error("Failed to find file '{0}'. File does not exist")]
MissingFile(PathBuf),
#[error("Failed to read file '{0}'. Cannot read file contents")]
FileReadingError(PathBuf),
#[error("Invalid number format: {0}")]
InvalidFormat(String),
#[error("Invalid byte string length: expected no more than 8, found {0}")]
InvalidByteStringLength(usize),
#[error("String-to-u64 conversion error: {0}")]
ConversionError(String),
}

View File

@@ -1,8 +1,6 @@
mod args;
mod cipher;
mod error;
mod output;
mod value;
use crate::{
args::{Args, OperationChoice},
@@ -23,12 +21,12 @@ fn main() -> color_eyre::Result<()> {
match operation {
OperationChoice::Encrypt => {
let cipher = algorithm.get_cipher(key);
let cipher = algorithm.get_cipher(&key);
let ciphertext = cipher.encrypt(&text.to_be_bytes())?;
println!("{ciphertext:016X}");
}
OperationChoice::Decrypt => {
let cipher = algorithm.get_cipher(key);
let cipher = algorithm.get_cipher(&key);
let plaintext = cipher.decrypt(&text.to_be_bytes())?;
match output_format.unwrap_or_default() {
OutputFormat::Binary => println!("{plaintext:064b}"),

View File

@@ -1,131 +0,0 @@
use crate::error::ValueError;
use std::{
fmt::{Display, LowerHex, UpperHex},
fs::read_to_string,
path::PathBuf,
str::FromStr,
};
#[derive(Debug, Clone, Copy)]
pub struct Value(u64);
impl Value {
#[inline]
#[must_use]
pub const fn as_64(self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub const fn to_be_bytes(self) -> [u8; 8] {
self.0.to_be_bytes()
}
}
impl From<Value> for u64 {
fn from(value: Value) -> Self {
value.as_64()
}
}
impl From<u64> for Value {
fn from(value: u64) -> Self {
Self(value)
}
}
impl FromStr for Value {
type Err = ValueError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(num) = s.parse::<u64>() {
return Ok(Self(num));
}
let path = PathBuf::from(s);
if path.exists() && path.is_file() {
if let Ok(contents) = read_to_string(&path) {
let value = parse_string_to_u64(&contents)?;
return Ok(Self(value));
}
return Err(ValueError::FileReadingError(path));
}
let value = parse_string_to_u64(s)?;
Ok(Self(value))
}
}
fn parse_string_to_u64(s: &str) -> Result<u64, ValueError> {
let trimmed = s.trim();
if trimmed.is_empty() {
return Err(ValueError::EmptyString);
}
// Hexadecimal with 0x/0X prefix
if let Some(hex_str) = trimmed
.strip_prefix("0X")
.or_else(|| trimmed.strip_prefix("0x"))
{
return parse_radix(hex_str, 16, "Hex");
}
// Binary with 0b/0B prefix
if let Some(bin_str) = trimmed
.strip_prefix("0b")
.or_else(|| trimmed.strip_prefix("0B"))
{
return parse_radix(bin_str, 2, "Binary");
}
// 8-character ASCII string conversion to u64
if trimmed.len() > 8 {
return Err(ValueError::InvalidByteStringLength(trimmed.len()));
}
ascii_string_to_u64(trimmed)
}
fn parse_radix(s: &str, radix: u32, name: &str) -> Result<u64, ValueError> {
let trimmed = s.trim_start_matches('0');
if trimmed.is_empty() {
return Ok(0);
}
u64::from_str_radix(trimmed, radix)
.map_err(|e| ValueError::InvalidFormat(format!("{name} parsing failed: {e}")))
}
fn ascii_string_to_u64(s: &str) -> Result<u64, ValueError> {
if !s.is_ascii() {
return Err(ValueError::ConversionError(
"String contains non-ASCII characters".into(),
));
}
let mut bytes = [0; 8];
for (idx, byte) in s.bytes().enumerate() {
bytes[idx] = byte;
}
Ok(u64::from_be_bytes(bytes))
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:0b}", self.0)
}
}
impl UpperHex for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:016X}", self.0)
}
}
impl LowerHex for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:016x}", self.0)
}
}