//! DES (FIPS 46-3) and Triple-DES (3DES / TDEA) block cipher
//! implementations.
//!
//! DES operates on 64-bit blocks with a 56-bit effective key
//! (stored as 64 bits with parity). Triple-DES uses three DES keys
//! in EDE (Encrypt-Decrypt-Encrypt) mode.
//!
//! # ⚠ Side-channel posture (legacy, no hardening planned)
//!
//! Both DES and Triple-DES use **table-based S-boxes** with the
//! same cache-timing leakage surface as the AES table-based
//! implementation (cf. [`super::aes`]). There is **no hardening
//! planned** — these primitives ship for legacy interop only and
//! should be avoided on SCA-sensitive targets. Prefer AES (and
//! once roadmap item `T1-A` lands, a fixsliced AES) instead.
//!
//! # Security note
//!
//! DES is considered insecure due to its 56-bit key length and
//! should not be used for new applications. 3DES is deprecated by
//! NIST as of 2023. Use AES instead.

use crate::BlockCipher;

// ============================================================
// DES permutation and S-box tables
// ============================================================

/// Initial Permutation (IP), 1-indexed.
const IP: [u8; 64] = [
    58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 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, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47,
    39, 31, 23, 15, 7,
];

/// Final Permutation (IP^-1), 1-indexed.
const FP: [u8; 64] = [
    40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 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, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9,
    49, 17, 57, 25,
];

/// Expansion permutation E (32 -> 48 bits), 1-indexed.
const E_PERM: [u8; 48] = [
    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, 19, 20, 21, 20, 21,
    22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1,
];

/// Permutation P (after S-box output), 1-indexed.
const P_PERM: [u8; 32] = [
    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, 13, 30, 6, 22, 11, 4,
    25,
];

/// Permuted Choice 1 (PC-1): select 56 bits from 64-bit key, 1-indexed.
const PC1: [u8; 56] = [
    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, 52, 44, 36, 63, 55,
    47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4,
];

/// Permuted Choice 2 (PC-2): select 48 bits from 56-bit key state, 1-indexed.
const PC2: [u8; 48] = [
    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, 31, 37, 47, 55, 30,
    40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32,
];

/// Number of left shifts per round in key schedule.
const KEY_SHIFTS: [u8; 16] = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1];

/// DES S-boxes (8 boxes, each 4x16 = 64 entries, 6 bits in -> 4 bits out).
const SBOXES: [[u8; 64]; 8] = [
    // S1
    [
        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, 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, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
    ],
    // S2
    [
        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, 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, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
    ],
    // S3
    [
        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, 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, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
    ],
    // S4
    [
        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, 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, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
    ],
    // S5
    [
        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, 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, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
    ],
    // S6
    [
        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, 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, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
    ],
    // S7
    [
        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, 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, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
    ],
    // S8
    [
        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, 2, 0, 14, 9, 11, 7,
        11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11,
    ],
];

// ============================================================
// u64-based helpers for DES
// ============================================================

/// Convert 8 bytes (big-endian) to u64.
#[inline]
fn bytes_to_u64(b: &[u8]) -> u64 {
    ((b[0] as u64) << 56)
        | ((b[1] as u64) << 48)
        | ((b[2] as u64) << 40)
        | ((b[3] as u64) << 32)
        | ((b[4] as u64) << 24)
        | ((b[5] as u64) << 16)
        | ((b[6] as u64) << 8)
        | (b[7] as u64)
}

/// Convert u64 to 8 bytes (big-endian).
#[inline]
fn u64_to_bytes(v: u64) -> [u8; 8] {
    v.to_be_bytes()
}

/// Get bit `pos` (1-indexed, MSB=1) from a u64.
#[inline]
fn get_bit64(val: u64, pos: u8) -> u64 {
    (val >> (64 - pos as u32)) & 1
}

/// Apply a permutation table on a u64.
/// `in_width` is the number of significant bits in the input (counted from MSB).
/// The table entries are 1-indexed positions in the input.
/// Output bits are placed starting from the MSB of the returned u64.
fn permute64(input: u64, table: &[u8]) -> u64 {
    let mut output: u64 = 0;
    for (i, &pos) in table.iter().enumerate() {
        let bit = get_bit64(input, pos);
        output |= bit << (63 - i as u32);
    }
    output
}

// ============================================================
// DES core
// ============================================================

/// DES block cipher (64-bit block, 56-bit effective key).
pub struct Des {
    /// 16 round subkeys, each 48 bits (stored right-aligned in u64, but
    /// we keep them in the high 48 bits for consistency).
    subkeys: [u64; 16],
}

