optimize attack

This commit is contained in:
2026-04-23 15:59:20 +02:00
parent 231b8211e4
commit ff3a28db10
+78 -105
View File
@@ -245,78 +245,51 @@ const TAC: [u64; 24] = [
0xb38000003c800000, 0xb38000003c800000,
]; ];
/// Binary search in a sorted slice of Tab by `output` field /// Full meet-in-the-middle attack (7bit u, 10bit w)
fn binary_search_tab(table: &[Tab], target: u64) -> Option<&Tab> {
table
.binary_search_by(|entry| {
if entry.output < target {
Ordering::Less
} else if entry.output > target {
Ordering::Greater
} else {
Ordering::Equal
}
})
.ok()
.map(|idx| &table[idx])
}
/// Full meet-in-the-middle attack
fn attack(z: u64) -> u64 { fn attack(z: u64) -> u64 {
const LOG_SIZE_TAC: u32 = 24; const LOG_U: u32 = 7; // size of u space (bits)
const LOG_SIZE_KERB: u32 = 32; const LOG_W: u32 = 10; // size of w space (bits)
let size_l = 1usize << LOG_SIZE_TAC; let l_size = 1 << LOG_U;
let truev: u64 = 0; // v = 0
// Build table L (u -> stream( S = u XOR truev ) ) // --- Build table L (enumerate u) ---
let mut a0 = Lfsr { state: 0, mask: 0 }; let mut l: Vec<Tab> = Vec::with_capacity(l_size);
let mut b0 = Lfsr { state: 0, mask: 0 }; let mut current_gray: u64 = 0;
let mut c0 = Lfsr { state: 0, mask: 0 }; let mut s = truev;
init(0, &mut a0, &mut b0, &mut c0);
let stream0 = stream(&mut a0, &mut b0, &mut c0);
println!("[*] stream(S=0) = 0x{:016X}", stream0);
let mut l: Vec<Tab> = Vec::with_capacity(size_l); // First entry u = 0
let truev: u64 = 0;
println!("[*] building L ({} entries)...", size_l);
let mut current_u = 0u64;
// First entry i=0
{
let (mut a, mut b, mut c) = ( let (mut a, mut b, mut c) = (
Lfsr { state: 0, mask: 0 }, Lfsr { state: 0, mask: 0 },
Lfsr { state: 0, mask: 0 }, Lfsr { state: 0, mask: 0 },
Lfsr { state: 0, mask: 0 }, Lfsr { state: 0, mask: 0 },
); );
init(current_u ^ truev, &mut a, &mut b, &mut c); init(s, &mut a, &mut b, &mut c);
l.push(Tab { l.push(Tab {
output: stream(&mut a, &mut b, &mut c), output: stream(&mut a, &mut b, &mut c),
input: current_u, input: 0,
}); });
}
for i in 1u64..size_l as u64 { // Remaining 2^LOG_U - 1 entries using Gray code + TAC
// Jump to next u using Gray-code XOR for i in 1u64..l_size as u64 {
current_u ^= TAC[i.trailing_zeros() as usize]; let new_gray = i ^ (i >> 1);
let d = current_gray ^ new_gray;
let pos = d.trailing_zeros() as usize;
s ^= TAC[pos]; // update S = truev ^ u
current_gray = new_gray;
let (mut a, mut b, mut c) = ( let (mut a, mut b, mut c) = (
Lfsr { state: 0, mask: 0 }, Lfsr { state: 0, mask: 0 },
Lfsr { state: 0, mask: 0 }, Lfsr { state: 0, mask: 0 },
Lfsr { state: 0, mask: 0 }, Lfsr { state: 0, mask: 0 },
); );
init(current_u ^ truev, &mut a, &mut b, &mut c); init(s, &mut a, &mut b, &mut c);
l.push(Tab { l.push(Tab {
output: stream(&mut a, &mut b, &mut c), output: stream(&mut a, &mut b, &mut c),
input: current_u, input: s, // store the full S
}); });
if i & 0xFFFFF == 0 {
println!(" {} / {}", i, size_l);
}
} }
println!("[*] Sorting L"); // Sort L by output for binary search
l.sort_by(|a, b| { l.sort_by(|a, b| {
let cmp = a.output.cmp(&b.output); let cmp = a.output.cmp(&b.output);
if cmp != Ordering::Equal { if cmp != Ordering::Equal {
@@ -325,60 +298,71 @@ fn attack(z: u64) -> u64 {
a.input.cmp(&b.input) a.input.cmp(&b.input)
} }
}); });
println!("[*] Sort finished.");
println!("[*] Search in KerB"); // --- Search over w ---
let mut current_w = 0u64; current_gray = 0;
let mut result = 0u64; s = truev;
let mut found = false;
// First iteration w=0 // Base case w = 0
{
let (mut a, mut b, mut c) = ( let (mut a, mut b, mut c) = (
Lfsr { state: 0, mask: 0 }, Lfsr { state: 0, mask: 0 },
Lfsr { state: 0, mask: 0 }, Lfsr { state: 0, mask: 0 },
Lfsr { state: 0, mask: 0 }, Lfsr { state: 0, mask: 0 },
); );
init(current_w ^ truev, &mut a, &mut b, &mut c); init(s, &mut a, &mut b, &mut c);
let sw = stream(&mut a, &mut b, &mut c); let sw = stream(&mut a, &mut b, &mut c);
let target = z ^ sw ^ stream0; let target = z ^ sw;
if let Some(hit) = binary_search_tab(&l, target) {
result = hit.input ^ current_w ^ truev;
found = true;
println!("[!] Correlation found ! w=0, u=0x{:016X}", hit.input);
}
}
for i in 1u64..(1u64 << LOG_SIZE_KERB) { if let Ok(idx) = l.binary_search_by(|entry| {
if found { if entry.output < target {
break; Ordering::Less
} } else if entry.output > target {
current_w ^= KER_B[i.trailing_zeros() as usize]; Ordering::Greater
let (mut a, mut b, mut c) = (
Lfsr { state: 0, mask: 0 },
Lfsr { state: 0, mask: 0 },
Lfsr { state: 0, mask: 0 },
);
init(current_w ^ truev, &mut a, &mut b, &mut c);
let sw = stream(&mut a, &mut b, &mut c);
let target = z ^ sw ^ stream0;
if let Some(hit) = binary_search_tab(&l, target) {
result = hit.input ^ current_w ^ truev;
found = true;
println!("[!] Correlation found at i={}", i);
println!(" w = 0x{:016X}", current_w);
println!(" u = 0x{:016X}", hit.input);
}
}
if !found {
println!("[-] Secret not found.");
0
} else { } else {
result Ordering::Equal
} }
}) {
let hit = &l[idx];
// Secret = truev ^ w ^ stored_input
return s ^ hit.input;
}
// Iterate over the remaining 1023 values of w
let mut i = 1u64;
while i >> LOG_W == 0 {
let new_gray = i ^ (i >> 1);
let d = current_gray ^ new_gray;
let pos = d.trailing_zeros() as usize;
s ^= KER_B[pos]; // update S = truev ^ w
current_gray = new_gray;
let (mut a, mut b, mut c) = (
Lfsr { state: 0, mask: 0 },
Lfsr { state: 0, mask: 0 },
Lfsr { state: 0, mask: 0 },
);
init(s, &mut a, &mut b, &mut c);
let sw = stream(&mut a, &mut b, &mut c);
let target = z ^ sw;
if let Ok(idx) = l.binary_search_by(|entry| {
if entry.output < target {
Ordering::Less
} else if entry.output > target {
Ordering::Greater
} else {
Ordering::Equal
}
}) {
let hit = &l[idx];
return s ^ hit.input;
}
i += 1;
}
// Not found
0
} }
//-------------------------- MAIN -------------------------- //-------------------------- MAIN --------------------------
@@ -398,7 +382,7 @@ fn main() {
println!("0x{:08X}", z); println!("0x{:08X}", z);
println!("key S = 0x{:016X}\n", s); println!("key S = 0x{:016X}\n", s);
print_representation(); // print_representation();
let plain: [u8; 8] = [b'H', b'E', b'L', b'L', b'O', b' ', b'W', b'O']; let plain: [u8; 8] = [b'H', b'E', b'L', b'L', b'O', b' ', b'W', b'O'];
let message = pack8(&plain); let message = pack8(&plain);
@@ -415,7 +399,7 @@ fn main() {
std::str::from_utf8(&recovered_text[..8]).unwrap() std::str::from_utf8(&recovered_text[..8]).unwrap()
); );
let z_challenge: u64 = 0x2D0E339CFD588D1B; let z_challenge: u64 = 0x9AC76518F2322DA4;
println!("=== Meet-in-the-Middle attack==="); println!("=== Meet-in-the-Middle attack===");
println!("Target cipher stream : 0x{:016X}\n", z_challenge); println!("Target cipher stream : 0x{:016X}\n", z_challenge);
@@ -425,15 +409,4 @@ fn main() {
println!("\nSecret found : S = 0x{:016X}", secret); println!("\nSecret found : S = 0x{:016X}", secret);
println!(" Time : {:.2} s\n", elapsed); println!(" Time : {:.2} s\n", elapsed);
// Verification
init(secret, &mut a, &mut b, &mut c);
let z_check = stream(&mut a, &mut b, &mut c);
println!("=== Check ===");
println!("stream(S_found) = 0x{:016X}", z_check);
println!("z_challenge = 0x{:016X}", z_challenge);
println!(
"Correlation : {}",
if z_check == z_challenge { "YES" } else { "NO" }
);
} }