optimize attack
This commit is contained in:
+81
-108
@@ -245,78 +245,51 @@ const TAC: [u64; 24] = [
|
||||
0xb38000003c800000,
|
||||
];
|
||||
|
||||
/// Binary search in a sorted slice of Tab by `output` field
|
||||
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
|
||||
/// Full meet-in-the-middle attack (7‑bit u, 10‑bit w)
|
||||
fn attack(z: u64) -> u64 {
|
||||
const LOG_SIZE_TAC: u32 = 24;
|
||||
const LOG_SIZE_KERB: u32 = 32;
|
||||
let size_l = 1usize << LOG_SIZE_TAC;
|
||||
const LOG_U: u32 = 7; // size of u space (bits)
|
||||
const LOG_W: u32 = 10; // size of w space (bits)
|
||||
let l_size = 1 << LOG_U;
|
||||
let truev: u64 = 0; // v = 0
|
||||
|
||||
// Build table L (u -> stream( S = u XOR truev ) )
|
||||
let mut a0 = Lfsr { state: 0, mask: 0 };
|
||||
let mut b0 = Lfsr { state: 0, mask: 0 };
|
||||
let mut c0 = Lfsr { state: 0, mask: 0 };
|
||||
init(0, &mut a0, &mut b0, &mut c0);
|
||||
let stream0 = stream(&mut a0, &mut b0, &mut c0);
|
||||
println!("[*] stream(S=0) = 0x{:016X}", stream0);
|
||||
// --- Build table L (enumerate u) ---
|
||||
let mut l: Vec<Tab> = Vec::with_capacity(l_size);
|
||||
let mut current_gray: u64 = 0;
|
||||
let mut s = truev;
|
||||
|
||||
let mut l: Vec<Tab> = Vec::with_capacity(size_l);
|
||||
// First entry u = 0
|
||||
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);
|
||||
l.push(Tab {
|
||||
output: stream(&mut a, &mut b, &mut c),
|
||||
input: 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) = (
|
||||
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);
|
||||
l.push(Tab {
|
||||
output: stream(&mut a, &mut b, &mut c),
|
||||
input: current_u,
|
||||
});
|
||||
}
|
||||
|
||||
for i in 1u64..size_l as u64 {
|
||||
// Jump to next u using Gray-code XOR
|
||||
current_u ^= TAC[i.trailing_zeros() as usize];
|
||||
// Remaining 2^LOG_U - 1 entries using Gray code + TAC
|
||||
for i in 1u64..l_size as u64 {
|
||||
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) = (
|
||||
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 {
|
||||
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| {
|
||||
let cmp = a.output.cmp(&b.output);
|
||||
if cmp != Ordering::Equal {
|
||||
@@ -325,60 +298,71 @@ fn attack(z: u64) -> u64 {
|
||||
a.input.cmp(&b.input)
|
||||
}
|
||||
});
|
||||
println!("[*] Sort finished.");
|
||||
|
||||
println!("[*] Search in KerB");
|
||||
let mut current_w = 0u64;
|
||||
let mut result = 0u64;
|
||||
let mut found = false;
|
||||
// --- Search over w ---
|
||||
current_gray = 0;
|
||||
s = truev;
|
||||
|
||||
// First iteration w=0
|
||||
{
|
||||
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 ! w=0, u=0x{:016X}", hit.input);
|
||||
// Base case w = 0
|
||||
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];
|
||||
// Secret = truev ^ w ^ stored_input
|
||||
return s ^ hit.input;
|
||||
}
|
||||
|
||||
for i in 1u64..(1u64 << LOG_SIZE_KERB) {
|
||||
if found {
|
||||
break;
|
||||
}
|
||||
current_w ^= KER_B[i.trailing_zeros() as usize];
|
||||
// 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(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 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 at i={}", i);
|
||||
println!(" w = 0x{:016X}", current_w);
|
||||
println!(" u = 0x{:016X}", hit.input);
|
||||
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;
|
||||
}
|
||||
|
||||
if !found {
|
||||
println!("[-] Secret not found.");
|
||||
0
|
||||
} else {
|
||||
result
|
||||
}
|
||||
// Not found
|
||||
0
|
||||
}
|
||||
|
||||
//-------------------------- MAIN --------------------------
|
||||
@@ -398,7 +382,7 @@ fn main() {
|
||||
println!("0x{:08X}", z);
|
||||
|
||||
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 message = pack8(&plain);
|
||||
@@ -415,7 +399,7 @@ fn main() {
|
||||
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!("Target cipher stream : 0x{:016X}\n", z_challenge);
|
||||
|
||||
@@ -425,15 +409,4 @@ fn main() {
|
||||
|
||||
println!("\nSecret found : S = 0x{:016X}", secret);
|
||||
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" }
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user