impl Des {
    /// Generate the 16 round subkeys from a 64-bit key.
    fn generate_subkeys(key: u64) -> [u64; 16] {
        // Apply PC-1 to get 56 bits in the high 56 bits of a u64
        let pc1 = permute64(key, &PC1);

        // Split into C (high 28 bits) and D (next 28 bits)
        let mut c: u32 = (pc1 >> 36) as u32; // top 28 bits
        let mut d: u32 = ((pc1 >> 8) as u32) & 0x0FFFFFFF; // next 28 bits

        let mut subkeys = [0u64; 16];

        for round in 0..16 {
            let shift = KEY_SHIFTS[round] as u32;
            // Left-rotate C and D by `shift` positions within 28 bits
            c = ((c << shift) | (c >> (28 - shift))) & 0x0FFFFFFF;
            d = ((d << shift) | (d >> (28 - shift))) & 0x0FFFFFFF;

            // Concatenate C and D into 56 bits in the high part of a u64
            let cd: u64 = ((c as u64) << 36) | ((d as u64) << 8);

            // Apply PC-2 to get 48-bit subkey in high 48 bits
            subkeys[round] = permute64(cd, &PC2);
        }

        subkeys
    }

    /// The DES Feistel function f(R, K).
    /// R is 32 bits in the high 32 bits of a u64.
    /// K is 48 bits in the high 48 bits of a u64.
    /// Returns 32 bits in the high 32 bits of a u64.
    fn feistel(r: u32, subkey: u64) -> u32 {
        // Expand R from 32 to 48 bits using E permutation
        // R is in the high 32 bits... actually let's put R into a u64 in high bits
        let r64 = (r as u64) << 32;
        let expanded = permute64(r64, &E_PERM); // 48 bits in high 48 bits

        // XOR with subkey
        let xored = expanded ^ subkey;

        // Apply 8 S-boxes: extract 6 bits at a time from the high 48 bits
        let mut sbox_result: u32 = 0;
        for i in 0..8 {
            // Extract 6 bits starting at bit position (16 + i*6) from the right
            // or equivalently, bits [63 - i*6 .. 63 - i*6 - 5] from MSB numbering
            let shift = 58 - (i * 6); // 58, 52, 46, 40, 34, 28, 22, 16
            let six_bits = ((xored >> shift) & 0x3F) as u8;

            // Row = outer two bits (bit5, bit0), column = inner four bits (bit4..bit1)
            let row = ((six_bits & 0x20) >> 4) | (six_bits & 0x01); // bit5 << 1 | bit0
            let col = (six_bits >> 1) & 0x0F;
            let val = SBOXES[i as usize][(row as usize) * 16 + (col as usize)];

            // Pack into sbox_result (high 32 bits pattern: 4 bits per S-box)
            sbox_result |= (val as u32) << (28 - i * 4);
        }

        // Apply P permutation on the 32-bit S-box output
        let sbox64 = (sbox_result as u64) << 32;
        let p_out = permute64(sbox64, &P_PERM);
        (p_out >> 32) as u32
    }

    /// Core DES cipher (encrypt or decrypt based on subkey order).
    fn des_cipher(&self, input: u64, decrypt: bool) -> u64 {
        // Initial permutation
        let permuted = permute64(input, &IP);

        let mut l: u32 = (permuted >> 32) as u32;
        let mut r: u32 = permuted as u32;

        // 16 Feistel rounds
        for round in 0..16 {
            let subkey_idx = if decrypt { 15 - round } else { round };
            let f_out = Self::feistel(r, self.subkeys[subkey_idx]);

            let new_r = l ^ f_out;
            l = r;
            r = new_r;
        }

        // Combine R || L (note the 32-bit swap) and apply final permutation
        let pre_fp: u64 = ((r as u64) << 32) | (l as u64);
        permute64(pre_fp, &FP)
    }
}

impl BlockCipher for Des {
    const BLOCK_LEN: usize = 8;
    const KEY_LENS: &'static [usize] = &[8]; // 64 bits (56 effective + 8 parity)

    fn new(key: &[u8]) -> Self {
        assert_eq!(key.len(), 8, "DES requires an 8-byte key");
        Des {
            subkeys: Self::generate_subkeys(bytes_to_u64(key)),
        }
    }

    fn encrypt_block(&self, block: &mut [u8]) {
        assert!(block.len() >= 8, "DES: block must be at least 8 bytes");
        let input = bytes_to_u64(&block[..8]);
        let output = self.des_cipher(input, false);
        block[..8].copy_from_slice(&u64_to_bytes(output));
    }

    fn decrypt_block(&self, block: &mut [u8]) {
        assert!(block.len() >= 8, "DES: block must be at least 8 bytes");
        let input = bytes_to_u64(&block[..8]);
        let output = self.des_cipher(input, true);
        block[..8].copy_from_slice(&u64_to_bytes(output));
    }
}

// ============================================================
// Triple DES (3DES / TDEA) -- EDE mode
// ============================================================

/// Triple DES in EDE (Encrypt-Decrypt-Encrypt) mode.
///
/// Uses three independent DES keys (K1, K2, K3). The 24-byte key is split
/// into three 8-byte DES keys.
pub struct TripleDes {
    k1: Des,
    k2: Des,
    k3: Des,
}

impl BlockCipher for TripleDes {
    const BLOCK_LEN: usize = 8;
    const KEY_LENS: &'static [usize] = &[24]; // 3 x 8 bytes

