mirror of
https://github.com/kristoferssolo/des-rs.git
synced 2025-12-20 11:04:38 +00:00
feat: impl Subkey and Key types
This commit is contained in:
parent
ab1c0e4ad9
commit
f0b9acbc9d
@ -175,7 +175,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
quote! {
|
quote! {
|
||||||
/// Create a new [`Self`] from a key value
|
/// Create a new [`Self`] from a key value
|
||||||
#[inline]
|
#[inline]
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub fn new(key: #inner) -> Self {
|
pub fn new(key: #inner) -> Self {
|
||||||
key.into()
|
key.into()
|
||||||
}
|
}
|
||||||
@ -184,7 +184,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
quote! {
|
quote! {
|
||||||
/// Create a new [`Self`] from a key value
|
/// Create a new [`Self`] from a key value
|
||||||
#[inline]
|
#[inline]
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub fn new(key: #inner) -> Result<Self, #error_type> {
|
pub fn new(key: #inner) -> Result<Self, #error_type> {
|
||||||
key.try_into()
|
key.try_into()
|
||||||
}
|
}
|
||||||
@ -205,14 +205,14 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
#new_method
|
#new_method
|
||||||
|
|
||||||
/// Convert to hex string (formatted for the bit width)
|
/// Convert to hex string (formatted for the bit width)
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub fn to_hex(self) -> String {
|
pub fn to_hex(self) -> String {
|
||||||
let value = self.0 & Self::MAX;
|
let value = self.0 & Self::MAX;
|
||||||
format!("{:0width$X}", value, width = #hex_width)
|
format!("{:0width$X}", value, width = #hex_width)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert to binary string (full bit width with leading zeros)
|
/// Convert to binary string (full bit width with leading zeros)
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub fn to_bin(self) -> String {
|
pub fn to_bin(self) -> String {
|
||||||
let value = self.0 & Self::MAX;
|
let value = self.0 & Self::MAX;
|
||||||
format!("{:0width$b}", value, width = #bin_width)
|
format!("{:0width$b}", value, width = #bin_width)
|
||||||
@ -220,27 +220,27 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
|
|
||||||
/// Check if all bits are set
|
/// Check if all bits are set
|
||||||
#[inline]
|
#[inline]
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub const fn all(self) -> bool {
|
pub const fn all(self) -> bool {
|
||||||
self.0 == Self::MAX
|
self.0 == Self::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if any bit is set
|
/// Check if any bit is set
|
||||||
#[inline]
|
#[inline]
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub const fn any(self) -> bool {
|
pub const fn any(self) -> bool {
|
||||||
self.0 != 0
|
self.0 != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Count the number of set bits
|
/// Count the number of set bits
|
||||||
#[inline]
|
#[inline]
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub const fn count_ones(self) -> u32 {
|
pub const fn count_ones(self) -> u32 {
|
||||||
self.0.count_ones()
|
self.0.count_ones()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Count the number of zero bits within the constrained bit width
|
/// Count the number of zero bits within the constrained bit width
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub const fn count_zeros(self) -> u32 {
|
pub const fn count_zeros(self) -> u32 {
|
||||||
let value = self.0 as #inner & Self::MAX;
|
let value = self.0 as #inner & Self::MAX;
|
||||||
let ones_count = self.count_ones();
|
let ones_count = self.count_ones();
|
||||||
@ -248,7 +248,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Reverse the bit pattern within the constrained bit width
|
/// Reverse the bit pattern within the constrained bit width
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub const fn reverse_bits(self) -> Self {
|
pub const fn reverse_bits(self) -> Self {
|
||||||
let value = self.0 as #inner & Self::MAX;
|
let value = self.0 as #inner & Self::MAX;
|
||||||
let reversed = value.reverse_bits();
|
let reversed = value.reverse_bits();
|
||||||
@ -260,7 +260,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
|
|
||||||
|
|
||||||
/// Rotate the bits left by `n` positions within the bit width
|
/// Rotate the bits left by `n` positions within the bit width
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub const fn rotate_left(self, n: u8) -> Self {
|
pub const fn rotate_left(self, n: u8) -> Self {
|
||||||
let n = n % Self::BIT_WIDTH;
|
let n = n % Self::BIT_WIDTH;
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
@ -274,7 +274,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Rotate the bits right by `n` positions within the bit width
|
/// Rotate the bits right by `n` positions within the bit width
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub const fn rotate_right(self, n: u8) -> Self {
|
pub const fn rotate_right(self, n: u8) -> Self {
|
||||||
let n = n % Self::BIT_WIDTH;
|
let n = n % Self::BIT_WIDTH;
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
@ -288,7 +288,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find the number of leading zero bits within the bit width
|
/// Find the number of leading zero bits within the bit width
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub fn leading_zeros(self) -> u32 {
|
pub fn leading_zeros(self) -> u32 {
|
||||||
let value = self.0 as #inner & Self::MAX;
|
let value = self.0 as #inner & Self::MAX;
|
||||||
let bit_width = Self::BIT_WIDTH as u32;
|
let bit_width = Self::BIT_WIDTH as u32;
|
||||||
@ -301,7 +301,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find the number of trailing zero bits within the bit width
|
/// Find the number of trailing zero bits within the bit width
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub fn trailing_zeros(self) -> u32 {
|
pub fn trailing_zeros(self) -> u32 {
|
||||||
let value = self.0 as #inner & Self::MAX;
|
let value = self.0 as #inner & Self::MAX;
|
||||||
let reversed = self.reverse_bits();
|
let reversed = self.reverse_bits();
|
||||||
@ -309,7 +309,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find the number of leading one bits from the MSB within the bit width
|
/// Find the number of leading one bits from the MSB within the bit width
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub fn leading_ones(self) -> u32 {
|
pub fn leading_ones(self) -> u32 {
|
||||||
let value = self.0 as #inner & Self::MAX;
|
let value = self.0 as #inner & Self::MAX;
|
||||||
let not_value = !value & Self::MAX;
|
let not_value = !value & Self::MAX;
|
||||||
@ -323,7 +323,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find the number of trailing one bits from the LSB within the bit width
|
/// Find the number of trailing one bits from the LSB within the bit width
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub fn trailing_ones(self) -> u32 {
|
pub fn trailing_ones(self) -> u32 {
|
||||||
let value = self.0 as #inner & Self::MAX;
|
let value = self.0 as #inner & Self::MAX;
|
||||||
let reversed = self.reverse_bits();
|
let reversed = self.reverse_bits();
|
||||||
@ -331,13 +331,13 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the value is zero within the bit width
|
/// Check if the value is zero within the bit width
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub const fn is_zero(self) -> bool {
|
pub const fn is_zero(self) -> bool {
|
||||||
(self.0 & Self::MAX) == 0
|
(self.0 & Self::MAX) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a specific bit to 1 (clamped to bit width)
|
/// Set a specific bit to 1 (clamped to bit width)
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub const fn bit_is_set(self, bit: u8) -> bool {
|
pub const fn bit_is_set(self, bit: u8) -> bool {
|
||||||
let clamped = self.clamp_bit_positions(bit);
|
let clamped = self.clamp_bit_positions(bit);
|
||||||
let mask = 1<< clamped;
|
let mask = 1<< clamped;
|
||||||
@ -364,7 +364,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if bits in the given mask are set (within bit width)
|
/// Check if bits in the given mask are set (within bit width)
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub const fn contains_mask(self, mask: Self) -> bool {
|
pub const fn contains_mask(self, mask: Self) -> bool {
|
||||||
let self_value = self.0 & Self::MAX;
|
let self_value = self.0 & Self::MAX;
|
||||||
let mask_value = mask.0 & Self::MAX;
|
let mask_value = mask.0 & Self::MAX;
|
||||||
@ -384,7 +384,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extract a bitfield from the constrained range
|
/// Extract a bitfield from the constrained range
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
pub const fn bitfield(self, low: u8, high: u8) -> u64 {
|
pub const fn bitfield(self, low: u8, high: u8) -> u64 {
|
||||||
let low = self.clamp_bit_positions(low);
|
let low = self.clamp_bit_positions(low);
|
||||||
let high = self.clamp_bit_positions(high);
|
let high = self.clamp_bit_positions(high);
|
||||||
@ -408,7 +408,7 @@ fn generate_bitwise_ops(info: &Struct) -> TokenStream {
|
|||||||
self.0 = (self.0 & !mask) | ((value << low) & mask);
|
self.0 = (self.0 & !mask) | ((value << low) & mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_use]
|
#[must_use]
|
||||||
const fn clamp_bit_positions(self, bit: u8) -> u8 {
|
const fn clamp_bit_positions(self, bit: u8) -> u8 {
|
||||||
if bit >= Self::BIT_WIDTH {
|
if bit >= Self::BIT_WIDTH {
|
||||||
return Self::BIT_WIDTH - 1;
|
return Self::BIT_WIDTH - 1;
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
use bit_wrap::BitWrapper;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, BitWrapper)]
|
||||||
|
#[bit_width(64)]
|
||||||
|
pub struct Block(u64);
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::keys::{key::KeyError, subkey::SubkeyError};
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum DesError {
|
||||||
|
/// Key value exceeds the maximum allowed value for the bit width
|
||||||
|
#[error("Key value {value} exceeds maximum {max} for {width}-bit type")]
|
||||||
|
KeyOutOfRange {
|
||||||
|
value: u64, // Raw value that was too large
|
||||||
|
max: u64, // Maximum allowed value (2^bit_width - 1)
|
||||||
|
width: u8, // Bit width of the key type
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Failed to parse a hex or binary string representation
|
||||||
|
#[error("Failed to parse key string: {0}")]
|
||||||
|
ParseError(#[from] std::num::ParseIntError),
|
||||||
|
|
||||||
|
/// Failed to parse from a string with invalid format
|
||||||
|
#[error("Invalid key format: {0}")]
|
||||||
|
InvalidFormat(String),
|
||||||
|
|
||||||
|
/// Bitfield operation with invalid range (high < low)
|
||||||
|
#[error("Invalid bitfield range: low={low}, high={high} (must have low <= high)")]
|
||||||
|
InvalidBitfieldRange { low: u8, high: u8 },
|
||||||
|
|
||||||
|
/// Attempted to set a bit beyond the valid bit width
|
||||||
|
#[error("Bit index {bit} out of range for {width}-bit type")]
|
||||||
|
InvalidBitIndex { bit: u8, width: u8 },
|
||||||
|
|
||||||
|
#[error("Unknown error: {0}")]
|
||||||
|
Unknown(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DesError {
|
||||||
|
pub fn unknown(input: impl Into<String>) -> Self {
|
||||||
|
Self::Unknown(input.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_from_key_error_for_des {
|
||||||
|
($error_type:ty) => {
|
||||||
|
impl From<$error_type> for DesError {
|
||||||
|
fn from(error: $error_type) -> Self {
|
||||||
|
type Input = $error_type;
|
||||||
|
match error {
|
||||||
|
Input::ValueOutOfRange { value, max, width } => Self::KeyOutOfRange {
|
||||||
|
value: value as u64,
|
||||||
|
max: max as u64,
|
||||||
|
width,
|
||||||
|
},
|
||||||
|
Input::ParseError(err) => Self::ParseError(err),
|
||||||
|
Input::Unknown(msg) => Self::Unknown(msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_key_error_for_des!(SubkeyError);
|
||||||
|
impl_from_key_error_for_des!(KeyError);
|
||||||
@ -1,5 +1,2 @@
|
|||||||
mod key;
|
pub mod key;
|
||||||
mod subkey;
|
pub mod subkey;
|
||||||
|
|
||||||
pub use key::Key;
|
|
||||||
pub use subkey::Subkey;
|
|
||||||
|
|||||||
@ -1,5 +1,47 @@
|
|||||||
|
use crate::keys::key::Key;
|
||||||
use bit_wrap::BitWrapper;
|
use bit_wrap::BitWrapper;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, BitWrapper)]
|
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, BitWrapper)]
|
||||||
#[bit_width(48)]
|
#[bit_width(48)]
|
||||||
pub struct Subkey(u64);
|
pub struct Subkey(u64);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Subkeys([Subkey; 16]);
|
||||||
|
|
||||||
|
impl From<Key> for Subkey {
|
||||||
|
fn from(_key: Key) -> Self {
|
||||||
|
todo!("when other functions are moved to type methods, imlmenet this");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<[u64; 16]> for Subkeys {
|
||||||
|
type Error = SubkeyError;
|
||||||
|
fn try_from(keys: [u64; 16]) -> Result<Self, Self::Error> {
|
||||||
|
let mut subkeys = [Subkey::default(); 16];
|
||||||
|
|
||||||
|
for (idx, &key) in keys.iter().enumerate() {
|
||||||
|
let subkey = Subkey::try_from(key)?;
|
||||||
|
subkeys[idx] = subkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Subkeys(subkeys))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Subkeys> for [Subkey; 16] {
|
||||||
|
fn from(subkeys: Subkeys) -> Self {
|
||||||
|
subkeys.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[Subkey; 16]> for Subkeys {
|
||||||
|
fn as_ref(&self) -> &[Subkey; 16] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<u64> for Subkey {
|
||||||
|
fn eq(&self, other: &u64) -> bool {
|
||||||
|
&self.0 == other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,39 +1,43 @@
|
|||||||
mod blocks;
|
pub mod blocks;
|
||||||
mod constants;
|
pub mod constants;
|
||||||
mod error;
|
pub mod error;
|
||||||
mod keys;
|
pub mod keys;
|
||||||
|
|
||||||
use crate::constants::{E_BOX, FP, IP, P_BOX, PC1_TABLE, PC2_TABLE, ROUND_ROTATIONS, S_BOXES};
|
use crate::{
|
||||||
pub use keys::{Key, Subkey};
|
blocks::Block,
|
||||||
|
constants::{E_BOX, FP, IP, P_BOX, PC1_TABLE, PC2_TABLE, ROUND_ROTATIONS, S_BOXES},
|
||||||
|
error::DesError,
|
||||||
|
keys::{key::Key, subkey::Subkey},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Des {
|
pub struct Des {
|
||||||
pub subkeys: [u64; 16],
|
pub subkeys: [Subkey; 16],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Des {
|
impl Des {
|
||||||
/// Create a new DES instance from a 64-bit key (8 bytes).
|
/// Create a new DES instance from a 64-bit key (8 bytes).
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(key: u64) -> Self {
|
pub fn new(key: impl Into<Key>) -> Self {
|
||||||
let subkeys = generate_subkeys(key);
|
let subkeys = generate_subkeys(key.into());
|
||||||
Self { subkeys }
|
Self { subkeys }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypt a 64-bit block.
|
/// Encrypt a 64-bit block.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn encrypt(&self, block: u64) -> u64 {
|
pub fn encrypt(&self, block: impl Into<Block>) -> u64 {
|
||||||
self.des(block, true)
|
self.des(block.into(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt a 64-bit block.
|
/// Decrypt a 64-bit block.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn decrypt(&self, block: u64) -> u64 {
|
pub fn decrypt(&self, block: impl Into<Block>) -> u64 {
|
||||||
self.des(block, false)
|
self.des(block.into(), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Core DES function: encrypt if forward=true, else decrypt.
|
/// Core DES function: encrypt if forward=true, else decrypt.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn des(&self, block: u64, forward: bool) -> u64 {
|
fn des(&self, block: Block, forward: bool) -> u64 {
|
||||||
let permutated_block = ip(block);
|
let permutated_block = ip(block);
|
||||||
|
|
||||||
let (left, right) = if forward {
|
let (left, right) = if forward {
|
||||||
@ -54,16 +58,18 @@ impl Des {
|
|||||||
/// Accounts for DES specification's big-endian bit numbering (1-64, MSB first)
|
/// Accounts for DES specification's big-endian bit numbering (1-64, MSB first)
|
||||||
/// versus Rust u64's little-endian bit numbering (0-63, LSB first).
|
/// versus Rust u64's little-endian bit numbering (0-63, LSB first).
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn pc1(key: u64) -> u64 {
|
pub fn pc1(key: impl Into<Key>) -> u64 {
|
||||||
permutate(key, 64, 56, &PC1_TABLE)
|
permutate(key.into(), 64, 56, &PC1_TABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compression permuation
|
/// Compression permuation
|
||||||
/// Reduces 56-bits to 48-bit key
|
/// Reduces 56-bits to 48-bit key
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn pc2(key: u64) -> u64 {
|
pub fn pc2(key: u64) -> Result<Subkey, DesError> {
|
||||||
let key_56 = key & 0x00FF_FFFF_FFFF_FFFF;
|
let key_56 = key & 0x00FF_FFFF_FFFF_FFFF;
|
||||||
permutate(key_56, 56, 48, &PC2_TABLE)
|
permutate(key_56, 56, 48, &PC2_TABLE)
|
||||||
|
.try_into()
|
||||||
|
.map_err(DesError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -108,7 +114,7 @@ fn concatenate_halves(left: u32, right: u32, bit_offset: u8) -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generate 16 subkeys from the 64-bit key.
|
/// Generate 16 subkeys from the 64-bit key.
|
||||||
fn generate_subkeys(key: u64) -> [u64; 16] {
|
fn generate_subkeys(key: Key) -> [Subkey; 16] {
|
||||||
let reduced_key = pc1(key); // C_0, D_0
|
let reduced_key = pc1(key); // C_0, D_0
|
||||||
let (mut left, mut right) = split_block(reduced_key);
|
let (mut left, mut right) = split_block(reduced_key);
|
||||||
|
|
||||||
@ -120,7 +126,8 @@ fn generate_subkeys(key: u64) -> [u64; 16] {
|
|||||||
let combined = concatenate_halves(left, right, 28);
|
let combined = concatenate_halves(left, right, 28);
|
||||||
pc2(combined)
|
pc2(combined)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Result<Vec<_>, DesError>>()
|
||||||
|
.expect("no errors")
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("Exactly 16 subkeys expected")
|
.expect("Exactly 16 subkeys expected")
|
||||||
}
|
}
|
||||||
@ -133,7 +140,13 @@ fn generate_subkeys(key: u64) -> [u64; 16] {
|
|||||||
/// - `output_bits` - Number of bits in the output (1-64)
|
/// - `output_bits` - Number of bits in the output (1-64)
|
||||||
/// - `position_table` - 1-based positions (1 to `input_bits`) where each output bit comes from
|
/// - `position_table` - 1-based positions (1 to `input_bits`) where each output bit comes from
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn permutate(input: u64, input_bits: u32, output_bits: u32, position_table: &[u8]) -> u64 {
|
fn permutate(
|
||||||
|
input: impl Into<u64>,
|
||||||
|
input_bits: u32,
|
||||||
|
output_bits: u32,
|
||||||
|
position_table: &[u8],
|
||||||
|
) -> u64 {
|
||||||
|
let input = input.into();
|
||||||
position_table
|
position_table
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -159,8 +172,8 @@ fn permutate(input: u64, input_bits: u32, output_bits: u32, position_table: &[u8
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn ip(message: u64) -> u64 {
|
fn ip(message: impl Into<Block>) -> u64 {
|
||||||
permutate(message, 64, 64, &IP)
|
permutate(message.into(), 64, 64, &IP)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expand the right side of the data from 32 bits to 48.
|
/// Expand the right side of the data from 32 bits to 48.
|
||||||
@ -202,7 +215,7 @@ pub fn fp(block: u64) -> u64 {
|
|||||||
|
|
||||||
/// Process 16 Feistel rounds for ECB encryption/decryption.
|
/// Process 16 Feistel rounds for ECB encryption/decryption.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn process_feistel_rounds(initial_block: u64, subkeys: &[u64]) -> (u32, u32) {
|
fn process_feistel_rounds(initial_block: u64, subkeys: &[Subkey]) -> (u32, u32) {
|
||||||
let (mut left, mut right) = split_block(initial_block);
|
let (mut left, mut right) = split_block(initial_block);
|
||||||
for &subkey in subkeys {
|
for &subkey in subkeys {
|
||||||
(left, right) = feistel(left, right, subkey);
|
(left, right) = feistel(left, right, subkey);
|
||||||
@ -213,7 +226,7 @@ fn process_feistel_rounds(initial_block: u64, subkeys: &[u64]) -> (u32, u32) {
|
|||||||
/// Feistel function: Expand, XOR with subkey, S-box, permute.
|
/// Feistel function: Expand, XOR with subkey, S-box, permute.
|
||||||
/// `R_i` = `L_(i-1)` XOR f(`R_(i-1)`, `K_1`)
|
/// `R_i` = `L_(i-1)` XOR f(`R_(i-1)`, `K_1`)
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn feistel(left: u32, right: u32, subkey: u64) -> (u32, u32) {
|
fn feistel(left: u32, right: u32, subkey: Subkey) -> (u32, u32) {
|
||||||
let function_output = f_function(right, subkey);
|
let function_output = f_function(right, subkey);
|
||||||
let new_right = left ^ function_output;
|
let new_right = left ^ function_output;
|
||||||
// L_i = R_(i-1)
|
// L_i = R_(i-1)
|
||||||
@ -222,9 +235,9 @@ fn feistel(left: u32, right: u32, subkey: u64) -> (u32, u32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn f_function(right: u32, subkey: u64) -> u32 {
|
fn f_function(right: u32, subkey: Subkey) -> u32 {
|
||||||
let expanded = expansion_permutation(right);
|
let expanded = expansion_permutation(right);
|
||||||
let xored = expanded ^ subkey;
|
let xored = expanded ^ subkey.as_ref();
|
||||||
let sboxed = s_box_substitution(xored);
|
let sboxed = s_box_substitution(xored);
|
||||||
p_box_permutation(sboxed)
|
p_box_permutation(sboxed)
|
||||||
}
|
}
|
||||||
@ -232,7 +245,7 @@ fn f_function(right: u32, subkey: u64) -> u32 {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use claims::assert_ge;
|
use claims::{assert_ge, assert_ok};
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
const TEST_KEY: u64 = 0x1334_5779_9BBC_DFF1;
|
const TEST_KEY: u64 = 0x1334_5779_9BBC_DFF1;
|
||||||
@ -287,14 +300,9 @@ mod tests {
|
|||||||
#[case(0x00F8_6655_7AAB_33C7, 0xBF91_8D3D_3F0A)] // K_15
|
#[case(0x00F8_6655_7AAB_33C7, 0xBF91_8D3D_3F0A)] // K_15
|
||||||
#[case(0x00F0_CCAA_F556_678F, 0xCB3D_8B0E_17F5)] // K_16
|
#[case(0x00F0_CCAA_F556_678F, 0xCB3D_8B0E_17F5)] // K_16
|
||||||
fn pc2_permutaion_correct(#[case] before: u64, #[case] after: u64) {
|
fn pc2_permutaion_correct(#[case] before: u64, #[case] after: u64) {
|
||||||
let result = pc2(before);
|
let result = assert_ok!(pc2(before));
|
||||||
|
|
||||||
assert_eq!(result, after, "PC2 permutation failed");
|
assert_eq!(*result.as_ref(), after, "PC2 permutation failed");
|
||||||
assert_ge!(
|
|
||||||
result.leading_zeros(),
|
|
||||||
16,
|
|
||||||
"PC2 result should have leading 16 bits as 0"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
|
use des_lib::Des;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
use des::Des;
|
|
||||||
|
|
||||||
const TEST_KEY: u64 = 0x1334_5779_9BBC_DFF1;
|
const TEST_KEY: u64 = 0x1334_5779_9BBC_DFF1;
|
||||||
const TEST_PLAINTEXT: u64 = 0x0123_4567_89AB_CDEF;
|
const TEST_PLAINTEXT: u64 = 0x0123_4567_89AB_CDEF;
|
||||||
const TEST_CIPHERTEXT: u64 = 0x85E8_1354_0F0A_B405;
|
const TEST_CIPHERTEXT: u64 = 0x85E8_1354_0F0A_B405;
|
||||||
@ -14,9 +13,9 @@ fn des_instance() -> Des {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_ecb_mode_equivalence() {
|
fn test_ecb_mode_equivalence() {
|
||||||
// If you implement ECB mode, test it matches single block
|
// If you implement ECB mode, test it matches single block
|
||||||
let key = 0x1334_5779_9BBC_DFF1;
|
let key = 0x1334_5779_9BBC_DFF1u64;
|
||||||
let des = Des::new(key);
|
let des = Des::new(key);
|
||||||
let plain = 0x0123_4567_89AB_CDEF;
|
let plain = 0x0123_4567_89AB_CDEFu64;
|
||||||
|
|
||||||
let _single_block = des.encrypt(plain);
|
let _single_block = des.encrypt(plain);
|
||||||
// let ecb_result = encrypt_ecb(&[plain]);
|
// let ecb_result = encrypt_ecb(&[plain]);
|
||||||
@ -52,7 +51,11 @@ fn encrypt_decrypt_roundtrip(
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn weak_keys_rejected() {
|
fn weak_keys_rejected() {
|
||||||
let weak_keys = [0x0101010101010101, 0xFEFEFEFEFEFEFEFE, 0xE001E001E001E001];
|
let weak_keys = [
|
||||||
|
0x0101010101010101u64,
|
||||||
|
0xFEFEFEFEFEFEFEFE,
|
||||||
|
0xE001E001E001E001,
|
||||||
|
];
|
||||||
|
|
||||||
for key in weak_keys {
|
for key in weak_keys {
|
||||||
let des = Des::new(key);
|
let des = Des::new(key);
|
||||||
@ -90,8 +93,8 @@ fn all_one_paintext() {
|
|||||||
fn different_inputs() {
|
fn different_inputs() {
|
||||||
let des = des_instance();
|
let des = des_instance();
|
||||||
|
|
||||||
let plain1 = 1;
|
let plain1 = 1u64;
|
||||||
let plain2 = 2;
|
let plain2 = 2u64;
|
||||||
let enc1 = des.encrypt(plain1);
|
let enc1 = des.encrypt(plain1);
|
||||||
let enc2 = des.encrypt(plain2);
|
let enc2 = des.encrypt(plain2);
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use des::Des;
|
use des_lib::Des;
|
||||||
|
|
||||||
// Full expected subkeys for TEST_KEY (48 bits each, from FIPS spec)
|
// Full expected subkeys for TEST_KEY (48 bits each, from FIPS spec)
|
||||||
const EXPECTED_SUBKEYS: [u64; 16] = [
|
const EXPECTED_SUBKEYS: [u64; 16] = [
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user