feat(aes): add 128 bit key and block

This commit is contained in:
Kristofers Solo 2025-11-10 09:56:20 +02:00
parent bc1622e43f
commit 8b80e17f82
Signed by: kristoferssolo
GPG Key ID: 74FF8144483D82C8
8 changed files with 430 additions and 0 deletions

17
aes/src/aes.rs Normal file
View 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
View 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
View File

@ -0,0 +1,5 @@
mod block128;
mod secret_block;
use crate::secret_block;
pub use block128::Block128;

View 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
View File

@ -0,0 +1 @@
// AES Constants

92
aes/src/key/aes_key.rs Normal file
View 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
View File

@ -0,0 +1,3 @@
mod aes_key;
pub use aes_key::Key;

View File

@ -0,0 +1,6 @@
mod aes;
mod block;
mod constants;
mod key;
pub use {aes::Aes, block::Block128};