mirror of
https://github.com/kristoferssolo/cipher-workshop.git
synced 2025-12-20 11:04:38 +00:00
feat(aes): add 128 bit key and block
This commit is contained in:
parent
bc1622e43f
commit
8b80e17f82
17
aes/src/aes.rs
Normal file
17
aes/src/aes.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use crate::key::Key;
|
||||
|
||||
pub struct Aes {}
|
||||
|
||||
impl Aes {
|
||||
pub fn new(_key: impl Into<Key>) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Aes {
|
||||
const BLOCK_SIZE: usize = 16;
|
||||
|
||||
fn from_key(key: &[u8]) -> Self {
|
||||
Self::new(key)
|
||||
}
|
||||
}
|
||||
118
aes/src/block/block128.rs
Normal file
118
aes/src/block/block128.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use crate::block::secret_block;
|
||||
use cipher_core::{BlockError, InputBlock};
|
||||
use std::{
|
||||
slice::{from_raw_parts, from_raw_parts_mut},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
secret_block! {
|
||||
pub struct Block128(u128, 128, 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF);
|
||||
}
|
||||
|
||||
impl InputBlock for Block128 {
|
||||
const BLOCK_SIZE: usize = 128;
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
unsafe { from_raw_parts((&raw const self.0).cast::<u128>().cast::<u8>(), 16) }
|
||||
}
|
||||
fn as_bytes_mut(&mut self) -> &mut [u8] {
|
||||
unsafe { from_raw_parts_mut((&raw mut self.0).cast::<u128>().cast::<u8>(), 16) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Block128 {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_be_bytes(bytes: [u8; 16]) -> Self {
|
||||
Self(u128::from_be_bytes(bytes))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn to_be_bytes(self) -> [u8; 16] {
|
||||
self.0.to_be_bytes()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn to_le_bytes(self) -> [u8; 16] {
|
||||
self.0.to_le_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Block128 {
|
||||
type Err = BlockError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(parse_string_to_u128(s)?))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_string_to_u128(s: &str) -> Result<u128, BlockError> {
|
||||
let trimmed = s.trim();
|
||||
|
||||
if trimmed.is_empty() {
|
||||
return Err(BlockError::EmptyBlock);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
ascii_string_to_u128(trimmed)
|
||||
}
|
||||
|
||||
fn parse_radix(s: &str, radix: u32) -> Result<u128, BlockError> {
|
||||
let trimmed = s.trim_start_matches('0');
|
||||
if trimmed.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
u128::from_str_radix(trimmed, radix).map_err(BlockError::from)
|
||||
}
|
||||
|
||||
fn ascii_string_to_u128(s: &str) -> Result<u128, BlockError> {
|
||||
if s.len() > 8 {
|
||||
return Err(BlockError::InvalidByteStringLength(s.len()));
|
||||
}
|
||||
|
||||
if !s.is_ascii() {
|
||||
return Err(BlockError::conversion_error(
|
||||
"u64",
|
||||
"String contains non-ASCII characters",
|
||||
));
|
||||
}
|
||||
|
||||
let mut bytes = [0u8; 16];
|
||||
let offset = 16 - s.len();
|
||||
bytes[offset..].copy_from_slice(s.as_bytes());
|
||||
|
||||
Ok(u128::from_be_bytes(bytes))
|
||||
}
|
||||
|
||||
impl From<[u8; 16]> for Block128 {
|
||||
fn from(bytes: [u8; 16]) -> Self {
|
||||
Self::from_be_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Block128> for Vec<u8> {
|
||||
fn from(value: Block128) -> Self {
|
||||
value.0.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Block128> for Vec<u8> {
|
||||
fn from(value: &Block128) -> Self {
|
||||
value.0.to_be_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
5
aes/src/block/mod.rs
Normal file
5
aes/src/block/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod block128;
|
||||
mod secret_block;
|
||||
|
||||
use crate::secret_block;
|
||||
pub use block128::Block128;
|
||||
188
aes/src/block/secret_block.rs
Normal file
188
aes/src/block/secret_block.rs
Normal file
@ -0,0 +1,188 @@
|
||||
/// Macro to generate a masked, zeroizable integer wrapper type.
|
||||
///
|
||||
/// Usage:
|
||||
/// ```
|
||||
/// use des::secret_block;
|
||||
/// secret_block! {
|
||||
/// /// docs...
|
||||
/// pub struct Block48(u64, 48, 0x0000_FFFF_FFFF_FFFFu64);
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! secret_block {
|
||||
(
|
||||
$(#[$meta:meta])*
|
||||
$vis:vis struct $name:ident ( $int:tt, $bits:expr, $mask:expr );
|
||||
) => {
|
||||
$(#[$meta])*
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
$vis struct $name($int);
|
||||
|
||||
impl $name {
|
||||
/// Mask to restrict the underlying integer to valid bits.
|
||||
pub const MASK: $int = $mask;
|
||||
|
||||
/// Calculate the number of hex digits needed for the bit width
|
||||
const fn hex_width() -> usize {
|
||||
($bits as usize).div_ceil(4)
|
||||
}
|
||||
|
||||
/// Calculate the number of octal digits needed for the bit width
|
||||
const fn octal_width() -> usize {
|
||||
($bits as usize).div_ceil(3)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(value: $int) -> Self {
|
||||
Self(value & Self::MASK)
|
||||
}
|
||||
|
||||
secret_block!(@conversions_as $int);
|
||||
secret_block!(@conversions_from $int $int);
|
||||
}
|
||||
|
||||
impl From<$int> for $name {
|
||||
fn from(v: $int) -> Self {
|
||||
Self(v & Self::MASK)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$name> for $int {
|
||||
fn from(value: $name) -> $int {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::UpperHex for $name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:0width$X}", self.0, width = Self::hex_width())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::LowerHex for $name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:0width$x}", self.0, width = Self::hex_width())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Octal for $name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:0width$o}", self.0, width = Self::octal_width())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Binary for $name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:0width$b}", self.0, width = $bits)
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
// Helper: generate conversions_as based on type
|
||||
(@conversions_as u8) => {
|
||||
/// Return value as u8
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn as_u8(&self) -> u8 {
|
||||
self.0 as u8
|
||||
}
|
||||
|
||||
secret_block!(@conversions_as u16);
|
||||
};
|
||||
(@conversions_as u16) => {
|
||||
/// Return value as u16
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn as_u16(&self) -> u16 {
|
||||
self.0 as u16
|
||||
}
|
||||
|
||||
secret_block!(@conversions_as u32);
|
||||
};
|
||||
(@conversions_as u32) => {
|
||||
/// Return value as u32
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn as_u32(&self) -> u32 {
|
||||
self.0 as u32
|
||||
}
|
||||
|
||||
secret_block!(@conversions_as u64);
|
||||
};
|
||||
(@conversions_as u64) => {
|
||||
/// Return value as u64
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn as_u64(&self) -> u64 {
|
||||
self.0 as u64
|
||||
}
|
||||
};
|
||||
(@conversions_as u128) => {
|
||||
/// Return value as u64
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn as_u128(&self) -> u128 {
|
||||
self.0 as u128
|
||||
}
|
||||
};
|
||||
// Helper: generate conversions_from based on type
|
||||
(@conversions_from u8 $int:tt) => {
|
||||
/// Create value from u8
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_u8(key: u8) -> Self {
|
||||
Self(key as $int & Self::MASK)
|
||||
}
|
||||
};
|
||||
(@conversions_from u16 $int:tt) => {
|
||||
/// Create value from u16
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_u16(key: u16) -> Self {
|
||||
Self(key as $int & Self::MASK)
|
||||
}
|
||||
|
||||
secret_block!(@conversions_from u8 $int);
|
||||
};
|
||||
(@conversions_from u32 $int:tt) => {
|
||||
/// Create value from u32
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_u32(key: u32) -> Self {
|
||||
Self(key as $int & Self::MASK)
|
||||
}
|
||||
|
||||
secret_block!(@conversions_from u16 $int);
|
||||
};
|
||||
(@conversions_from u64 $int:tt) => {
|
||||
/// Create value from u64
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_u64(key: u64) -> Self {
|
||||
Self(key as $int & Self::MASK)
|
||||
}
|
||||
|
||||
secret_block!(@conversions_from u32 $int);
|
||||
};
|
||||
(@conversions_from u128 $int:tt) => {
|
||||
/// Create value from u64
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_u128(key: u128) -> Self {
|
||||
Self(key & Self::MASK)
|
||||
}
|
||||
|
||||
secret_block!(@conversions_from u64 $int);
|
||||
}
|
||||
}
|
||||
1
aes/src/constants.rs
Normal file
1
aes/src/constants.rs
Normal file
@ -0,0 +1 @@
|
||||
// AES Constants
|
||||
92
aes/src/key/aes_key.rs
Normal file
92
aes/src/key/aes_key.rs
Normal file
@ -0,0 +1,92 @@
|
||||
use std::fmt::Debug;
|
||||
use zeroize::ZeroizeOnDrop;
|
||||
|
||||
/// 128-bit Key for AES
|
||||
#[derive(ZeroizeOnDrop)]
|
||||
pub struct Key([u8; 16]);
|
||||
|
||||
impl Key {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_array(bytes: [u8; 16]) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn as_array(&self) -> &[u8; 16] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn as_2d(&self) -> [[u8; 4]; 4] {
|
||||
[
|
||||
[self.0[0], self.0[1], self.0[2], self.0[3]],
|
||||
[self.0[4], self.0[5], self.0[6], self.0[7]],
|
||||
[self.0[8], self.0[9], self.0[10], self.0[11]],
|
||||
[self.0[12], self.0[13], self.0[14], self.0[15]],
|
||||
]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn as_u128(&self) -> u128 {
|
||||
u128::from_be_bytes(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[[u8; 4]; 4]> for Key {
|
||||
fn from(matrix: [[u8; 4]; 4]) -> Self {
|
||||
let mut bytes = [0; 16];
|
||||
for (idx, row) in matrix.iter().enumerate() {
|
||||
bytes[idx * 4..(idx - 1) * 4].copy_from_slice(row);
|
||||
}
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<[u8; 16]> for Key {
|
||||
fn from(bytes: [u8; 16]) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for Key {
|
||||
fn from(value: &[u8]) -> Self {
|
||||
let mut bytes = [0; 16];
|
||||
let len = value.len().min(16);
|
||||
bytes[..len].copy_from_slice(&value[..len]);
|
||||
bytes.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for Key {
|
||||
fn from(key: u128) -> Self {
|
||||
key.to_be_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Key> for [u8; 16] {
|
||||
fn from(key: Key) -> Self {
|
||||
key.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Key> for [[u8; 4]; 4] {
|
||||
fn from(key: Key) -> Self {
|
||||
key.as_2d()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Key {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Key {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("Key([REDACTED])")
|
||||
}
|
||||
}
|
||||
3
aes/src/key/mod.rs
Normal file
3
aes/src/key/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod aes_key;
|
||||
|
||||
pub use aes_key::Key;
|
||||
@ -0,0 +1,6 @@
|
||||
mod aes;
|
||||
mod block;
|
||||
mod constants;
|
||||
mod key;
|
||||
|
||||
pub use {aes::Aes, block::Block128};
|
||||
Loading…
Reference in New Issue
Block a user