    fn new(key: &[u8]) -> Self {
        assert_eq!(key.len(), 24, "3DES requires a 24-byte key (3 x 8)");
        TripleDes {
            k1: Des::new(&key[0..8]),
            k2: Des::new(&key[8..16]),
            k3: Des::new(&key[16..24]),
        }
    }

    fn encrypt_block(&self, block: &mut [u8]) {
        self.k1.encrypt_block(block);
        self.k2.decrypt_block(block);
        self.k3.encrypt_block(block);
    }

    fn decrypt_block(&self, block: &mut [u8]) {
        self.k3.decrypt_block(block);
        self.k2.encrypt_block(block);
        self.k1.decrypt_block(block);
    }
}

// ============================================================
// Tests
// ============================================================

#[cfg(test)]
mod tests {
    use super::*;

    fn hex_to_bytes(s: &str) -> Vec<u8> {
        (0..s.len())
            .step_by(2)
            .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
            .collect()
    }

    /// DES ECB known-answer tests from authoritative sources.
    #[test]
    fn des_known_answer_tests() {
        // J. Orlin Grabbe DES tutorial vector
        let k1 = Des::new(&hex_to_bytes("133457799BBCDFF1"));
        let mut b1 = hex_to_bytes("0123456789ABCDEF");
        k1.encrypt_block(&mut b1);
        assert_eq!(b1, hex_to_bytes("85E813540F0AB405").as_slice());
        k1.decrypt_block(&mut b1);
        assert_eq!(b1, hex_to_bytes("0123456789ABCDEF").as_slice());

        // Key=FEDCBA9876543210, PT=0123456789ABCDEF -> ED39D950FA74BCC4
        let k2 = Des::new(&hex_to_bytes("FEDCBA9876543210"));
        let mut b2 = hex_to_bytes("0123456789ABCDEF");
        k2.encrypt_block(&mut b2);
        assert_eq!(b2, hex_to_bytes("ED39D950FA74BCC4").as_slice());
        k2.decrypt_block(&mut b2);
        assert_eq!(b2, hex_to_bytes("0123456789ABCDEF").as_slice());

        // Key=0123456789ABCDEF, PT=0000000000000000 -> D5D44FF720683D0D
        let k3 = Des::new(&hex_to_bytes("0123456789ABCDEF"));
        let mut b3 = [0u8; 8];
        k3.encrypt_block(&mut b3);
        assert_eq!(b3.to_vec(), hex_to_bytes("D5D44FF720683D0D"));

        // Key=0123456789ABCDEF, PT=0123456789ABCDEF -> 56CC09E7CFDC4CEF
        let mut b4 = hex_to_bytes("0123456789ABCDEF");
        k3.encrypt_block(&mut b4);
        assert_eq!(b4, hex_to_bytes("56CC09E7CFDC4CEF").as_slice());
    }

    /// DES round-trip with various inputs.
    #[test]
    fn des_round_trip() {
        let cipher = Des::new(&hex_to_bytes("0123456789ABCDEF"));
        for pt_hex in &[
            "0000000000000000",
            "FFFFFFFFFFFFFFFF",
            "4E6F772069732074",
            "0123456789ABCDEF",
        ] {
            let pt = hex_to_bytes(pt_hex);
            let mut block = pt.clone();
            cipher.encrypt_block(&mut block);
            assert_ne!(block, pt.as_slice(), "CT should differ from PT for {}", pt_hex);
            cipher.decrypt_block(&mut block);
            assert_eq!(block, pt.as_slice(), "round-trip failed for PT={}", pt_hex);
        }
    }

    /// 3DES round-trip test.
    #[test]
    fn triple_des_round_trip() {
        let key = hex_to_bytes("0123456789ABCDEF23456789ABCDEF01456789ABCDEF0123");
        let plaintext = hex_to_bytes("4E6F772069732074");

        let cipher = TripleDes::new(&key);

        let mut block = plaintext.clone();
        cipher.encrypt_block(&mut block);
        assert_ne!(block, plaintext.as_slice());

        cipher.decrypt_block(&mut block);
        assert_eq!(block, plaintext.as_slice());
    }

    /// 3DES EDE consistency: TripleDes matches manual E-D-E.
    #[test]
    fn triple_des_ede_consistency() {
        let key = hex_to_bytes("0123456789ABCDEF23456789ABCDEF01456789ABCDEF0123");
        let plaintext = hex_to_bytes("4E6F772069732074");
        let cipher = TripleDes::new(&key);

        let mut block = plaintext.clone();
        cipher.encrypt_block(&mut block);

        // Verify by manual EDE
        let k1 = Des::new(&hex_to_bytes("0123456789ABCDEF"));
        let k2 = Des::new(&hex_to_bytes("23456789ABCDEF01"));
        let k3 = Des::new(&hex_to_bytes("456789ABCDEF0123"));

        let mut manual = plaintext.clone();
        k1.encrypt_block(&mut manual);
        k2.decrypt_block(&mut manual);
        k3.encrypt_block(&mut manual);

        assert_eq!(block, manual.as_slice(), "3DES EDE mismatch");
    }
}
