use crate::{ constants::{PC1, PC2, ROUND_ROTATIONS}, key::{Key, cd56::CD56, key56::Key56, subkey::Subkey}, utils::permutate, }; use std::{ array, fmt::Debug, iter::Rev, ops::Index, slice::{Iter, IterMut}, }; /// Container for all 16 round subkeys; zeroized on drop. #[derive(Default)] pub struct Subkeys([Subkey; 16]); impl Subkeys { /// Generates 16 round subkeys from the given key. #[must_use] pub fn from_key(key: &Key) -> Self { let mut cd56 = pc1(key).split(); // 56-bit: C0 + D0 let subkeys = array::from_fn(|idx| { cd56.rotate_left(ROUND_ROTATIONS[idx]); pc2(&cd56) }); Self(subkeys) } /// Returns an iterator over the subkeys. pub fn iter(&self) -> Iter<'_, Subkey> { self.0.iter() } /// Returns a reverse iterator over the subkeys. pub fn iter_rev(&self) -> Rev> { self.0.iter().rev() } /// Returns a mutable iterator over the subkeys. pub fn iter_mut(&mut self) -> IterMut<'_, Subkey> { self.0.iter_mut() } } /// 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_u64(), 56, 48, &PC2).into() } impl<'a> IntoIterator for &'a Subkeys { type Item = &'a Subkey; type IntoIter = Iter<'a, Subkey>; fn into_iter(self) -> Self::IntoIter { self.iter() } } impl<'a> IntoIterator for &'a mut Subkeys { type Item = &'a mut Subkey; type IntoIter = IterMut<'a, Subkey>; fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } impl Index for Subkeys { type Output = Subkey; fn index(&self, index: usize) -> &Self::Output { &self.0[index] } } impl Debug for Subkeys { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("Subkeys[REDACTED]") } } #[cfg(test)] mod tests { use super::*; use rstest::rstest; const TEST_KEY: u64 = 0x1334_5779_9BBC_DFF1; #[rstest] #[case(TEST_KEY, 0x00F0_CCAA_F556_678F)] fn pc1_permutaion_correct(#[case] key: u64, #[case] expected: u64) { let result = pc1(&key.into()).as_u64(); assert_eq!( result, expected, "PC1 permutation failed. Expected {expected:08X}, got {result:08X}", ); } #[rstest] #[case(0x00F0_CCAA_F556_678F, 0xCB3D_8B0E_17F5)] // K_0 #[case(0x00E1_9955_FAAC_CF1E, 0x1B02_EFFC_7072)] // K_1 #[case(0x00C3_32AB_F559_9E3D, 0x79AE_D9DB_C9E5)] // K_2 #[case(0x000C_CAAF_F566_78F5, 0x55FC_8A42_CF99)] // K_3 #[case(0x0033_2ABF_C599_E3D5, 0x72AD_D6DB_351D)] // K_4 #[case(0x00CC_AAFF_0667_8F55, 0x7CEC_07EB_53A8)] // K_5 #[case(0x0032_ABFC_399E_3D55, 0x63A5_3E50_7B2F)] // K_6 #[case(0x00CA_AFF0_C678_F556, 0xEC84_B7F6_18BC)] // K_7 #[case(0x002A_BFC3_39E3_D559, 0xF78A_3AC1_3BFB)] // K_8 #[case(0x0055_7F86_63C7_AAB3, 0xE0DB_EBED_E781)] // K_9 #[case(0x0055_FE19_9F1E_AACC, 0xB1F3_47BA_464F)] // K_10 #[case(0x0057_F866_5C7A_AB33, 0x215F_D3DE_D386)] // K_11 #[case(0x005F_E199_51EA_ACCF, 0x7571_F594_67E9)] // K_12 #[case(0x007F_8665_57AA_B33C, 0x97C5_D1FA_BA41)] // K_13 #[case(0x00FE_1995_5EAA_CCF1, 0x5F43_B7F2_E73A)] // K_14 #[case(0x00F8_6655_7AAB_33C7, 0xBF91_8D3D_3F0A)] // K_15 #[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_u64(); assert_eq!( result, expected, "PC2 permutation failed. Expected {expected:016X}, got {result:016X}" ); } }