mirror of
https://github.com/kristoferssolo/cipher-workshop.git
synced 2025-12-20 11:04:38 +00:00
refactor(key): improve secret_int! macro
This commit is contained in:
parent
2d59f4fb70
commit
db52714d52
@ -3,97 +3,26 @@ use thiserror::Error;
|
||||
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CipherError {
|
||||
/// Invalid key size for the cipher
|
||||
#[error("invalid key size: expected {expected} bytes, got {actual}")]
|
||||
#[error("Invalid key size: expected {expected} bytes, got {actual}")]
|
||||
InvalidKeySize { expected: usize, actual: usize },
|
||||
|
||||
/// Input data doesn't match the cipher's block size
|
||||
#[error("invalid block size: expected {expected} bytes, got {actual}")]
|
||||
#[error("Invalid block size: expected {expected} bytes, got {actual}")]
|
||||
InvalidBlockSize { expected: usize, actual: usize },
|
||||
|
||||
/// Decryption detected invalid padding
|
||||
#[error("invalid padding detected during decryption")]
|
||||
InvalidPadding,
|
||||
|
||||
/// Input length not valid for unpadded operation
|
||||
#[error("invalid plaintext length: {actual} bytes (must be multiple of block size)")]
|
||||
InvalidPlaintextLength { actual: usize },
|
||||
|
||||
/// General size validation failure
|
||||
#[error("size mismatch: expected {expected} bytes, got {actual}")]
|
||||
InvalidSize { expected: usize, actual: usize },
|
||||
}
|
||||
|
||||
impl CipherError {
|
||||
/// Creates a key size error
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn invalid_key_size(expected: usize, actual: usize) -> Self {
|
||||
Self::InvalidKeySize { expected, actual }
|
||||
}
|
||||
|
||||
/// Creates a block size error
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn invalid_block_size(expected: usize, actual: usize) -> Self {
|
||||
Self::InvalidBlockSize { expected, actual }
|
||||
}
|
||||
|
||||
/// Creates an invalid padding error
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn invalid_padding() -> Self {
|
||||
Self::InvalidPadding
|
||||
}
|
||||
|
||||
/// Creates a plaintext length error
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn invalid_plaintext_length(actual: usize) -> Self {
|
||||
Self::InvalidPlaintextLength { actual }
|
||||
}
|
||||
|
||||
/// Returns true if this is a key size error
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_key_error(&self) -> bool {
|
||||
matches!(self, Self::InvalidKeySize { .. })
|
||||
}
|
||||
|
||||
/// Returns true if this is a block size error
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_block_error(&self) -> bool {
|
||||
matches!(self, Self::InvalidBlockSize { .. })
|
||||
}
|
||||
|
||||
/// Returns true if this is a size-related error
|
||||
#[must_use]
|
||||
pub const fn is_size_error(&self) -> bool {
|
||||
self.is_key_error() || self.is_block_error() || matches!(self, Self::InvalidSize { .. })
|
||||
}
|
||||
|
||||
/// Returns the expected size for size-related errors
|
||||
#[must_use]
|
||||
pub const fn expected_size(&self) -> Option<usize> {
|
||||
match self {
|
||||
Self::InvalidKeySize { expected, .. }
|
||||
| Self::InvalidBlockSize { expected, .. }
|
||||
| Self::InvalidSize { expected, .. } => Some(*expected),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the actual size for size-related errors
|
||||
#[must_use]
|
||||
pub const fn actual_size(&self) -> Option<usize> {
|
||||
match self {
|
||||
Self::InvalidKeySize { actual, .. }
|
||||
| Self::InvalidBlockSize { actual, .. }
|
||||
| Self::InvalidSize { actual, .. }
|
||||
| Self::InvalidPlaintextLength { actual } => Some(*actual),
|
||||
Self::InvalidPadding => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type alias for clean Result types
|
||||
|
||||
@ -1,10 +1,28 @@
|
||||
use crate::{CipherAction, CipherError, CipherResult};
|
||||
|
||||
/// Generic block cipher trait.
|
||||
///
|
||||
/// Implements the standard encrypt/decrypt interface for block ciphers.
|
||||
/// Implementers define `transform_impl` to handle the core algorithm,
|
||||
/// while `transform` provides validation and convenience wrappers.
|
||||
pub trait BlockCipher: Sized {
|
||||
const BLOCK_SIZE: usize;
|
||||
|
||||
/// Core cipher transformation (must be implemented by concrete types).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `CipherError` if the transformation fails.
|
||||
fn transform_impl(&self, block: &[u8], action: CipherAction) -> CipherResult<Vec<u8>>;
|
||||
|
||||
/// Transforms a block with validation.
|
||||
///
|
||||
/// Validates that the block size matches `BLOCK_SIZE` before delegating
|
||||
/// to `transform_impl`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `CipherError::InvalidBlockSize` if `block.len() != BLOCK_SIZE`.
|
||||
fn transform(&self, block: &[u8], action: CipherAction) -> CipherResult<Vec<u8>> {
|
||||
if block.len() != Self::BLOCK_SIZE {
|
||||
return Err(CipherError::invalid_block_size(
|
||||
@ -15,9 +33,20 @@ pub trait BlockCipher: Sized {
|
||||
self.transform_impl(block, action)
|
||||
}
|
||||
|
||||
/// Encrypts a single block.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `CipherError::InvalidBlockSize` if the plaintext is not exactly `BLOCK_SIZE` bytes.
|
||||
fn encrypt(&self, plaintext: &[u8]) -> CipherResult<Vec<u8>> {
|
||||
self.transform(plaintext, CipherAction::Encrypt)
|
||||
}
|
||||
|
||||
/// Decrypts a single block.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `CipherError::InvalidBlockSize` if the plaintext is not exactly `BLOCK_SIZE` bytes.
|
||||
fn decrypt(&self, ciphertext: &[u8]) -> CipherResult<Vec<u8>> {
|
||||
self.transform(ciphertext, CipherAction::Decrypt)
|
||||
}
|
||||
|
||||
@ -45,27 +45,27 @@ impl BitXor for Block48 {
|
||||
impl BitXor<Subkey> for Block48 {
|
||||
type Output = Self;
|
||||
fn bitxor(self, rhs: Subkey) -> Self::Output {
|
||||
Self(self.0 ^ rhs.as_int())
|
||||
Self(self.0 ^ rhs.as_u64())
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor<&Subkey> for Block48 {
|
||||
type Output = Self;
|
||||
fn bitxor(self, rhs: &Subkey) -> Self::Output {
|
||||
Self(self.0 ^ rhs.as_int())
|
||||
Self(self.0 ^ rhs.as_u64())
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor<Subkey> for &Block48 {
|
||||
type Output = Block48;
|
||||
fn bitxor(self, rhs: Subkey) -> Self::Output {
|
||||
Block48(self.0 ^ rhs.as_int())
|
||||
Block48(self.0 ^ rhs.as_u64())
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor<&Subkey> for &Block48 {
|
||||
type Output = Block48;
|
||||
fn bitxor(self, rhs: &Subkey) -> Self::Output {
|
||||
Block48(self.0 ^ rhs.as_int())
|
||||
Block48(self.0 ^ rhs.as_u64())
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,16 +4,33 @@ use crate::{
|
||||
key::{Key, Subkey, Subkeys},
|
||||
utils::permutate,
|
||||
};
|
||||
use cipher_core::{BlockCipher, CipherAction, CipherError, CipherResult};
|
||||
use cipher_core::{BlockCipher, CipherAction, CipherError};
|
||||
|
||||
pub struct Des {
|
||||
subkeys: Subkeys,
|
||||
}
|
||||
|
||||
impl Des {
|
||||
pub fn new(key: impl Into<Key>) -> CipherResult<Self> {
|
||||
let subkeys = Subkeys::from_key(&key.into())?;
|
||||
Ok(Self { subkeys })
|
||||
/// Creates a new DES cipher with the given key.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `key` - An 8-byte key (64 bits). Any type implementing `Into<Key>` is accepted
|
||||
/// (e.g., `&[u8; 8]`, `u64`).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// `CipherError::InvalidKeySize` if the key is not exactly 8 bytes.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use des::Des;
|
||||
/// let des = Des::new(0x1334_5779_9BBC_DFF1u64).expect("Valid key");
|
||||
/// ```
|
||||
pub fn new(key: impl Into<Key>) -> Self {
|
||||
let subkeys = Subkeys::from_key(&key.into());
|
||||
Self { subkeys }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,11 +8,10 @@ pub struct CD56 {
|
||||
}
|
||||
|
||||
impl CD56 {
|
||||
pub fn new(c: impl Into<Half28>, d: impl Into<Half28>) -> Self {
|
||||
Self {
|
||||
c: c.into(),
|
||||
d: d.into(),
|
||||
}
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn new(c: Half28, d: Half28) -> Self {
|
||||
Self { c, d }
|
||||
}
|
||||
|
||||
pub fn rotate_left(&mut self, amount: u8) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::key::secret_int;
|
||||
use crate::key::secret_key;
|
||||
|
||||
secret_int! {
|
||||
secret_key! {
|
||||
/// 28-bit half (C or D), stored in lower 28 bits of u32.
|
||||
pub struct Half28(u32, 28, 0x0FFF_FFFF);
|
||||
}
|
||||
@ -11,15 +11,14 @@ impl Half28 {
|
||||
let value = self.0;
|
||||
let main_shifted = (value << amount) & Self::MASK;
|
||||
let wrapped_bits = (value >> (28 - amount)) & ((1 << amount) - 1);
|
||||
Self::from_int(main_shifted | wrapped_bits)
|
||||
Self::from_u32(main_shifted | wrapped_bits)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(0x0F0C_CAAF, 0x0E19_955F, 1)] // C_1
|
||||
@ -55,7 +54,7 @@ mod tests {
|
||||
#[case(0x0EAA_CCF1, 0x0AAB_33C7, 2)] // D_15
|
||||
#[case(0x0AAB_33C7, 0x0556_678F, 1)] // D_16
|
||||
fn half28_rotation(#[case] key: u32, #[case] expected: u32, #[case] amount: u8) {
|
||||
let result = Half28::from_int(key).rotate_left(amount).as_int();
|
||||
let result = Half28::from_u32(key).rotate_left(amount).as_u32();
|
||||
|
||||
assert_eq!(
|
||||
result, expected,
|
||||
|
||||
@ -1,26 +1,26 @@
|
||||
use crate::{
|
||||
key::{cd56::CD56, half28::Half28},
|
||||
secret_int,
|
||||
secret_key,
|
||||
};
|
||||
|
||||
secret_int! {
|
||||
secret_key! {
|
||||
/// 56-bit key after PC-1 (lower 56 bits used).
|
||||
pub struct Key56(u64, 56, 0x00FF_FFFF_FFFF_FFFF);
|
||||
}
|
||||
|
||||
impl Key56 {
|
||||
#[must_use]
|
||||
pub fn split(&self) -> CD56 {
|
||||
pub const fn split(&self) -> CD56 {
|
||||
let c = ((self.0 >> 28) & 0x0FFF_FFFF) as u32;
|
||||
let d = (self.0 & 0x0FFF_FFFF) as u32;
|
||||
CD56::new(c, d)
|
||||
CD56::new(Half28::from_u32(c), Half28::from_u32(d))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_half28(left: &Half28, right: &Half28) -> Self {
|
||||
let left = u64::from(left.as_int());
|
||||
let right = u64::from(right.as_int());
|
||||
Self::from_int((left << 28) | right)
|
||||
pub const fn from_half28(left: &Half28, right: &Half28) -> Self {
|
||||
let left = left.as_u64();
|
||||
let right = right.as_u64();
|
||||
Self::from_u64((left << 28) | right)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,9 +2,9 @@ mod cd56;
|
||||
mod des_key;
|
||||
mod half28;
|
||||
mod key56;
|
||||
mod secret_int;
|
||||
mod secret_key;
|
||||
mod subkey;
|
||||
mod subkeys;
|
||||
|
||||
use crate::secret_int;
|
||||
use crate::secret_key;
|
||||
pub use {des_key::Key, subkey::Subkey, subkeys::Subkeys};
|
||||
|
||||
@ -1,77 +0,0 @@
|
||||
/// Macro to generate a masked, zeroizable integer wrapper type.
|
||||
///
|
||||
/// Usage:
|
||||
/// ```
|
||||
/// secret_int! {
|
||||
/// /// docs...
|
||||
/// pub struct Subkey(u64, 48, 0x0000_FFFF_FFFF_FFFFu64);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Optional `clone` token enables an explicit Clone impl:
|
||||
/// ```
|
||||
/// secret_int! { pub struct Foo(u32, 28, 0x0FFF_FFFFu32, clone); }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! secret_int {
|
||||
(
|
||||
$(#[$meta:meta])*
|
||||
$vis:vis struct $name:ident ( $int:ty, $bits:expr, $mask:expr $(, $opt:ident )? );
|
||||
) => {
|
||||
$(#[$meta])*
|
||||
#[derive(::zeroize::ZeroizeOnDrop, Default, Eq)]
|
||||
#[zeroize(drop)]
|
||||
$vis struct $name($int);
|
||||
|
||||
impl $name {
|
||||
/// Mask to restrict the underlying integer to valid bits.
|
||||
pub const MASK: $int = $mask;
|
||||
|
||||
/// Create from the given integer.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_int(key: $int) -> Self {
|
||||
Self(key & Self::MASK)
|
||||
}
|
||||
|
||||
/// Return as masked integer value;
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn as_int(&self) -> $int {
|
||||
self.0 & Self::MASK
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally add Clone if requested explicitly (discouraged for secrets)
|
||||
secret_int!(@maybe_add_clone $( $opt )? ; $name, $int);
|
||||
|
||||
impl ::std::fmt::Debug for $name {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
f.write_str(concat!(stringify!($name), "[REDACTED]"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$int> for $name {
|
||||
fn from(v: $int) -> Self {
|
||||
Self::from_int(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for $name {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_int() == other.as_int()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// helper arm: create Clone impl only when "clone" token present
|
||||
(@maybe_add_clone clone ; $name:ident, $int:ty) => {
|
||||
impl Clone for $name {
|
||||
fn clone(&self) -> Self {
|
||||
// explicit clone - intentionally duplicating secret
|
||||
Self::from_int(self.as_int())
|
||||
}
|
||||
}
|
||||
};
|
||||
(@maybe_add_clone ; $name:ident, $int:ty) => {};
|
||||
}
|
||||
126
des/src/key/secret_key.rs
Normal file
126
des/src/key/secret_key.rs
Normal file
@ -0,0 +1,126 @@
|
||||
/// Macro to generate a masked, zeroizable integer wrapper type.
|
||||
///
|
||||
/// Usage:
|
||||
/// ```
|
||||
/// secret_key! {
|
||||
/// /// docs...
|
||||
/// pub struct Subkey(u64, 48, 0x0000_FFFF_FFFF_FFFFu64);
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! secret_key {
|
||||
(
|
||||
$(#[$meta:meta])*
|
||||
$vis:vis struct $name:ident ( $int:tt, $bits:expr, $mask:expr );
|
||||
) => {
|
||||
$(#[$meta])*
|
||||
#[derive(::zeroize::ZeroizeOnDrop, Default)]
|
||||
#[zeroize(drop)]
|
||||
$vis struct $name($int);
|
||||
|
||||
impl $name {
|
||||
/// Mask to restrict the underlying integer to valid bits.
|
||||
pub const MASK: $int = $mask;
|
||||
|
||||
secret_key!(@conversions_as $int);
|
||||
secret_key!(@conversions_from $int $int);
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for $name {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
f.write_str(concat!(stringify!($name), "[REDACTED]"))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$int> for $name {
|
||||
fn from(v: $int) -> Self {
|
||||
Self(v & Self::MASK)
|
||||
}
|
||||
}
|
||||
};
|
||||
// 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_key!(@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_key!(@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_key!(@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_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_key!(@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_key!(@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 & Self::MASK)
|
||||
}
|
||||
|
||||
secret_key!(@conversions_from u32 $int);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::key::secret_int;
|
||||
use crate::key::secret_key;
|
||||
|
||||
secret_int! {
|
||||
secret_key! {
|
||||
/// A single DES round subkey (48 bits stored in lower bits of u64).
|
||||
pub struct Subkey(u64, 48, 0x0000_FFFF_FFFF_FFFF);
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@ use crate::{
|
||||
key::{Key, cd56::CD56, key56::Key56, subkey::Subkey},
|
||||
utils::permutate,
|
||||
};
|
||||
use cipher_core::CipherResult;
|
||||
use std::{
|
||||
array,
|
||||
fmt::Debug,
|
||||
iter::Rev,
|
||||
ops::Index,
|
||||
@ -12,79 +12,52 @@ use std::{
|
||||
};
|
||||
|
||||
/// Container for all 16 round subkeys; zeroized on drop.
|
||||
#[derive(Default)]
|
||||
pub struct Subkeys([Subkey; 16]);
|
||||
|
||||
impl Subkeys {
|
||||
#[inline]
|
||||
/// Generates 16 round subkeys from the given key.
|
||||
#[must_use]
|
||||
pub const fn new_empty() -> Self {
|
||||
Self([const { Subkey::zero() }; 16])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn as_array(&self) -> &[Subkey; 16] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get(&self, idx: usize) -> Option<&Subkey> {
|
||||
self.0.get(idx)
|
||||
}
|
||||
|
||||
/// # Errors
|
||||
/// # Panics
|
||||
pub fn from_key(key: &Key) -> CipherResult<Self> {
|
||||
pub fn from_key(key: &Key) -> Self {
|
||||
let mut cd56 = pc1(key).split(); // 56-bit: C0 + D0
|
||||
|
||||
let subkeys = ROUND_ROTATIONS
|
||||
.iter()
|
||||
.map(|&shift_amount| {
|
||||
cd56.rotate_left(shift_amount);
|
||||
pc2(&cd56)
|
||||
})
|
||||
.collect::<Vec<Subkey>>()
|
||||
.try_into()
|
||||
.expect("Exactly 16 subkeys expected");
|
||||
let subkeys = array::from_fn(|idx| {
|
||||
cd56.rotate_left(ROUND_ROTATIONS[idx]);
|
||||
pc2(&cd56)
|
||||
});
|
||||
|
||||
Ok(Self(subkeys))
|
||||
Self(subkeys)
|
||||
}
|
||||
|
||||
/// Borrowing forward iterator.
|
||||
/// Returns an iterator over the subkeys.
|
||||
pub fn iter(&self) -> Iter<'_, Subkey> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Borrowing reverse iterator.
|
||||
/// Returns a reverse iterator over the subkeys.
|
||||
pub fn iter_rev(&self) -> Rev<Iter<'_, Subkey>> {
|
||||
self.0.iter().rev()
|
||||
}
|
||||
|
||||
/// Mutable iterator if you need it.
|
||||
/// Returns a mutable iterator over the subkeys.
|
||||
pub fn iter_mut(&mut self) -> IterMut<'_, Subkey> {
|
||||
self.0.iter_mut()
|
||||
}
|
||||
|
||||
/// Consume `self` and return a new `Subkeys` with reversed order.
|
||||
#[must_use]
|
||||
pub const fn reversed(mut self) -> Self {
|
||||
self.0.reverse();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Initial permutation (PC-1): 64-bit -> 56-bit.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn pc1(key: &Key) -> Key56 {
|
||||
permutate(key.as_u64(), 64, 56, &PC1).into()
|
||||
}
|
||||
|
||||
/// Compression permutation (PC-2): 56-bit -> 48-bit.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
fn pc2(cd: &CD56) -> Subkey {
|
||||
let key56 = Key56::from(cd);
|
||||
permutate(key56.as_int(), 56, 48, &PC2).into()
|
||||
permutate(key56.as_u64(), 56, 48, &PC2).into()
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Subkeys {
|
||||
@ -116,12 +89,6 @@ impl Debug for Subkeys {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Subkeys {
|
||||
fn default() -> Self {
|
||||
Self::new_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -132,7 +99,7 @@ mod tests {
|
||||
#[rstest]
|
||||
#[case(TEST_KEY, 0x00F0_CCAA_F556_678F)]
|
||||
fn pc1_permutaion_correct(#[case] key: u64, #[case] expected: u64) {
|
||||
let result = pc1(&key.into()).as_int();
|
||||
let result = pc1(&key.into()).as_u64();
|
||||
assert_eq!(
|
||||
result, expected,
|
||||
"PC1 permutation failed. Expected {expected:08X}, got {result:08X}",
|
||||
@ -159,7 +126,7 @@ mod tests {
|
||||
#[case(0x00F0_CCAA_F556_678F, 0xCB3D_8B0E_17F5)] // K_16
|
||||
fn pc2_permutaion(#[case] before: u64, #[case] expected: u64) {
|
||||
let key56 = Key56::from(before).split();
|
||||
let result = pc2(&key56).as_int();
|
||||
let result = pc2(&key56).as_u64();
|
||||
assert_eq!(
|
||||
result, expected,
|
||||
"PC2 permutation failed. Expected {expected:016X}, got {result:016X}"
|
||||
|
||||
@ -114,7 +114,7 @@ fn encrypt_decrypt_roundtrip(
|
||||
#[case] expected_ciphertext: u64,
|
||||
#[case] key: u64,
|
||||
) {
|
||||
let des = assert_ok!(Des::new(key), "Valid DES key");
|
||||
let des = Des::new(key);
|
||||
|
||||
let ciphertext = assert_ok!(des.encrypt(&plaintext.to_be_bytes()));
|
||||
let dectrypted = assert_ok!(des.decrypt(&ciphertext));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user