use crate::BlockCipher;
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,
];
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,
];
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,
];
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,
];
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,
];
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,
];
const KEY_SHIFTS: [u8; 16] = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1];
const SBOXES: [[u8; 64]; 8] = [
[
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,
],
[
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,
],
[
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,
],
[
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,
],
[
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,
],
[
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,
],
[
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,
],
[
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,
],
];
#[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)
}
#[inline]
fn u64_to_bytes(v: u64) -> [u8; 8] {
v.to_be_bytes()
}
#[inline]
fn get_bit64(val: u64, pos: u8) -> u64 {
(val >> (64 - pos as u32)) & 1
}
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
}
pub struct Des {
subkeys: [u64; 16],
}
impl Des {
fn generate_subkeys(key: u64) -> [u64; 16] {
let pc1 = permute64(key, &PC1);
let mut c: u32 = (pc1 >> 36) as u32; let mut d: u32 = ((pc1 >> 8) as u32) & 0x0FFFFFFF;
let mut subkeys = [0u64; 16];
for round in 0..16 {
let shift = KEY_SHIFTS[round] as u32;
c = ((c << shift) | (c >> (28 - shift))) & 0x0FFFFFFF;
d = ((d << shift) | (d >> (28 - shift))) & 0x0FFFFFFF;
let cd: u64 = ((c as u64) << 36) | ((d as u64) << 8);
subkeys[round] = permute64(cd, &PC2);
}
subkeys
}
fn feistel(r: u32, subkey: u64) -> u32 {
let r64 = (r as u64) << 32;
let expanded = permute64(r64, &E_PERM);
let xored = expanded ^ subkey;
let mut sbox_result: u32 = 0;
for i in 0..8 {
let shift = 58 - (i * 6); let six_bits = ((xored >> shift) & 0x3F) as u8;
let row = ((six_bits & 0x20) >> 4) | (six_bits & 0x01); let col = (six_bits >> 1) & 0x0F;
let val = SBOXES[i as usize][(row as usize) * 16 + (col as usize)];
sbox_result |= (val as u32) << (28 - i * 4);
}
let sbox64 = (sbox_result as u64) << 32;
let p_out = permute64(sbox64, &P_PERM);
(p_out >> 32) as u32
}
fn des_cipher(&self, input: u64, decrypt: bool) -> u64 {
let permuted = permute64(input, &IP);
let mut l: u32 = (permuted >> 32) as u32;
let mut r: u32 = permuted as u32;
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;
}
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];
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));
}
}
pub struct TripleDes {
k1: Des,
k2: Des,
k3: Des,
}
impl BlockCipher for TripleDes {
const BLOCK_LEN: usize = 8;
const KEY_LENS: &'static [usize] = &[24];
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);
}
}
#[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()
}
#[test]
fn des_known_answer_tests() {
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());
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());
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"));
let mut b4 = hex_to_bytes("0123456789ABCDEF");
k3.encrypt_block(&mut b4);
assert_eq!(b4, hex_to_bytes("56CC09E7CFDC4CEF").as_slice());
}
#[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);
}
}
#[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());
}
#[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);
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");
}
}