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