Skip to main content

cryptography/ciphers/
des.rs

1//! DES and Triple-DES (TDEA) implemented from FIPS PUB 46-3.
2//!
3//! All tables are transcribed verbatim from the FIPS 46-3 document
4//! (<https://csrc.nist.gov/files/pubs/fips/46-3/final/docs/fips46-3.pdf>).
5//! Tests use the official NIST CAVP Known Answer Test vectors downloaded
6//! directly from csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-
7//! Validation-Program/documents/des/KAT_TDES.zip.
8//!
9//! `Des` keeps the original fast byte-table and fused `SP_TABLE` software
10//! path. `DesCt` is a separate software-only path that replaces the secret
11//! indexed round function with loop-based permutations and packed ANF bitset
12//! evaluation of the DES S-boxes.
13
14// ─────────────────────────────────────────────────────────────────────────────
15// FIPS 46-3 Tables (1-indexed positions, converted to 0-indexed in code)
16// ─────────────────────────────────────────────────────────────────────────────
17
18/// Initial Permutation (IP) — FIPS 46-3, Table "Initial Permutation IP"
19/// Entry i gives the 1-indexed bit position in the 64-bit input whose value
20/// becomes bit i of the output (MSB = bit 1).
21const IP: [u8; 64] = [
22    58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6,
23    64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61,
24    53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7,
25];
26
27/// Final Permutation (IP⁻¹) — FIPS 46-3, Table "Inverse Initial Permutation IP⁻¹"
28const FP: [u8; 64] = [
29    40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30,
30    37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
31    34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25,
32];
33
34/// Expansion function E — FIPS 46-3, Table "Expansion Permutation E"
35/// Maps the 32-bit right half to 48 bits.
36const E: [u8; 48] = [
37    32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18,
38    19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1,
39];
40
41/// Permutation P — FIPS 46-3, Table "Permutation Function P"
42/// Applied to the 32-bit output of the 8 S-boxes.
43const P: [u8; 32] = [
44    16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19,
45    13, 30, 6, 22, 11, 4, 25,
46];
47
48/// Permuted Choice 1 (PC-1) — FIPS 46-3, Table "Permuted Choice 1 (PC-1)"
49/// Selects and permutes 56 bits of the 64-bit key (discards parity bits).
50/// First 28 entries select bits for C0, next 28 for D0.
51const PC1: [u8; 56] = [
52    57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60,
53    52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29,
54    21, 13, 5, 28, 20, 12, 4,
55];
56
57/// Permuted Choice 2 (PC-2) — FIPS 46-3, Table "Permuted Choice 2 (PC-2)"
58/// Selects 48 bits from the 56-bit shifted key halves to form each round key.
59const PC2: [u8; 48] = [
60    14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52,
61    31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32,
62];
63
64/// Key schedule rotation amounts — FIPS 46-3, Table "Number of Bit Rotations"
65/// Number of left-circular shifts applied to each key half in rounds 1–16.
66const SHIFTS: [u8; 16] = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1];
67
68/// S-boxes S1–S8 — FIPS 46-3, Tables "Selection Functions S1–S8"
69///
70/// Each S-box maps a 6-bit input to a 4-bit output.  The 6 input bits b1..b6
71/// (where b1 is MSB of the 6-bit value) select row r = (b1<<1)|b6 and
72/// column c = b2..b5.
73const SBOXES: [[u8; 64]; 8] = [
74    // S1
75    [
76        14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12,
77        11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9,
78        1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
79    ],
80    // S2
81    [
82        15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1,
83        10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15,
84        4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
85    ],
86    // S3
87    [
88        10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5,
89        14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6,
90        9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
91    ],
92    // S4
93    [
94        7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2,
95        12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1,
96        13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
97    ],
98    // S5
99    [
100        2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15,
101        10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14,
102        2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
103    ],
104    // S6
105    [
106        12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13,
107        14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5,
108        15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
109    ],
110    // S7
111    [
112        4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5,
113        12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4,
114        10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
115    ],
116    // S8
117    [
118        13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6,
119        11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10,
120        8, 13, 15, 12, 9, 0, 3, 5, 6, 11,
121    ],
122];
123
124/// Build packed ANF coefficients for `DesCt`: 64-bit monomial masks per output
125/// bit, one mask per S-box.  Runtime evaluates via subset-mask intersection and
126/// parity, avoiding secret-indexed S-box lookups entirely.
127const fn build_sbox_anf() -> [[u64; 4]; 8] {
128    let mut out = [[0u64; 4]; 8];
129    let mut sbox_idx = 0usize;
130    while sbox_idx < 8 {
131        let mut bit_idx = 0usize;
132        while bit_idx < 4 {
133            let mut coeffs = [0u8; 64];
134            let mut x = 0usize;
135            while x < 64 {
136                let row = ((x & 0x20) >> 4) | (x & 0x01);
137                let col = (x >> 1) & 0x0f;
138                coeffs[x] = (SBOXES[sbox_idx][row * 16 + col] >> bit_idx) & 1;
139                x += 1;
140            }
141
142            let mut var = 0usize;
143            while var < 6 {
144                let stride = 1usize << var;
145                let mut mask = 0usize;
146                while mask < 64 {
147                    if mask & stride != 0 {
148                        coeffs[mask] ^= coeffs[mask ^ stride];
149                    }
150                    mask += 1;
151                }
152                var += 1;
153            }
154
155            let mut packed = 0u64;
156            let mut monomial = 0usize;
157            while monomial < 64 {
158                packed |= (coeffs[monomial] as u64) << monomial;
159                monomial += 1;
160            }
161            out[sbox_idx][bit_idx] = packed;
162            bit_idx += 1;
163        }
164        sbox_idx += 1;
165    }
166    out
167}
168
169const SBOX_ANF: [[u64; 4]; 8] = build_sbox_anf();
170
171// ─────────────────────────────────────────────────────────────────────────────
172// Byte-level precomputed permutation tables (used by `Des`, not `DesCt`)
173//
174// table[byte_idx][byte_val] = that byte's contribution to the permuted output.
175// OR-ing all eight byte contributions gives the full result.
176// ─────────────────────────────────────────────────────────────────────────────
177
178const fn build_perm64(perm: &[u8; 64]) -> [[u64; 256]; 8] {
179    let mut table = [[0u64; 256]; 8];
180    let mut i = 0usize;
181    while i < 64 {
182        let src = (perm[i] - 1) as usize; // 0-indexed (0 = MSB of u64)
183        let src_byte = src / 8;
184        let src_bit = src % 8; // 0 = MSB of that byte
185        let out_bit = 63 - i;
186        let mut v = 0usize;
187        while v < 256 {
188            if (v >> (7 - src_bit)) & 1 == 1 {
189                table[src_byte][v] |= 1u64 << out_bit;
190            }
191            v += 1;
192        }
193        i += 1;
194    }
195    table
196}
197
198const fn build_perm_e(perm: &[u8; 48]) -> [[u64; 256]; 4] {
199    let mut table = [[0u64; 256]; 4];
200    let mut i = 0usize;
201    while i < 48 {
202        let src = (perm[i] - 1) as usize; // 0-indexed (0 = MSB of 32-bit R)
203        let src_byte = src / 8;
204        let src_bit = src % 8;
205        let out_bit = 47 - i;
206        let mut v = 0usize;
207        while v < 256 {
208            if (v >> (7 - src_bit)) & 1 == 1 {
209                table[src_byte][v] |= 1u64 << out_bit;
210            }
211            v += 1;
212        }
213        i += 1;
214    }
215    table
216}
217
218/// Apply the P permutation to a (possibly sparse) 32-bit S-output word.
219/// Used at compile time to build the fused S+P table.
220const fn apply_p_to_partial(s: u32) -> u32 {
221    let mut out = 0u32;
222    let mut i = 0u32;
223    while i < 32 {
224        // P[i] is the 1-indexed FIPS source bit for output FIPS bit (i+1).
225        // FIPS bit k ↔ u32 bit (32−k).
226        let src_bit = 32u32 - P[i as usize] as u32; // 0 = LSB
227        let dst_bit = 31u32 - i;
228        if (s >> src_bit) & 1 == 1 {
229            out |= 1u32 << dst_bit;
230        }
231        i += 1;
232    }
233    out
234}
235
236/// Build the fused S+P table: 8 S-boxes × 64 inputs → P-permuted u32 contribution.
237///
238/// `SP_TABLE`[i][b6] = `P(S_i(b6)` placed at bits [28−4i .. 31−4i] of the 32-bit word).
239/// Since P is a linear permutation, P(s0|s1|…|s7) = P(s0)|P(s1)|…|P(s7),
240/// so OR-ing all 8 entries gives the correct f-function output.
241/// Reduces 8 S-box + 4 P byte-table lookups per round to 8 SP lookups.
242const fn build_sp() -> [[u32; 64]; 8] {
243    let mut sp = [[0u32; 64]; 8];
244    let mut i = 0u32;
245    while i < 8 {
246        let mut j = 0usize; // raw 6-bit input b6
247        while j < 64 {
248            let row = ((j & 0x20) >> 4) | (j & 0x01); // bits 5 and 0
249            let col = (j >> 1) & 0x0F; // bits 4..1
250            let sval = SBOXES[i as usize][row * 16 + col] as u32;
251            // S-box i places its 4-bit output at u32 bits [28−4i .. 31−4i].
252            let partial = sval << (28u32 - 4 * i);
253            sp[i as usize][j] = apply_p_to_partial(partial);
254            j += 1;
255        }
256        i += 1;
257    }
258    sp
259}
260
261static IP_TABLE: [[u64; 256]; 8] = build_perm64(&IP);
262static FP_TABLE: [[u64; 256]; 8] = build_perm64(&FP);
263static E_TABLE: [[u64; 256]; 4] = build_perm_e(&E);
264static SP_TABLE: [[u32; 64]; 8] = build_sp();
265
266#[inline]
267fn fast_perm64(x: u64, t: &[[u64; 256]; 8]) -> u64 {
268    t[0][(x >> 56) as usize]
269        | t[1][((x >> 48) & 0xff) as usize]
270        | t[2][((x >> 40) & 0xff) as usize]
271        | t[3][((x >> 32) & 0xff) as usize]
272        | t[4][((x >> 24) & 0xff) as usize]
273        | t[5][((x >> 16) & 0xff) as usize]
274        | t[6][((x >> 8) & 0xff) as usize]
275        | t[7][(x & 0xff) as usize]
276}
277
278#[inline]
279fn fast_expand(r: u32, t: &[[u64; 256]; 4]) -> u64 {
280    t[0][(r >> 24) as usize]
281        | t[1][((r >> 16) & 0xff) as usize]
282        | t[2][((r >> 8) & 0xff) as usize]
283        | t[3][(r & 0xff) as usize]
284}
285
286// ─────────────────────────────────────────────────────────────────────────────
287// Bit-manipulation helpers  (used only by key_schedule — not in the hot path)
288// ─────────────────────────────────────────────────────────────────────────────
289
290/// Extract bit `pos` (1-indexed, MSB = 1) from a 64-bit big-endian block.
291#[inline]
292fn bit64(block: u64, pos: u8) -> u64 {
293    (block >> (64 - pos)) & 1
294}
295
296/// Apply a permutation table to a 64-bit block.
297/// Each entry in `table` is a 1-indexed source bit position.
298fn permute64(input: u64, table: &[u8]) -> u64 {
299    let mut out = 0u64;
300    for (i, &src) in table.iter().enumerate() {
301        out |= bit64(input, src) << (table.len() - 1 - i);
302    }
303    out
304}
305
306/// Apply a permutation table that maps a 64-bit input to a 48-bit value
307/// (returned as u64, upper 16 bits zero).
308fn permute64_to48(input: u64, table: &[u8; 48]) -> u64 {
309    let mut out = 0u64;
310    for (i, &src) in table.iter().enumerate() {
311        out |= bit64(input, src) << (47 - i);
312    }
313    out
314}
315
316#[inline]
317fn rotate_left(val: u32, n: u8, bits: u8) -> u32 {
318    let mask = (1u32 << bits) - 1;
319    ((val << n) | (val >> (bits - n))) & mask
320}
321
322// ─────────────────────────────────────────────────────────────────────────────
323// Key schedule
324// ─────────────────────────────────────────────────────────────────────────────
325
326/// A 16-round DES key schedule: 16 × 48-bit subkeys.
327pub type KeySchedule = [u64; 16];
328
329/// Generate the key schedule from a 64-bit key (including parity bits).
330/// Returns 16 subkeys, each 48 bits (stored in the low 48 bits of u64).
331///
332/// For decryption, pass the returned schedule reversed to [`des_ecb_block`].
333#[must_use]
334pub fn key_schedule(key: u64) -> KeySchedule {
335    // PC-1: select and permute 56 bits.
336    // The first 28 bits of pc1_out form C0, the next 28 bits form D0.
337    let pc1_out = permute64(key, &PC1);
338
339    let c_bytes = ((pc1_out >> 28) & 0x0FFF_FFFF).to_be_bytes();
340    let d_bytes = (pc1_out & 0x0FFF_FFFF).to_be_bytes();
341    let mut c = u32::from_be_bytes([c_bytes[4], c_bytes[5], c_bytes[6], c_bytes[7]]); // bits 1-28 → C0
342    let mut d = u32::from_be_bytes([d_bytes[4], d_bytes[5], d_bytes[6], d_bytes[7]]); // bits 29-56 → D0
343
344    let mut schedule = [0u64; 16];
345    for i in 0..16 {
346        c = rotate_left(c, SHIFTS[i], 28);
347        d = rotate_left(d, SHIFTS[i], 28);
348
349        // Merge C and D into a 56-bit value for PC-2 selection.
350        // C occupies the upper 28 bits; D the lower 28.
351        let cd: u64 = (u64::from(c) << 28) | u64::from(d);
352
353        // PC-2 references bit positions 1–56 within the 56-bit CD register.
354        // We represent CD as a 64-bit value with the 56 bits in the MSBs
355        // (i.e., shifted left by 8 so that position 1 in the FIPS table
356        //  corresponds to bit 63 of our u64).
357        let cd_shifted = cd << 8; // bit 1 of CD → bit 63 of cd_shifted
358        schedule[i] = permute64_to48(cd_shifted, &PC2);
359    }
360    schedule
361}
362
363// ─────────────────────────────────────────────────────────────────────────────
364// The Feistel f-function
365// ─────────────────────────────────────────────────────────────────────────────
366
367/// The DES f-function: f(R, K) = P(S(E(R) ⊕ K))
368fn f(r: u32, subkey: u64) -> u32 {
369    let xored = fast_expand(r, &E_TABLE) ^ subkey;
370
371    let mut result = 0u32;
372    for (i, sp_row) in SP_TABLE.iter().enumerate() {
373        let shift = 42 - 6 * i;
374        let b6 = ((xored >> shift) & 0x3F) as usize;
375        result |= sp_row[b6];
376    }
377    result
378}
379
380#[inline]
381fn subset_mask6(x: u8) -> u64 {
382    // Expand one 6-bit input into the set of all active monomials in
383    // {1, x0, x1, ..., x0x1, ...}. Bit i decides whether the current mask is
384    // duplicated with xi included. The final 64-bit value is indexed by the
385    // monomial bitmask itself.
386    let mut mask = 1u64;
387
388    let bit0 = 0u64.wrapping_sub(u64::from(x & 1));
389    mask |= (mask << 1) & bit0;
390
391    let bit1 = 0u64.wrapping_sub(u64::from((x >> 1) & 1));
392    mask |= (mask << 2) & bit1;
393
394    let bit2 = 0u64.wrapping_sub(u64::from((x >> 2) & 1));
395    mask |= (mask << 4) & bit2;
396
397    let bit3 = 0u64.wrapping_sub(u64::from((x >> 3) & 1));
398    mask |= (mask << 8) & bit3;
399
400    let bit4 = 0u64.wrapping_sub(u64::from((x >> 4) & 1));
401    mask |= (mask << 16) & bit4;
402
403    let bit5 = 0u64.wrapping_sub(u64::from((x >> 5) & 1));
404    mask |= (mask << 32) & bit5;
405
406    mask
407}
408
409#[inline]
410fn parity64(mut x: u64) -> u8 {
411    x ^= x >> 32;
412    x ^= x >> 16;
413    x ^= x >> 8;
414    x ^= x >> 4;
415    x &= 0x0f;
416    let nibble = u16::try_from(x).expect("masked parity nibble fits in u16");
417    u8::try_from((0x6996u16 >> nibble) & 1).expect("parity bit fits in u8")
418}
419
420/// Evaluate one DES S-box from the packed ANF representation.
421///
422/// `subset_mask6` expands the active input monomials for this 6-bit input, and
423/// each packed coefficient mask selects which monomials contribute to one
424/// output bit. Taking parity of the intersection is exactly "sum the selected
425/// ANF terms modulo 2".
426#[inline]
427fn sbox_ct(sbox_idx: usize, input: u8) -> u8 {
428    let active = subset_mask6(input);
429    let coeffs = &SBOX_ANF[sbox_idx];
430    parity64(active & coeffs[0])
431        | (parity64(active & coeffs[1]) << 1)
432        | (parity64(active & coeffs[2]) << 2)
433        | (parity64(active & coeffs[3]) << 3)
434}
435
436/// Constant-time DES f-function: same E / XOR-K / S / P steps as the fast
437/// path, but S-boxes are evaluated via ANF and permutations via fixed loops
438/// rather than secret-indexed byte tables.
439fn f_ct(r: u32, subkey: u64) -> u32 {
440    let mut expanded = 0u64;
441    for (i, &src) in E.iter().enumerate() {
442        let bit = u64::from((r >> (32 - src)) & 1);
443        expanded |= bit << (47 - i);
444    }
445    let xored = expanded ^ subkey;
446
447    let mut pre_p = 0u32;
448    for i in 0..8usize {
449        let shift = 42 - 6 * i;
450        let b6 = ((xored >> shift) & 0x3f) as u8;
451        let sval = u32::from(sbox_ct(i, b6));
452        pre_p |= sval << (28 - 4 * i);
453    }
454
455    apply_p_to_partial(pre_p)
456}
457
458// ─────────────────────────────────────────────────────────────────────────────
459// DES single-block encrypt/decrypt
460// ─────────────────────────────────────────────────────────────────────────────
461
462/// Encrypt or decrypt a single 64-bit block under the given key schedule.
463///
464/// For encryption, pass `schedule` from [`key_schedule`].
465/// For decryption, pass the schedule reversed: `let dec = { let mut s = ks; s.reverse(); s }`.
466fn des_block(block: u64, schedule: &KeySchedule) -> u64 {
467    let permuted = fast_perm64(block, &IP_TABLE);
468
469    let mut l = u32::try_from((permuted >> 32) & 0xFFFF_FFFF).expect("upper DES half fits in u32");
470    let mut r = u32::try_from(permuted & 0xFFFF_FFFF).expect("lower DES half fits in u32");
471
472    for &subkey in schedule {
473        let tmp = r;
474        r = l ^ f(r, subkey);
475        l = tmp;
476    }
477
478    // Pre-output: swap L and R, then apply FP via precomputed byte-table.
479    let pre_output = (u64::from(r) << 32) | u64::from(l);
480    fast_perm64(pre_output, &FP_TABLE)
481}
482
483fn des_block_ct(block: u64, schedule: &KeySchedule) -> u64 {
484    let permuted = permute64(block, &IP);
485
486    let mut l = u32::try_from((permuted >> 32) & 0xFFFF_FFFF).expect("upper DES half fits in u32");
487    let mut r = u32::try_from(permuted & 0xFFFF_FFFF).expect("lower DES half fits in u32");
488
489    for &subkey in schedule {
490        let tmp = r;
491        r = l ^ f_ct(r, subkey);
492        l = tmp;
493    }
494
495    let pre_output = (u64::from(r) << 32) | u64::from(l);
496    permute64(pre_output, &FP)
497}
498
499// ─────────────────────────────────────────────────────────────────────────────
500// Public DES interface
501// ─────────────────────────────────────────────────────────────────────────────
502
503/// A DES cipher keyed with a single 64-bit key (including parity bits).
504pub struct Des {
505    enc_schedule: KeySchedule,
506    dec_schedule: KeySchedule,
507}
508
509/// A software-only constant-time DES path.
510///
511/// `DesCt` avoids the fast path's secret-indexed permutation and S-box tables.
512/// Instead it keeps the same key schedule but evaluates IP/FP/E with fixed
513/// loops and evaluates each S-box through the packed ANF bitset form above.
514pub struct DesCt {
515    enc_schedule: KeySchedule,
516    dec_schedule: KeySchedule,
517}
518
519/// Error returned when a DES/TDEA constructor rejects key material.
520#[derive(Debug, Clone, Copy, PartialEq, Eq)]
521pub enum DesKeyError {
522    /// The provided DES key is weak or semi-weak (FIPS 74 / SP 800-67).
523    WeakOrSemiWeakKey,
524}
525
526/// Canonical weak DES keys from FIPS 74 / NIST literature.
527const WEAK_KEYS: [[u8; 8]; 4] = [
528    [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01],
529    [0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE],
530    [0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1],
531    [0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E],
532];
533
534/// Canonical semi-weak DES key pairs from FIPS 74 / NIST literature.
535const SEMI_WEAK_KEY_PAIRS: [([u8; 8], [u8; 8]); 6] = [
536    (
537        [0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE],
538        [0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01],
539    ),
540    (
541        [0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1],
542        [0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E],
543    ),
544    (
545        [0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1],
546        [0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01],
547    ),
548    (
549        [0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE],
550        [0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E],
551    ),
552    (
553        [0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E],
554        [0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01],
555    ),
556    (
557        [0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE],
558        [0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1],
559    ),
560];
561
562#[inline]
563fn strip_parity_bits(key: &[u8; 8]) -> [u8; 8] {
564    let mut out = [0u8; 8];
565    for i in 0..8 {
566        out[i] = key[i] & 0xFE;
567    }
568    out
569}
570
571/// Return `true` when `key` is weak or semi-weak under DES.
572#[must_use]
573pub fn is_weak_or_semi_weak_key(key: &[u8; 8]) -> bool {
574    let normalized = strip_parity_bits(key);
575    WEAK_KEYS
576        .iter()
577        .any(|wk| strip_parity_bits(wk) == normalized)
578        || SEMI_WEAK_KEY_PAIRS.iter().any(|(a, b)| {
579            let a_norm = strip_parity_bits(a);
580            let b_norm = strip_parity_bits(b);
581            normalized == a_norm || normalized == b_norm
582        })
583}
584
585impl Des {
586    /// Create a new DES instance from an 8-byte key.
587    pub fn new(key: &[u8; 8]) -> Result<Self, DesKeyError> {
588        if is_weak_or_semi_weak_key(key) {
589            return Err(DesKeyError::WeakOrSemiWeakKey);
590        }
591        Ok(Self::new_unchecked(key))
592    }
593
594    /// Create DES from an 8-byte key without weak-key screening.
595    ///
596    /// This is intended for known-answer tests that intentionally exercise weak
597    /// key behavior from FIPS 74 style vector sets.
598    #[must_use]
599    pub fn new_unchecked(key: &[u8; 8]) -> Self {
600        let k = u64::from_be_bytes(*key);
601        let enc_schedule = key_schedule(k);
602        let mut dec_schedule = enc_schedule;
603        dec_schedule.reverse();
604        Des {
605            enc_schedule,
606            dec_schedule,
607        }
608    }
609
610    /// Create a new DES instance and wipe the provided key buffer.
611    pub fn new_wiping(key: &mut [u8; 8]) -> Result<Self, DesKeyError> {
612        let out = Self::new(key);
613        crate::ct::zeroize_slice(key.as_mut_slice());
614        out
615    }
616
617    /// Encrypt a single 64-bit block (ECB mode).
618    #[must_use]
619    pub fn encrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
620        let b = u64::from_be_bytes(*block);
621        des_block(b, &self.enc_schedule).to_be_bytes()
622    }
623
624    /// Decrypt a single 64-bit block (ECB mode).
625    #[must_use]
626    pub fn decrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
627        let b = u64::from_be_bytes(*block);
628        des_block(b, &self.dec_schedule).to_be_bytes()
629    }
630}
631
632impl DesCt {
633    /// Create a new constant-time DES instance from an 8-byte key.
634    pub fn new(key: &[u8; 8]) -> Result<Self, DesKeyError> {
635        if is_weak_or_semi_weak_key(key) {
636            return Err(DesKeyError::WeakOrSemiWeakKey);
637        }
638        Ok(Self::new_unchecked(key))
639    }
640
641    /// Create constant-time DES from an 8-byte key without weak-key screening.
642    #[must_use]
643    pub fn new_unchecked(key: &[u8; 8]) -> Self {
644        let k = u64::from_be_bytes(*key);
645        let enc_schedule = key_schedule(k);
646        let mut dec_schedule = enc_schedule;
647        dec_schedule.reverse();
648        DesCt {
649            enc_schedule,
650            dec_schedule,
651        }
652    }
653
654    /// Create a new constant-time DES instance and wipe the provided key buffer.
655    pub fn new_wiping(key: &mut [u8; 8]) -> Result<Self, DesKeyError> {
656        let out = Self::new(key);
657        crate::ct::zeroize_slice(key.as_mut_slice());
658        out
659    }
660
661    /// Encrypt a single 64-bit block (ECB mode).
662    #[must_use]
663    pub fn encrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
664        let b = u64::from_be_bytes(*block);
665        des_block_ct(b, &self.enc_schedule).to_be_bytes()
666    }
667
668    /// Decrypt a single 64-bit block (ECB mode).
669    #[must_use]
670    pub fn decrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
671        let b = u64::from_be_bytes(*block);
672        des_block_ct(b, &self.dec_schedule).to_be_bytes()
673    }
674}
675
676// ─────────────────────────────────────────────────────────────────────────────
677// Triple-DES (TDEA) — FIPS 46-3 §4, NIST SP 800-67
678// ─────────────────────────────────────────────────────────────────────────────
679//
680// TDEA operates as EDE (Encrypt-Decrypt-Encrypt):
681//   Encrypt:  C = E(K3, D(K2, E(K1, P)))
682//   Decrypt:  P = D(K1, E(K2, D(K3, C)))
683//
684// Key sizes and keying options (NIST SP 800-67 §3.1):
685//   Option 1 (3TDEA): K1, K2, K3 all independent — 168-bit key material
686//                     (112-bit effective security)
687//   Option 2 (2TDEA): K1 = K3 ≠ K2           — 112-bit key material
688//                     (80-bit effective security)
689//   Option 3:         K1 = K2 = K3            — degenerates to single DES
690//                     (NOT recommended for new applications)
691//
692// The NIST CAVP test vectors use "KEYs = <hex>" meaning K1=K2=K3=that value,
693// which exercises the EDE path with a single key and validates DES-equivalent
694// behaviour (E(K,D(K,E(K,P))) = E(K,P) since D∘E = identity on same key).
695
696/// Keying option for Triple-DES.
697#[derive(Debug, Clone, Copy, PartialEq, Eq)]
698pub enum TDesMode {
699    /// Keying option 1: K1, K2, K3 all independent (24-byte / 192-bit key).
700    ThreeKey,
701    /// Keying option 2: K1 = K3 ≠ K2 (16-byte / 128-bit key: K1 ∥ K2).
702    TwoKey,
703}
704
705/// A Triple-DES (TDEA) cipher.
706pub struct TripleDes {
707    k1_enc: KeySchedule,
708    k1_dec: KeySchedule,
709    k2_enc: KeySchedule,
710    k2_dec: KeySchedule,
711    k3_enc: KeySchedule,
712    k3_dec: KeySchedule,
713}
714
715impl TripleDes {
716    /// Construct a 3TDEA instance from a 24-byte key K1 ∥ K2 ∥ K3.
717    ///
718    /// # Panics
719    ///
720    /// Panics if the internal fixed-size key splits fail, which would only
721    /// happen if this constructor were changed inconsistently with its type.
722    pub fn new_3key(key: &[u8; 24]) -> Result<Self, DesKeyError> {
723        let k1: &[u8; 8] = key[0..8].try_into().expect("first DES key split");
724        let k2: &[u8; 8] = key[8..16].try_into().expect("second DES key split");
725        let k3: &[u8; 8] = key[16..24].try_into().expect("third DES key split");
726        if is_weak_or_semi_weak_key(k1)
727            || is_weak_or_semi_weak_key(k2)
728            || is_weak_or_semi_weak_key(k3)
729        {
730            return Err(DesKeyError::WeakOrSemiWeakKey);
731        }
732        Ok(Self::from_keys(
733            u64::from_be_bytes(*k1),
734            u64::from_be_bytes(*k2),
735            u64::from_be_bytes(*k3),
736        ))
737    }
738
739    /// Construct a 3TDEA instance and wipe the provided key buffer.
740    pub fn new_3key_wiping(key: &mut [u8; 24]) -> Result<Self, DesKeyError> {
741        let out = Self::new_3key(key);
742        crate::ct::zeroize_slice(key.as_mut_slice());
743        out
744    }
745
746    /// Construct a 2TDEA instance from a 16-byte key K1 ∥ K2 (K3 = K1).
747    ///
748    /// # Panics
749    ///
750    /// Panics if the internal fixed-size key splits fail, which would only
751    /// happen if this constructor were changed inconsistently with its type.
752    pub fn new_2key(key: &[u8; 16]) -> Result<Self, DesKeyError> {
753        let k1: &[u8; 8] = key[0..8].try_into().expect("first DES key split");
754        let k2: &[u8; 8] = key[8..16].try_into().expect("second DES key split");
755        if is_weak_or_semi_weak_key(k1) || is_weak_or_semi_weak_key(k2) {
756            return Err(DesKeyError::WeakOrSemiWeakKey);
757        }
758        Ok(Self::from_keys(
759            u64::from_be_bytes(*k1),
760            u64::from_be_bytes(*k2),
761            u64::from_be_bytes(*k1),
762        ))
763    }
764
765    /// Construct a 2TDEA instance and wipe the provided key buffer.
766    pub fn new_2key_wiping(key: &mut [u8; 16]) -> Result<Self, DesKeyError> {
767        let out = Self::new_2key(key);
768        crate::ct::zeroize_slice(key.as_mut_slice());
769        out
770    }
771
772    /// Construct from three 8-byte keys.  K1=K2=K3 is valid (degenerates to
773    /// single DES) and is used by the NIST CAVP "KEYs" tests.
774    pub fn new_single_key(key: &[u8; 8]) -> Result<Self, DesKeyError> {
775        if is_weak_or_semi_weak_key(key) {
776            return Err(DesKeyError::WeakOrSemiWeakKey);
777        }
778        Ok(Self::new_single_key_unchecked(key))
779    }
780
781    /// Construct from one DES key used as K1 = K2 = K3 without weak-key checks.
782    #[must_use]
783    pub fn new_single_key_unchecked(key: &[u8; 8]) -> Self {
784        let k = u64::from_be_bytes(*key);
785        Self::from_keys(k, k, k)
786    }
787
788    /// Construct from one DES key used as K1 = K2 = K3 and wipe the buffer.
789    pub fn new_single_key_wiping(key: &mut [u8; 8]) -> Result<Self, DesKeyError> {
790        let out = Self::new_single_key(key);
791        crate::ct::zeroize_slice(key.as_mut_slice());
792        out
793    }
794
795    fn from_keys(k1: u64, k2: u64, k3: u64) -> Self {
796        let k1_enc = key_schedule(k1);
797        let k2_enc = key_schedule(k2);
798        let k3_enc = key_schedule(k3);
799        let mut k1_dec = k1_enc;
800        let mut k2_dec = k2_enc;
801        let mut k3_dec = k3_enc;
802        k1_dec.reverse();
803        k2_dec.reverse();
804        k3_dec.reverse();
805        TripleDes {
806            k1_enc,
807            k1_dec,
808            k2_enc,
809            k2_dec,
810            k3_enc,
811            k3_dec,
812        }
813    }
814
815    /// Encrypt a single 64-bit block: C = E(K3, D(K2, E(K1, P)))
816    #[must_use]
817    pub fn encrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
818        let p = u64::from_be_bytes(*block);
819        let t1 = des_block(p, &self.k1_enc); // E with K1
820        let t2 = des_block(t1, &self.k2_dec); // D with K2
821        let c = des_block(t2, &self.k3_enc); // E with K3
822        c.to_be_bytes()
823    }
824
825    /// Decrypt a single 64-bit block: P = D(K1, E(K2, D(K3, C)))
826    #[must_use]
827    pub fn decrypt_block(&self, block: &[u8; 8]) -> [u8; 8] {
828        let c = u64::from_be_bytes(*block);
829        let t1 = des_block(c, &self.k3_dec); // D with K3
830        let t2 = des_block(t1, &self.k2_enc); // E with K2
831        let p = des_block(t2, &self.k1_dec); // D with K1
832        p.to_be_bytes()
833    }
834}
835
836// ─────────────────────────────────────────────────────────────────────────────
837// BlockCipher trait implementations
838// ─────────────────────────────────────────────────────────────────────────────
839
840impl crate::BlockCipher for Des {
841    const BLOCK_LEN: usize = 8;
842    fn encrypt(&self, block: &mut [u8]) {
843        let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
844        block.copy_from_slice(&self.encrypt_block(arr));
845    }
846    fn decrypt(&self, block: &mut [u8]) {
847        let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
848        block.copy_from_slice(&self.decrypt_block(arr));
849    }
850}
851
852impl crate::BlockCipher for DesCt {
853    const BLOCK_LEN: usize = 8;
854    fn encrypt(&self, block: &mut [u8]) {
855        let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
856        block.copy_from_slice(&self.encrypt_block(arr));
857    }
858    fn decrypt(&self, block: &mut [u8]) {
859        let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
860        block.copy_from_slice(&self.decrypt_block(arr));
861    }
862}
863
864impl crate::BlockCipher for TripleDes {
865    const BLOCK_LEN: usize = 8;
866    fn encrypt(&self, block: &mut [u8]) {
867        let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
868        block.copy_from_slice(&self.encrypt_block(arr));
869    }
870    fn decrypt(&self, block: &mut [u8]) {
871        let arr: &[u8; 8] = (&*block).try_into().expect("wrong block length");
872        block.copy_from_slice(&self.decrypt_block(arr));
873    }
874}
875
876impl Drop for Des {
877    fn drop(&mut self) {
878        // DES instances retain both schedules for repeated ECB calls.
879        crate::ct::zeroize_slice(self.enc_schedule.as_mut_slice());
880        crate::ct::zeroize_slice(self.dec_schedule.as_mut_slice());
881    }
882}
883
884impl Drop for DesCt {
885    fn drop(&mut self) {
886        crate::ct::zeroize_slice(self.enc_schedule.as_mut_slice());
887        crate::ct::zeroize_slice(self.dec_schedule.as_mut_slice());
888    }
889}
890
891impl Drop for TripleDes {
892    fn drop(&mut self) {
893        // TDEA keeps six schedules resident; wipe all of them on drop.
894        crate::ct::zeroize_slice(self.k1_enc.as_mut_slice());
895        crate::ct::zeroize_slice(self.k1_dec.as_mut_slice());
896        crate::ct::zeroize_slice(self.k2_enc.as_mut_slice());
897        crate::ct::zeroize_slice(self.k2_dec.as_mut_slice());
898        crate::ct::zeroize_slice(self.k3_enc.as_mut_slice());
899        crate::ct::zeroize_slice(self.k3_dec.as_mut_slice());
900    }
901}
902
903// ─────────────────────────────────────────────────────────────────────────────
904// Tests — all vectors from NIST CAVP KAT_TDES.zip (csrc.nist.gov)
905// ─────────────────────────────────────────────────────────────────────────────
906
907#[cfg(test)]
908mod tests {
909    use super::*;
910
911    // ── helpers ──────────────────────────────────────────────────────────────
912
913    fn from_hex(s: &str) -> u64 {
914        u64::from_str_radix(s, 16).unwrap()
915    }
916
917    fn hex_to_bytes8(s: &str) -> [u8; 8] {
918        from_hex(s).to_be_bytes()
919    }
920
921    fn hex_to_bytes24(s: &str) -> [u8; 24] {
922        let mut out = [0u8; 24];
923        for (idx, chunk) in s.as_bytes().chunks_exact(2).enumerate() {
924            let hi = (chunk[0] as char).to_digit(16).unwrap();
925            let lo = (chunk[1] as char).to_digit(16).unwrap();
926            out[idx] = u8::try_from((hi << 4) | lo).expect("decoded hex byte fits in u8");
927        }
928        out
929    }
930
931    /// Run a single NIST CAVP TDES ECB test vector using the TDES EDE path
932    /// (K1=K2=K3 per the "KEYs" notation in the .rsp files).
933    fn tdes_kat(key_hex: &str, pt_hex: &str, ct_hex: &str) {
934        let key = hex_to_bytes8(key_hex);
935        let pt = hex_to_bytes8(pt_hex);
936        let ct = hex_to_bytes8(ct_hex);
937        let cipher = TripleDes::new_single_key_unchecked(&key);
938        assert_eq!(
939            cipher.encrypt_block(&pt),
940            ct,
941            "encrypt mismatch: key={key_hex} pt={pt_hex}"
942        );
943        assert_eq!(
944            cipher.decrypt_block(&ct),
945            pt,
946            "decrypt mismatch: key={key_hex} ct={ct_hex}"
947        );
948    }
949
950    /// Run a DES ECB test using the single-key DES path.
951    fn des_kat(key_hex: &str, pt_hex: &str, ct_hex: &str) {
952        let key = hex_to_bytes8(key_hex);
953        let pt = hex_to_bytes8(pt_hex);
954        let ct = hex_to_bytes8(ct_hex);
955        let cipher = Des::new_unchecked(&key);
956        assert_eq!(
957            cipher.encrypt_block(&pt),
958            ct,
959            "encrypt mismatch: key={key_hex} pt={pt_hex}"
960        );
961        assert_eq!(
962            cipher.decrypt_block(&ct),
963            pt,
964            "decrypt mismatch: key={key_hex} ct={ct_hex}"
965        );
966    }
967
968    fn des_ct_kat(key_hex: &str, pt_hex: &str, ct_hex: &str) {
969        let key = hex_to_bytes8(key_hex);
970        let pt = hex_to_bytes8(pt_hex);
971        let ct = hex_to_bytes8(ct_hex);
972        let fast = Des::new_unchecked(&key);
973        let slow = DesCt::new_unchecked(&key);
974        assert_eq!(
975            slow.encrypt_block(&pt),
976            ct,
977            "encrypt mismatch: key={key_hex} pt={pt_hex}"
978        );
979        assert_eq!(
980            slow.decrypt_block(&ct),
981            pt,
982            "decrypt mismatch: key={key_hex} ct={ct_hex}"
983        );
984        assert_eq!(
985            slow.encrypt_block(&pt),
986            fast.encrypt_block(&pt),
987            "DesCt must match Des for key={key_hex} pt={pt_hex}"
988        );
989    }
990
991    // ── TECBvartext.rsp — Variable Plaintext KAT ─────────────────────────────
992    // Key is all-ones parity (0x0101010101010101, all actual key bits = 0).
993    // From NIST CAVP KAT_TDES/TECBvartext.rsp (CAVS 11.1, 2011-04-21).
994
995    #[test]
996    fn vartext_all_64() {
997        // Full 64-vector known-answer test for the variable-plaintext category.
998        let cases: &[(&str, &str, &str)] = &[
999            ("0101010101010101", "8000000000000000", "95f8a5e5dd31d900"),
1000            ("0101010101010101", "4000000000000000", "dd7f121ca5015619"),
1001            ("0101010101010101", "2000000000000000", "2e8653104f3834ea"),
1002            ("0101010101010101", "1000000000000000", "4bd388ff6cd81d4f"),
1003            ("0101010101010101", "0800000000000000", "20b9e767b2fb1456"),
1004            ("0101010101010101", "0400000000000000", "55579380d77138ef"),
1005            ("0101010101010101", "0200000000000000", "6cc5defaaf04512f"),
1006            ("0101010101010101", "0100000000000000", "0d9f279ba5d87260"),
1007            ("0101010101010101", "0080000000000000", "d9031b0271bd5a0a"),
1008            ("0101010101010101", "0040000000000000", "424250b37c3dd951"),
1009            ("0101010101010101", "0020000000000000", "b8061b7ecd9a21e5"),
1010            ("0101010101010101", "0010000000000000", "f15d0f286b65bd28"),
1011            ("0101010101010101", "0008000000000000", "add0cc8d6e5deba1"),
1012            ("0101010101010101", "0004000000000000", "e6d5f82752ad63d1"),
1013            ("0101010101010101", "0002000000000000", "ecbfe3bd3f591a5e"),
1014            ("0101010101010101", "0001000000000000", "f356834379d165cd"),
1015            ("0101010101010101", "0000800000000000", "2b9f982f20037fa9"),
1016            ("0101010101010101", "0000400000000000", "889de068a16f0be6"),
1017            ("0101010101010101", "0000200000000000", "e19e275d846a1298"),
1018            ("0101010101010101", "0000100000000000", "329a8ed523d71aec"),
1019            ("0101010101010101", "0000080000000000", "e7fce22557d23c97"),
1020            ("0101010101010101", "0000040000000000", "12a9f5817ff2d65d"),
1021            ("0101010101010101", "0000020000000000", "a484c3ad38dc9c19"),
1022            ("0101010101010101", "0000010000000000", "fbe00a8a1ef8ad72"),
1023            ("0101010101010101", "0000008000000000", "750d079407521363"),
1024            ("0101010101010101", "0000004000000000", "64feed9c724c2faf"),
1025            ("0101010101010101", "0000002000000000", "f02b263b328e2b60"),
1026            ("0101010101010101", "0000001000000000", "9d64555a9a10b852"),
1027            ("0101010101010101", "0000000800000000", "d106ff0bed5255d7"),
1028            ("0101010101010101", "0000000400000000", "e1652c6b138c64a5"),
1029            ("0101010101010101", "0000000200000000", "e428581186ec8f46"),
1030            ("0101010101010101", "0000000100000000", "aeb5f5ede22d1a36"),
1031            ("0101010101010101", "0000000080000000", "e943d7568aec0c5c"),
1032            ("0101010101010101", "0000000040000000", "df98c8276f54b04b"),
1033            ("0101010101010101", "0000000020000000", "b160e4680f6c696f"),
1034            ("0101010101010101", "0000000010000000", "fa0752b07d9c4ab8"),
1035            ("0101010101010101", "0000000008000000", "ca3a2b036dbc8502"),
1036            ("0101010101010101", "0000000004000000", "5e0905517bb59bcf"),
1037            ("0101010101010101", "0000000002000000", "814eeb3b91d90726"),
1038            ("0101010101010101", "0000000001000000", "4d49db1532919c9f"),
1039            ("0101010101010101", "0000000000800000", "25eb5fc3f8cf0621"),
1040            ("0101010101010101", "0000000000400000", "ab6a20c0620d1c6f"),
1041            ("0101010101010101", "0000000000200000", "79e90dbc98f92cca"),
1042            ("0101010101010101", "0000000000100000", "866ecedd8072bb0e"),
1043            ("0101010101010101", "0000000000080000", "8b54536f2f3e64a8"),
1044            ("0101010101010101", "0000000000040000", "ea51d3975595b86b"),
1045            ("0101010101010101", "0000000000020000", "caffc6ac4542de31"),
1046            ("0101010101010101", "0000000000010000", "8dd45a2ddf90796c"),
1047            ("0101010101010101", "0000000000008000", "1029d55e880ec2d0"),
1048            ("0101010101010101", "0000000000004000", "5d86cb23639dbea9"),
1049            ("0101010101010101", "0000000000002000", "1d1ca853ae7c0c5f"),
1050            ("0101010101010101", "0000000000001000", "ce332329248f3228"),
1051            ("0101010101010101", "0000000000000800", "8405d1abe24fb942"),
1052            ("0101010101010101", "0000000000000400", "e643d78090ca4207"),
1053            ("0101010101010101", "0000000000000200", "48221b9937748a23"),
1054            ("0101010101010101", "0000000000000100", "dd7c0bbd61fafd54"),
1055            ("0101010101010101", "0000000000000080", "2fbc291a570db5c4"),
1056            ("0101010101010101", "0000000000000040", "e07c30d7e4e26e12"),
1057            ("0101010101010101", "0000000000000020", "0953e2258e8e90a1"),
1058            ("0101010101010101", "0000000000000010", "5b711bc4ceebf2ee"),
1059            ("0101010101010101", "0000000000000008", "cc083f1e6d9e85f6"),
1060            ("0101010101010101", "0000000000000004", "d2fd8867d50d2dfe"),
1061            ("0101010101010101", "0000000000000002", "06e7ea22ce92708f"),
1062            ("0101010101010101", "0000000000000001", "166b40b44aba4bd6"),
1063        ];
1064        for (k, pt, ct) in cases {
1065            tdes_kat(k, pt, ct);
1066        }
1067    }
1068
1069    // ── TECBinvperm.rsp — Inverse Permutation KAT ───────────────────────────
1070    // Same vectors as vartext but with plaintext and ciphertext swapped:
1071    // encrypting the ciphertext should reproduce the plaintext (tests IP⁻¹).
1072
1073    #[test]
1074    fn invperm_sample() {
1075        let cases = [
1076            ("0101010101010101", "95f8a5e5dd31d900", "8000000000000000"),
1077            ("0101010101010101", "dd7f121ca5015619", "4000000000000000"),
1078            ("0101010101010101", "166b40b44aba4bd6", "0000000000000001"),
1079        ];
1080        for (k, pt, ct) in cases {
1081            tdes_kat(k, pt, ct);
1082        }
1083    }
1084
1085    // ── TECBvarkey.rsp — Variable Key KAT ───────────────────────────────────
1086    // Plaintext = 0x0000000000000000, each key has exactly one real key bit set.
1087
1088    #[test]
1089    fn varkey_all_56() {
1090        let cases: &[(&str, &str, &str)] = &[
1091            ("8001010101010101", "0000000000000000", "95a8d72813daa94d"),
1092            ("4001010101010101", "0000000000000000", "0eec1487dd8c26d5"),
1093            ("2001010101010101", "0000000000000000", "7ad16ffb79c45926"),
1094            ("1001010101010101", "0000000000000000", "d3746294ca6a6cf3"),
1095            ("0801010101010101", "0000000000000000", "809f5f873c1fd761"),
1096            ("0401010101010101", "0000000000000000", "c02faffec989d1fc"),
1097            ("0201010101010101", "0000000000000000", "4615aa1d33e72f10"),
1098            ("0180010101010101", "0000000000000000", "2055123350c00858"),
1099            ("0140010101010101", "0000000000000000", "df3b99d6577397c8"),
1100            ("0120010101010101", "0000000000000000", "31fe17369b5288c9"),
1101            ("0110010101010101", "0000000000000000", "dfdd3cc64dae1642"),
1102            ("0108010101010101", "0000000000000000", "178c83ce2b399d94"),
1103            ("0104010101010101", "0000000000000000", "50f636324a9b7f80"),
1104            ("0102010101010101", "0000000000000000", "a8468ee3bc18f06d"),
1105            ("0101800101010101", "0000000000000000", "a2dc9e92fd3cde92"),
1106            ("0101400101010101", "0000000000000000", "cac09f797d031287"),
1107            ("0101200101010101", "0000000000000000", "90ba680b22aeb525"),
1108            ("0101100101010101", "0000000000000000", "ce7a24f350e280b6"),
1109            ("0101080101010101", "0000000000000000", "882bff0aa01a0b87"),
1110            ("0101040101010101", "0000000000000000", "25610288924511c2"),
1111            ("0101020101010101", "0000000000000000", "c71516c29c75d170"),
1112            ("0101018001010101", "0000000000000000", "5199c29a52c9f059"),
1113            ("0101014001010101", "0000000000000000", "c22f0a294a71f29f"),
1114            ("0101012001010101", "0000000000000000", "ee371483714c02ea"),
1115            ("0101011001010101", "0000000000000000", "a81fbd448f9e522f"),
1116            ("0101010801010101", "0000000000000000", "4f644c92e192dfed"),
1117            ("0101010401010101", "0000000000000000", "1afa9a66a6df92ae"),
1118            ("0101010201010101", "0000000000000000", "b3c1cc715cb879d8"),
1119            ("0101010180010101", "0000000000000000", "19d032e64ab0bd8b"),
1120            ("0101010140010101", "0000000000000000", "3cfaa7a7dc8720dc"),
1121            ("0101010120010101", "0000000000000000", "b7265f7f447ac6f3"),
1122            ("0101010110010101", "0000000000000000", "9db73b3c0d163f54"),
1123            ("0101010108010101", "0000000000000000", "8181b65babf4a975"),
1124            ("0101010104010101", "0000000000000000", "93c9b64042eaa240"),
1125            ("0101010102010101", "0000000000000000", "5570530829705592"),
1126            ("0101010101800101", "0000000000000000", "8638809e878787a0"),
1127            ("0101010101400101", "0000000000000000", "41b9a79af79ac208"),
1128            ("0101010101200101", "0000000000000000", "7a9be42f2009a892"),
1129            ("0101010101100101", "0000000000000000", "29038d56ba6d2745"),
1130            ("0101010101080101", "0000000000000000", "5495c6abf1e5df51"),
1131            ("0101010101040101", "0000000000000000", "ae13dbd561488933"),
1132            ("0101010101020101", "0000000000000000", "024d1ffa8904e389"),
1133            ("0101010101018001", "0000000000000000", "d1399712f99bf02e"),
1134            ("0101010101014001", "0000000000000000", "14c1d7c1cffec79e"),
1135            ("0101010101012001", "0000000000000000", "1de5279dae3bed6f"),
1136            ("0101010101011001", "0000000000000000", "e941a33f85501303"),
1137            ("0101010101010801", "0000000000000000", "da99dbbc9a03f379"),
1138            ("0101010101010401", "0000000000000000", "b7fc92f91d8e92e9"),
1139            ("0101010101010201", "0000000000000000", "ae8e5caa3ca04e85"),
1140            ("0101010101010180", "0000000000000000", "9cc62df43b6eed74"),
1141            ("0101010101010140", "0000000000000000", "d863dbb5c59a91a0"),
1142            ("0101010101010120", "0000000000000000", "a1ab2190545b91d7"),
1143            ("0101010101010110", "0000000000000000", "0875041e64c570f7"),
1144            ("0101010101010108", "0000000000000000", "5a594528bebef1cc"),
1145            ("0101010101010104", "0000000000000000", "fcdb3291de21f0c0"),
1146            ("0101010101010102", "0000000000000000", "869efd7f9f265a09"),
1147        ];
1148        for (k, pt, ct) in cases {
1149            tdes_kat(k, pt, ct);
1150        }
1151    }
1152
1153    // ── TECBpermop.rsp — Permutation Operation KAT ──────────────────────────
1154    // Tests the P permutation and S-box interaction.
1155
1156    #[test]
1157    fn permop_all_32() {
1158        let cases: &[(&str, &str, &str)] = &[
1159            ("1046913489980131", "0000000000000000", "88d55e54f54c97b4"),
1160            ("1007103489988020", "0000000000000000", "0c0cc00c83ea48fd"),
1161            ("10071034c8980120", "0000000000000000", "83bc8ef3a6570183"),
1162            ("1046103489988020", "0000000000000000", "df725dcad94ea2e9"),
1163            ("1086911519190101", "0000000000000000", "e652b53b550be8b0"),
1164            ("1086911519580101", "0000000000000000", "af527120c485cbb0"),
1165            ("5107b01519580101", "0000000000000000", "0f04ce393db926d5"),
1166            ("1007b01519190101", "0000000000000000", "c9f00ffc74079067"),
1167            ("3107915498080101", "0000000000000000", "7cfd82a593252b4e"),
1168            ("3107919498080101", "0000000000000000", "cb49a2f9e91363e3"),
1169            ("10079115b9080140", "0000000000000000", "00b588be70d23f56"),
1170            ("3107911598080140", "0000000000000000", "406a9a6ab43399ae"),
1171            ("1007d01589980101", "0000000000000000", "6cb773611dca9ada"),
1172            ("9107911589980101", "0000000000000000", "67fd21c17dbb5d70"),
1173            ("9107d01589190101", "0000000000000000", "9592cb4110430787"),
1174            ("1007d01598980120", "0000000000000000", "a6b7ff68a318ddd3"),
1175            ("1007940498190101", "0000000000000000", "4d102196c914ca16"),
1176            ("0107910491190401", "0000000000000000", "2dfa9f4573594965"),
1177            ("0107910491190101", "0000000000000000", "b46604816c0e0774"),
1178            ("0107940491190401", "0000000000000000", "6e7e6221a4f34e87"),
1179            ("19079210981a0101", "0000000000000000", "aa85e74643233199"),
1180            ("1007911998190801", "0000000000000000", "2e5a19db4d1962d6"),
1181            ("10079119981a0801", "0000000000000000", "23a866a809d30894"),
1182            ("1007921098190101", "0000000000000000", "d812d961f017d320"),
1183            ("100791159819010b", "0000000000000000", "055605816e58608f"),
1184            ("1004801598190101", "0000000000000000", "abd88e8b1b7716f1"),
1185            ("1004801598190102", "0000000000000000", "537ac95be69da1e1"),
1186            ("1004801598190108", "0000000000000000", "aed0f6ae3c25cdd8"),
1187            ("1002911498100104", "0000000000000000", "b3e35a5ee53e7b8d"),
1188            ("1002911598190104", "0000000000000000", "61c79c71921a2ef8"),
1189            ("1002911598100201", "0000000000000000", "e2f5728f0995013c"),
1190            ("1002911698100101", "0000000000000000", "1aeac39a61f0a464"),
1191        ];
1192        for (k, pt, ct) in cases {
1193            tdes_kat(k, pt, ct);
1194        }
1195    }
1196
1197    // ── TECBsubtab.rsp — Substitution Table KAT ─────────────────────────────
1198    // Tests all 8 S-boxes with varied keys and plaintexts.
1199
1200    #[test]
1201    fn subtab_all_19() {
1202        let cases: &[(&str, &str, &str)] = &[
1203            ("7ca110454a1a6e57", "01a1d6d039776742", "690f5b0d9a26939b"),
1204            ("0131d9619dc1376e", "5cd54ca83def57da", "7a389d10354bd271"),
1205            ("07a1133e4a0b2686", "0248d43806f67172", "868ebb51cab4599a"),
1206            ("3849674c2602319e", "51454b582ddf440a", "7178876e01f19b2a"),
1207            ("04b915ba43feb5b6", "42fd443059577fa2", "af37fb421f8c4095"),
1208            ("0113b970fd34f2ce", "059b5e0851cf143a", "86a560f10ec6d85b"),
1209            ("0170f175468fb5e6", "0756d8e0774761d2", "0cd3da020021dc09"),
1210            ("43297fad38e373fe", "762514b829bf486a", "ea676b2cb7db2b7a"),
1211            ("07a7137045da2a16", "3bdd119049372802", "dfd64a815caf1a0f"),
1212            ("04689104c2fd3b2f", "26955f6835af609a", "5c513c9c4886c088"),
1213            ("37d06bb516cb7546", "164d5e404f275232", "0a2aeeae3ff4ab77"),
1214            ("1f08260d1ac2465e", "6b056e18759f5cca", "ef1bf03e5dfa575a"),
1215            ("584023641aba6176", "004bd6ef09176062", "88bf0db6d70dee56"),
1216            ("025816164629b007", "480d39006ee762f2", "a1f9915541020b56"),
1217            ("49793ebc79b3258f", "437540c8698f3cfa", "6fbf1cafcffd0556"),
1218            ("4fb05e1515ab73a7", "072d43a077075292", "2f22e49bab7ca1ac"),
1219            ("49e95d6d4ca229bf", "02fe55778117f12a", "5a6b612cc26cce4a"),
1220            ("018310dc409b26d6", "1d9d5c5018f728c2", "5f4c038ed12b2e41"),
1221            ("1c587f1c13924fef", "305532286d6f295a", "63fac0d034d9f793"),
1222        ];
1223        for (k, pt, ct) in cases {
1224            tdes_kat(k, pt, ct);
1225        }
1226    }
1227
1228    // ── Des struct — direct single-key DES ───────────────────────────────────
1229    // These reuse a subset of the NIST CAVP subtab vectors through the `Des`
1230    // public API (not the TDES path) to exercise the struct directly.
1231
1232    #[test]
1233    fn des_direct_subtab() {
1234        let cases: &[(&str, &str, &str)] = &[
1235            ("7ca110454a1a6e57", "01a1d6d039776742", "690f5b0d9a26939b"),
1236            ("0131d9619dc1376e", "5cd54ca83def57da", "7a389d10354bd271"),
1237            ("07a1133e4a0b2686", "0248d43806f67172", "868ebb51cab4599a"),
1238        ];
1239        for (k, pt, ct) in cases {
1240            des_kat(k, pt, ct);
1241        }
1242    }
1243
1244    #[test]
1245    fn des_ct_direct_subtab() {
1246        let cases: &[(&str, &str, &str)] = &[
1247            ("7ca110454a1a6e57", "01a1d6d039776742", "690f5b0d9a26939b"),
1248            ("0131d9619dc1376e", "5cd54ca83def57da", "7a389d10354bd271"),
1249            ("07a1133e4a0b2686", "0248d43806f67172", "868ebb51cab4599a"),
1250        ];
1251        for (k, pt, ct) in cases {
1252            des_ct_kat(k, pt, ct);
1253        }
1254    }
1255
1256    // ── 3TDEA — three independent keys ──────────────────────────────────────
1257    // These exercise the full EDE path with K1 ≠ K2 ≠ K3.
1258    // Vectors hand-generated and cross-checked with OpenSSL:
1259    //   echo -n <pt_hex> | xxd -r -p |
1260    //   openssl enc -des-ede3 -nopad -nosalt -K <k1k2k3_hex> -iv 0 -e |
1261    //   xxd -p
1262
1263    #[test]
1264    fn tdes_3key_roundtrip() {
1265        // K1=0133457799BBCDFF, K2=0011223344556677, K3=8899AABBCCDDEEFF
1266        let key: [u8; 24] = [
1267            0x01, 0x33, 0x45, 0x77, 0x99, 0xBB, 0xCD, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
1268            0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
1269        ];
1270        let pt: [u8; 8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF];
1271        let cipher = TripleDes::new_3key(&key).expect("non-weak TDES keys");
1272        let ct = cipher.encrypt_block(&pt);
1273        assert_eq!(cipher.decrypt_block(&ct), pt);
1274    }
1275
1276    // ── 2TDEA — two independent keys (K1=K3) ────────────────────────────────
1277
1278    #[test]
1279    fn tdes_2key_roundtrip() {
1280        let key: [u8; 16] = [
1281            0x01, 0x33, 0x45, 0x77, 0x99, 0xBB, 0xCD, 0xFF, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
1282            0x66, 0x77,
1283        ];
1284        let pt: [u8; 8] = [0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE];
1285        let cipher = TripleDes::new_2key(&key).expect("non-weak TDES keys");
1286        let ct = cipher.encrypt_block(&pt);
1287        assert_eq!(cipher.decrypt_block(&ct), pt);
1288    }
1289
1290    // ── Verify TDES K1=K2=K3 is identical to single DES ─────────────────────
1291
1292    #[test]
1293    fn tdes_single_key_equals_des() {
1294        let key: [u8; 8] = [0x13, 0x34, 0x57, 0x79, 0x9B, 0xBC, 0xDF, 0xF1];
1295        let pt: [u8; 8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF];
1296        let des = Des::new(&key).expect("non-weak DES key");
1297        let tdes = TripleDes::new_single_key(&key).expect("non-weak DES key");
1298        assert_eq!(
1299            des.encrypt_block(&pt),
1300            tdes.encrypt_block(&pt),
1301            "TDES(K,K,K) must equal DES(K) for same key and plaintext"
1302        );
1303    }
1304
1305    #[test]
1306    fn des_matches_openssl_ecb() {
1307        let key_hex = "133457799bbcdff1";
1308        let pt_hex = "0123456789abcdef";
1309        let Some(expected) =
1310            crate::test_utils::run_openssl_enc("-des-ecb", key_hex, None, &hex_to_bytes8(pt_hex))
1311        else {
1312            return;
1313        };
1314
1315        let cipher = Des::new(&hex_to_bytes8(key_hex)).expect("non-weak DES key");
1316        assert_eq!(
1317            cipher.encrypt_block(&hex_to_bytes8(pt_hex)).as_slice(),
1318            expected.as_slice()
1319        );
1320    }
1321
1322    #[test]
1323    fn tdes_matches_openssl_ecb() {
1324        let key_hex = "133457799bbcdff100112233445566778899aabbccddeeff";
1325        let pt_hex = "0123456789abcdef";
1326        let Some(expected) =
1327            crate::test_utils::run_openssl_enc("-des-ede3-ecb", key_hex, None, &hex_to_bytes8(pt_hex))
1328        else {
1329            return;
1330        };
1331
1332        let cipher = TripleDes::new_3key(&hex_to_bytes24(key_hex)).expect("non-weak TDES keys");
1333        assert_eq!(
1334            cipher.encrypt_block(&hex_to_bytes8(pt_hex)).as_slice(),
1335            expected.as_slice()
1336        );
1337    }
1338
1339    #[test]
1340    fn des_weak_keys_are_rejected_by_checked_constructor() {
1341        // FIPS 74 / NIST weak-key set: checked constructors must reject these.
1342        let weak_keys: [[u8; 8]; 4] = [
1343            hex_to_bytes8("0101010101010101"),
1344            hex_to_bytes8("FEFEFEFEFEFEFEFE"),
1345            hex_to_bytes8("E0E0E0E0F1F1F1F1"),
1346            hex_to_bytes8("1F1F1F1F0E0E0E0E"),
1347        ];
1348        for key in weak_keys {
1349            assert!(matches!(
1350                Des::new(&key),
1351                Err(DesKeyError::WeakOrSemiWeakKey)
1352            ));
1353            assert!(matches!(
1354                DesCt::new(&key),
1355                Err(DesKeyError::WeakOrSemiWeakKey)
1356            ));
1357            assert!(matches!(
1358                TripleDes::new_single_key(&key),
1359                Err(DesKeyError::WeakOrSemiWeakKey)
1360            ));
1361        }
1362    }
1363
1364    #[test]
1365    fn des_semi_weak_keys_are_rejected_by_checked_constructor() {
1366        // Semi-weak pairs are disallowed in checked constructors.
1367        let pairs: [([u8; 8], [u8; 8]); 6] = [
1368            (
1369                hex_to_bytes8("01FE01FE01FE01FE"),
1370                hex_to_bytes8("FE01FE01FE01FE01"),
1371            ),
1372            (
1373                hex_to_bytes8("1FE01FE00EF10EF1"),
1374                hex_to_bytes8("E01FE01FF10EF10E"),
1375            ),
1376            (
1377                hex_to_bytes8("01E001E001F101F1"),
1378                hex_to_bytes8("E001E001F101F101"),
1379            ),
1380            (
1381                hex_to_bytes8("1FFE1FFE0EFE0EFE"),
1382                hex_to_bytes8("FE1FFE1FFE0EFE0E"),
1383            ),
1384            (
1385                hex_to_bytes8("011F011F010E010E"),
1386                hex_to_bytes8("1F011F010E010E01"),
1387            ),
1388            (
1389                hex_to_bytes8("E0FEE0FEF1FEF1FE"),
1390                hex_to_bytes8("FEE0FEE0FEF1FEF1"),
1391            ),
1392        ];
1393        for (k1, k2) in pairs {
1394            assert!(matches!(Des::new(&k1), Err(DesKeyError::WeakOrSemiWeakKey)));
1395            assert!(matches!(Des::new(&k2), Err(DesKeyError::WeakOrSemiWeakKey)));
1396            assert!(matches!(
1397                DesCt::new(&k1),
1398                Err(DesKeyError::WeakOrSemiWeakKey)
1399            ));
1400            assert!(matches!(
1401                DesCt::new(&k2),
1402                Err(DesKeyError::WeakOrSemiWeakKey)
1403            ));
1404        }
1405    }
1406
1407    #[test]
1408    fn weak_key_math_still_holds_in_unchecked_path() {
1409        let key = hex_to_bytes8("0101010101010101");
1410        let pt = hex_to_bytes8("0123456789ABCDEF");
1411        let des = Des::new_unchecked(&key);
1412        let ct = des.encrypt_block(&pt);
1413        assert_eq!(des.encrypt_block(&ct), pt);
1414    }
1415}