cryptopals/examples/set2_12/main.rs
2023-04-27 20:19:27 -07:00

91 lines
3.4 KiB
Rust

use cryptopals::{set2_adversary::Set2Adversary, friendly::likely_ecb, prelude::ByteBased};
fn main() {
let adversary = Set2Adversary::gen();
// first of all, find the block size
let block_size = {
let mut inp = vec![];
let l1 = adversary.oracle_12(&inp).len();
loop {
inp.push(b'A');
let l2 = adversary.oracle_12(&inp).len();
if l2 > l1 {
break l2 - l1;
}
}
};
assert_eq!(block_size, 16);
// make sure we're in ECB mode
assert!(likely_ecb(&adversary.oracle_12(&[b'A'; 64])));
let message_len = adversary.oracle_12(&[]).len();
let mut known_message: Vec<u8> = vec![];
// sample questions for 'ABCD EFGH' and block size 4
// question dictionary pattern
// 000 000[A-Z]
// 00 00A[A-Z]
// 0 0AB[A-Z]
// ABC[A-Z]
// 000 BCD[A-Z]
// 00 CDE[A-Z]
// 0 DEF[A-Z]
// EFG[A-Z]
'inspect: while known_message.len() < message_len {
let interest_ix = known_message.len();
let interest_block_ix= interest_ix / block_size;
let interest_block_byte_ix = interest_ix % block_size;
let interest_shiftr1_padding = (block_size - interest_block_byte_ix - 1) % block_size; // padding it takes to put the character of interest at the end of a block
// build shiftr
let mut shiftr_block = vec![];
for _ in 0..interest_shiftr1_padding { shiftr_block.push(0)}
// build question
// this is the beginning of the dictionary pattern (minus the range at the end)
// if we're at the beginning of the string, use a zero as we know this
// comes from our shiftr block
// otherwise, use a character from the end of the string
let mut question_block = vec![];
for i in 0..block_size-1 {
let source_char_ix = known_message.len() as i64 - block_size as i64 + i as i64 + 1;
if source_char_ix < 0 { question_block.push(0); }
else { question_block.push(known_message[source_char_ix as usize]); }
}
// build dictionary
let mut dictionary: Vec<Vec<u8>> = vec![];
for possible_byte in u8::MIN..=u8::MAX {
let mut dict_block = question_block.clone();
dict_block.push(possible_byte);
let mut oracle_result = adversary.oracle_12(&dict_block);
dictionary.push(oracle_result.drain(0..block_size).collect())
}
// now shift the input. we know the position of the block containing the byte of interest
let mut tea_leaves: Vec<u8> = adversary.oracle_12(&shiftr_block);
let output_len = tea_leaves.len();
let block_of_interest: Vec<u8> = tea_leaves.drain(interest_block_ix * block_size..interest_block_ix * block_size + block_size).collect();
for possible_byte in u8::MIN..=u8::MAX {
if dictionary[possible_byte as usize] == block_of_interest {
if (possible_byte as usize) < block_size && (interest_ix + possible_byte as usize + shiftr_block.len() == output_len) {
break 'inspect // we hit padding
}
known_message.push(possible_byte);
continue 'inspect
}
}
panic!("we failed to break on padding");
}
println!("{}", known_message.to_text().unwrap())
}