precompute tables

This commit is contained in:
2026-04-28 11:44:01 +02:00
parent 7cfbe3442e
commit f85a268933
+80 -21
View File
@@ -4,41 +4,100 @@ use crate::tea1::*;
use rand::Rng;
use rayon::prelude::*;
const fn precompute_deriv_lut(lut: &[u16; 8]) -> [u8; 65536] {
let mut table = [0u8; 65536];
let mut wst: usize = 0;
while wst < 65536 {
let mut st0 = (wst & 0xFF) as u8;
let mut st1 = (wst >> 8) as u8;
let mut out = 0u8;
let mut i = 0usize;
while i < 8 {
let dist = ((st0 >> 7) & 1) | ((st0 << 1) & 0x02) | ((st1 << 1) & 0x0C);
if (lut[i] & (1u16 << dist)) != 0 {
out |= 1u8 << i;
}
st0 = (st0 >> 1) | (st0 << 7);
st1 = (st1 >> 1) | (st1 << 7);
i += 1;
}
table[wst] = out;
wst += 1;
}
table
}
const fn precompute_reorder_lut() -> [u8; 256] {
let mut table = [0u8; 256];
let mut b: usize = 0;
while b < 256 {
let v = b as u8;
table[b] = ((v << 6) & 0x40) | ((v << 1) & 0x20) | ((v << 2) & 0x08)
| ((v >> 3) & 0x14) | ((v >> 2) & 0x01) | ((v >> 5) & 0x02)
| ((v << 4) & 0x80);
b += 1;
}
table
}
const DERIV_A_LUT: [u8; 65536] = precompute_deriv_lut(&TEA1_LUT_A);
const DERIV_B_LUT: [u8; 65536] = precompute_deriv_lut(&TEA1_LUT_B);
const REORDER_LUT: [u8; 256] = precompute_reorder_lut();
// 5 bytes: 54 + 19*4 = 149 rounds max
const MAX_ROUNDS: usize = 149;
#[inline(always)]
fn precompute_sbox_seq(mut key_reg: u32, n_rounds: usize) -> [u8; MAX_ROUNDS] {
let mut seq = [0u8; MAX_ROUNDS];
for i in 0..n_rounds {
let idx = (((key_reg >> 24) ^ key_reg) & 0xFF) as usize;
let so = TEA1_SBOX[idx];
seq[i] = so;
key_reg = (key_reg << 8) | (so as u32);
}
seq
}
/// Returns true if the candidate key_reg produces the exact known keystream prefix
/// Early aborts on the first mismatch
#[inline(always)]
pub fn keyreg_matches_prefix(frame_number: u32, mut key_reg: u32, known_prefix: &[u8]) -> bool {
if known_prefix.is_empty() {
return true;
}
pub fn keyreg_matches_prefix(
frame_number: u32,
key_reg: u32,
known_prefix: &[u8],
) -> bool {
let n = known_prefix.len();
if n == 0 { return true; }
let total_rounds = 54 + 19 * (n - 1);
let sbox_seq = precompute_sbox_seq(key_reg, total_rounds);
let mut iv_reg = expand_iv(frame_number);
let mut skip_rounds: u32 = 54;
let mut round = 0;
for &target_byte in known_prefix {
for _ in 0..skip_rounds {
for (bi, &target) in known_prefix.iter().enumerate() {
let steps = if bi == 0 { 54 } else { 19 };
for _ in 0..steps {
// Step 1: non-linear feedback from key register
let sbox_idx = (((key_reg >> 24) ^ key_reg) & 0xFF) as usize;
let sbox_out = TEA1_SBOX[sbox_idx];
key_reg = (key_reg << 8) | (sbox_out as u32);
let sbox_out = sbox_seq[round];
round += 1;
// Step 2: derive 3 bytes from current state
let deriv_12 = state_word_to_newbyte(((iv_reg >> 8) & 0xFFFF) as u16, &TEA1_LUT_A);
let deriv_56 = state_word_to_newbyte(((iv_reg >> 40) & 0xFFFF) as u16, &TEA1_LUT_B);
let reord_4 = reorder_state_byte(((iv_reg >> 32) & 0xFF) as u8);
let deriv_12 = DERIV_A_LUT[((iv_reg >> 8) & 0xFFFF) as usize];
let deriv_56 = DERIV_B_LUT[((iv_reg >> 40) & 0xFFFF) as usize];
let reord_4 = REORDER_LUT[((iv_reg >> 32) & 0xFF) as usize];
// Step 3: combine
let new_byte = deriv_56 ^ ((iv_reg >> 56) as u8) ^ reord_4 ^ sbox_out;
let mix_byte = deriv_12;
let new_byte = deriv_56 ^ (iv_reg >> 56) as u8 ^ reord_4 ^ sbox_out;
// Step 4: update LFSR
iv_reg = ((iv_reg << 8) ^ ((mix_byte as u64) << 32)) | (new_byte as u64);
iv_reg = ((iv_reg << 8) ^ ((deriv_12 as u64) << 32)) | (new_byte as u64);
}
if (iv_reg >> 56) as u8 != target_byte {
return false;
if (iv_reg >> 56) as u8 != target {
return false; // early abort; ~255/256 candidates exit here
}
skip_rounds = 19;
}
true
}
@@ -50,7 +109,7 @@ pub fn recover_tea1_keyreg(frame_number: u32, known_keystream: &[u8]) -> Option<
return None;
}
let check_len = known_keystream.len().min(4);
let check_len = known_keystream.len().min(5);
let known_prefix = &known_keystream[0..check_len];
(0u32..=u32::MAX).into_par_iter().find_any({
@@ -67,7 +126,7 @@ pub fn tea1_attack_example() {
let reduced0 = init_key_register(&key0);
let frame0 = 0x1111_1111u32;
let ks0 = tea1_keystream(frame0, &key0, 4);
let ks0 = tea1_keystream(frame0, &key0, 5);
let recovered0 = recover_tea1_keyreg(frame0, &ks0);
assert_eq!(recovered0, Some(reduced0));