const BIT_MASK_TABLE: [u8; 8] = [0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01];
const BIT_SWAP_TABLE_1: [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 BIT_SWAP_TABLE_2: [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 BIT_SWAP_TABLE_3: [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 NIBBLE_DATA: [[u8; 64]; 4] = [
[
0xef, 0x03, 0x41, 0xfd, 0xd8, 0x74, 0x1e, 0x47, 0x26, 0xef, 0xfb, 0x22, 0xb3, 0xd8, 0x84,
0x1e, 0x39, 0xac, 0xa7, 0x60, 0x62, 0xc1, 0xcd, 0xba, 0x5c, 0x96, 0x90, 0x59, 0x05, 0x3b,
0x7a, 0x85, 0x40, 0xfd, 0x1e, 0xc8, 0xe7, 0x8a, 0x8b, 0x21, 0xda, 0x43, 0x64, 0x9f, 0x2d,
0x14, 0xb1, 0x72, 0xf5, 0x5b, 0xc8, 0xb6, 0x9c, 0x37, 0x76, 0xec, 0x39, 0xa0, 0xa3, 0x05,
0x52, 0x6e, 0x0f, 0xd9,
],
[
0xa7, 0xdd, 0x0d, 0x78, 0x9e, 0x0b, 0xe3, 0x95, 0x60, 0x36, 0x36, 0x4f, 0xf9, 0x60, 0x5a,
0xa3, 0x11, 0x24, 0xd2, 0x87, 0xc8, 0x52, 0x75, 0xec, 0xbb, 0xc1, 0x4c, 0xba, 0x24, 0xfe,
0x8f, 0x19, 0xda, 0x13, 0x66, 0xaf, 0x49, 0xd0, 0x90, 0x06, 0x8c, 0x6a, 0xfb, 0x91, 0x37,
0x8d, 0x0d, 0x78, 0xbf, 0x49, 0x11, 0xf4, 0x23, 0xe5, 0xce, 0x3b, 0x55, 0xbc, 0xa2, 0x57,
0xe8, 0x22, 0x74, 0xce,
],
[
0x2c, 0xea, 0xc1, 0xbf, 0x4a, 0x24, 0x1f, 0xc2, 0x79, 0x47, 0xa2, 0x7c, 0xb6, 0xd9, 0x68,
0x15, 0x80, 0x56, 0x5d, 0x01, 0x33, 0xfd, 0xf4, 0xae, 0xde, 0x30, 0x07, 0x9b, 0xe5, 0x83,
0x9b, 0x68, 0x49, 0xb4, 0x2e, 0x83, 0x1f, 0xc2, 0xb5, 0x7c, 0xa2, 0x19, 0xd8, 0xe5, 0x7c,
0x2f, 0x83, 0xda, 0xf7, 0x6b, 0x90, 0xfe, 0xc4, 0x01, 0x5a, 0x97, 0x61, 0xa6, 0x3d, 0x40,
0x0b, 0x58, 0xe6, 0x3d,
],
[
0x4d, 0xd1, 0xb2, 0x0f, 0x28, 0xbd, 0xe4, 0x78, 0xf6, 0x4a, 0x0f, 0x93, 0x8b, 0x17, 0xd1,
0xa4, 0x3a, 0xec, 0xc9, 0x35, 0x93, 0x56, 0x7e, 0xcb, 0x55, 0x20, 0xa0, 0xfe, 0x6c, 0x89,
0x17, 0x62, 0x17, 0x62, 0x4b, 0xb1, 0xb4, 0xde, 0xd1, 0x87, 0xc9, 0x14, 0x3c, 0x4a, 0x7e,
0xa8, 0xe2, 0x7d, 0xa0, 0x9f, 0xf6, 0x5c, 0x6a, 0x09, 0x8d, 0xf0, 0x0f, 0xe3, 0x53, 0x25,
0x95, 0x36, 0x28, 0xcb,
],
];
fn nibble_swap(buf: &mut [u8]) {
for b in buf.iter_mut() {
*b = (*b).rotate_left(4);
}
}
fn bit_convert(src: &mut [u8; 8], table: &[u8; 64]) {
let mut tmp = [0u8; 8];
for lop in 0..64 {
let prm = (table[lop] - 1) as usize;
if src[(prm >> 3) & 7] & BIT_MASK_TABLE[prm & 7] != 0 {
tmp[(lop >> 3) & 7] |= BIT_MASK_TABLE[lop & 7];
}
}
*src = tmp;
}
fn bit_convert4(src: &mut [u8; 8]) {
let mut tmp = [0u8; 8];
tmp[0] = ((src[7] << 5) | (src[4] >> 3)) & 0x3f;
tmp[1] = ((src[4] << 1) | (src[5] >> 7)) & 0x3f;
tmp[2] = ((src[4] << 5) | (src[5] >> 3)) & 0x3f;
tmp[3] = ((src[5] << 1) | (src[6] >> 7)) & 0x3f;
tmp[4] = ((src[5] << 5) | (src[6] >> 3)) & 0x3f;
tmp[5] = ((src[6] << 1) | (src[7] >> 7)) & 0x3f;
tmp[6] = ((src[6] << 5) | (src[7] >> 3)) & 0x3f;
tmp[7] = ((src[7] << 1) | (src[4] >> 7)) & 0x3f;
for lop in 0..4 {
tmp[lop] = (NIBBLE_DATA[lop][tmp[lop * 2] as usize] & 0xf0)
| (NIBBLE_DATA[lop][tmp[lop * 2 + 1] as usize] & 0x0f);
}
tmp[4] = 0;
tmp[5] = 0;
tmp[6] = 0;
tmp[7] = 0;
for lop in 0..32 {
let prm = (BIT_SWAP_TABLE_3[lop] - 1) as usize;
if tmp[prm >> 3] & BIT_MASK_TABLE[prm & 7] != 0 {
tmp[(lop >> 3) + 4] |= BIT_MASK_TABLE[lop & 7];
}
}
for i in 0..4 {
src[i] ^= tmp[i + 4];
}
}
pub fn decode_filename(buf: &mut [u8]) {
let mut off = 0;
while off + 8 <= buf.len() {
let chunk: &mut [u8; 8] = (&mut buf[off..off + 8]).try_into().unwrap();
nibble_swap(chunk);
bit_convert(chunk, &BIT_SWAP_TABLE_1);
bit_convert4(chunk);
bit_convert(chunk, &BIT_SWAP_TABLE_2);
off += 8;
}
}
pub fn decode_des_etc(buf: &mut [u8], flag_type: u8, mut cycle: i32) {
cycle = if cycle < 3 {
3
} else if cycle < 5 {
cycle + 1
} else if cycle < 7 {
cycle + 9
} else {
cycle + 15
};
let mut cnt = 0i32;
let mut lop = 0i32;
let mut off = 0usize;
while off + 8 <= buf.len() {
let chunk: &mut [u8; 8] = (&mut buf[off..off + 8]).try_into().unwrap();
if lop < 20 || (flag_type == 0 && lop % cycle == 0) {
bit_convert(chunk, &BIT_SWAP_TABLE_1);
bit_convert4(chunk);
bit_convert(chunk, &BIT_SWAP_TABLE_2);
} else if cnt == 7 && flag_type == 0 {
let tmp = *chunk;
cnt = 0;
chunk[0] = tmp[3];
chunk[1] = tmp[4];
chunk[2] = tmp[6];
chunk[3] = tmp[0];
chunk[4] = tmp[1];
chunk[5] = tmp[2];
chunk[6] = tmp[5];
let a = tmp[7];
chunk[7] = match a {
0x00 => 0x2b,
0x2b => 0x00,
0x01 => 0x68,
0x68 => 0x01,
0x48 => 0x77,
0x77 => 0x48,
0x60 => 0xff,
0xff => 0x60,
0x6c => 0x80,
0x80 => 0x6c,
0xb9 => 0xc0,
0xc0 => 0xb9,
0xeb => 0xfe,
0xfe => 0xeb,
other => other,
};
} else {
cnt += 1;
}
lop += 1;
off += 8;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nibble_swap_is_self_inverse() {
let mut buf = *b"abcdefgh";
nibble_swap(&mut buf);
nibble_swap(&mut buf);
assert_eq!(&buf, b"abcdefgh");
}
#[test]
fn bit_convert_is_deterministic() {
let mut a = [0xde, 0xad, 0xbe, 0xef, 0xfe, 0xed, 0xfa, 0xce];
let mut b = a;
bit_convert(&mut a, &BIT_SWAP_TABLE_1);
bit_convert(&mut b, &BIT_SWAP_TABLE_1);
assert_eq!(a, b);
}
#[test]
fn decode_filename_changes_input() {
let mut a = *b"data.txt";
let original = a;
decode_filename(&mut a);
assert_ne!(a, original, "transform should not be identity");
let mut b = original;
decode_filename(&mut b);
assert_eq!(a, b, "transform should be deterministic");
}
#[test]
fn decode_des_etc_runs_on_short_buffer() {
let mut buf = *b"short";
let original = buf;
decode_des_etc(&mut buf, 0, 1);
assert_eq!(buf, original);
}
#[test]
fn decode_des_etc_first_block_uses_full_transform() {
let mut buf = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11];
let original = buf;
decode_des_etc(&mut buf, 0, 1);
assert_ne!(buf, original);
}
}