1use super::tables::KPERM;
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub struct ControlWord(pub [u8; 8]);
15
16impl ControlWord {
17 pub const fn from_bytes(bytes: [u8; 8]) -> Self {
19 Self(bytes)
20 }
21
22 pub fn expand_block(&self) -> [u8; 56] {
24 let cw_u64 = u64::from_le_bytes(self.0);
25
26 let mut k = [0u64; 7];
27 k[6] = cw_u64;
28 for i in (1..=6).rev() {
29 k[i - 1] = key_permute(k[i]);
30 }
31
32 let mut sch = [0u8; 56];
33 for i in 0..7 {
34 let ki = k[i];
35 for j in 0..8 {
36 sch[i * 8 + j] = ((ki >> (j * 8)) as u8) ^ (i as u8);
37 }
38 }
39 sch
40 }
41
42 pub fn expand_stream(&self) -> [u8; 8] {
47 let mut cws = [0u8; 8];
48 for (i, out) in cws.iter_mut().enumerate() {
49 *out = self.0[i].rotate_left(4);
50 }
51 cws
52 }
53}
54
55fn key_permute(k: u64) -> u64 {
56 let bytes = k.to_le_bytes();
57 let mut result = 0u64;
58 for (i, &b) in bytes.iter().enumerate() {
59 result |= KPERM[i][b as usize];
60 }
61 result
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn nibble_swap_symmetry() {
70 let cw = ControlWord::from_bytes([0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0]);
71 let cws = cw.expand_stream();
72 assert_eq!(cws[0], 0x21);
73 assert_eq!(cws[1], 0x43);
74 assert_eq!(cws[7], 0x0f);
75 }
76
77 #[test]
78 fn vector_12_key_schedule() {
79 let cw = ControlWord::from_bytes([0x55, 0xfd, 0x78, 0x15, 0x27, 0xec, 0xa2, 0x29]);
81 let sch = cw.expand_block();
82 assert!(sch.iter().any(|&b| b != 0));
84 }
85}