diff --git a/src/crypto.rs b/src/crypto.rs index 3632c82..69389ec 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -58,7 +58,7 @@ impl Aes { Some(data.clone()) } - pub fn ecb(&self, mut data: Vec, mode: Mode) -> Option> { + pub fn ecb(&self, data: Vec, mode: Mode) -> Option> { if (!self.pad) && (data.len() % Self::BLOCK_SIZE != 0) { return None; } diff --git a/src/main.rs b/src/main.rs index c1402a8..7638b03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -168,8 +168,8 @@ fn q11() { let key: Vec = rng.sample_iter(&dist).take(16).collect(); let iv: Vec = rng.sample_iter(&dist).take(16).collect(); let aes = crypto::Aes::new(&key, true).unwrap(); - let mut start: Vec = rng.sample_iter(&dist).take(rng.gen_range(5,11)).collect(); - let mut end: Vec = rng.sample_iter(&dist).take(rng.gen_range(5,11)).collect(); + let mut start: Vec = rng.sample_iter(&dist).take(rng.gen_range(5, 11)).collect(); + let mut end: Vec = rng.sample_iter(&dist).take(rng.gen_range(5, 11)).collect(); start.append(&mut data); start.append(&mut end); @@ -180,21 +180,21 @@ fn q11() { } }; - let oracle = |data: &Vec| -> bool { - data[16..32] == data[32..48] - }; + let oracle = |data: &Vec| -> bool { data[16..32] == data[32..48] }; for i in 0..128 { - let data = [0u8;64]; + let data = [0u8; 64]; let (ecb, data) = black_box(data.to_vec()); assert!(ecb == oracle(&data)); } } -fn q12() -{ +fn q12() { println!("Running q12"); - let key: Vec = rand::thread_rng().sample_iter(rand::distributions::Standard).take(16).collect(); + let key: Vec = rand::thread_rng() + .sample_iter(rand::distributions::Standard) + .take(16) + .collect(); let plaintext: Vec = Bytes::from_base64("Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK").into(); let aes = crate::crypto::Aes::new(&key, true).unwrap(); @@ -204,7 +204,10 @@ fn q12() }; let starting_size = blackbox(Vec::new()).len(); - let (first_increment, next_size) = (0..).map(|x:usize| (x, blackbox(std::iter::repeat(0u8).take(x).collect()).len())).find(|(_,x)| *x != starting_size).unwrap(); + let (first_increment, next_size) = (0..) + .map(|x: usize| (x, blackbox(std::iter::repeat(0u8).take(x).collect()).len())) + .find(|(_, x)| *x != starting_size) + .unwrap(); let block_size = next_size - starting_size; println!(" Block size is {}", block_size); assert!(block_size == 16); @@ -220,15 +223,23 @@ fn q12() let mut known: Vec = Vec::with_capacity(data_length); println!(" Data length: {}", data_length); - for _ in 0..data_length - { - let nulls: Vec = std::iter::repeat(0u8).take(starting_size - known.len() - 1).collect(); + for _ in 0..data_length { + let nulls: Vec = std::iter::repeat(0u8) + .take(starting_size - known.len() - 1) + .collect(); let target_block = &blackbox(nulls.clone())[0..starting_size]; let mut found = false; 'byteiter: for b in 0u8..=255u8 { - let this_block = &blackbox(nulls.iter().chain(known.iter()).chain(std::iter::once(&b)).cloned().take(starting_size).collect())[0..starting_size]; - if this_block == target_block - { + let this_block = &blackbox( + nulls + .iter() + .chain(known.iter()) + .chain(std::iter::once(&b)) + .cloned() + .take(starting_size) + .collect(), + )[0..starting_size]; + if this_block == target_block { known.push(b); found = true; break 'byteiter; @@ -239,10 +250,12 @@ fn q12() // println!(" {}", String::from_utf8(known).unwrap().replace("\n", "\n ")); } -fn q13() -{ +fn q13() { println!("Running q13"); - let key: Vec = rand::thread_rng().sample_iter(rand::distributions::Standard).take(16).collect(); + let key: Vec = rand::thread_rng() + .sample_iter(rand::distributions::Standard) + .take(16) + .collect(); let parse = |x: &str| -> std::collections::HashMap { let mut out = std::collections::HashMap::new(); @@ -251,12 +264,15 @@ fn q13() if elements.len() == 2 { out.insert(elements[0].to_owned(), elements[1].to_owned()); } - }; + } out }; let profile_for = |mail: &str| -> String { - format!("email={}&uid=10&role=user", mail.replace("&","").replace("=","")) + format!( + "email={}&uid=10&role=user", + mail.replace("&", "").replace("=", "") + ) }; let encrypt = |profile: &str| -> Vec { @@ -278,13 +294,128 @@ fn q13() let mail2 = " ".repeat(16 - "email=".len()); let mail2 = mail2 + "admin"; let padding_char = (16 - "admin".len()) as u8; - let mail2 = mail2 + &String::from_utf8(vec![padding_char]).unwrap().repeat(padding_char as usize); + let mail2 = mail2 + + &String::from_utf8(vec![padding_char]) + .unwrap() + .repeat(padding_char as usize); let part2: &[u8] = &encrypt(&profile_for(&mail2))[16..32]; let whole: Vec = part1.iter().chain(part2.iter()).cloned().collect(); assert!(is_admin(whole)); } +fn q14() { + println!("Running q14"); + let key: Vec = rand::thread_rng() + .sample_iter(rand::distributions::Standard) + .take(16) + .collect(); + let plaintext: Vec = Bytes::from_base64("Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBqdXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUgYnkK").into(); + let aes = crate::crypto::Aes::new(&key, true).unwrap(); + let prefix: Vec = rand::thread_rng() + .sample_iter(rand::distributions::Standard) + .take(rand::thread_rng().gen_range(5, 11)) + .collect(); + + let blackbox = |mut attacker_data: Vec| -> Vec { + let mut data: Vec = prefix.clone(); + data.append(&mut attacker_data); + data.append(&mut plaintext.clone()); + let rv = aes.ecb(data, Mode::Encrypt).unwrap(); + rv + }; + + let starting_size = blackbox(Vec::new()).len(); + let (first_increment, next_size) = (0..) + .map(|x: usize| (x, blackbox(std::iter::repeat(0u8).take(x).collect()).len())) + .find(|(_, x)| *x != starting_size) + .unwrap(); + println!(" First size increment at {}", first_increment); + + let block_size = next_size - starting_size; + println!(" Block size is {}", block_size); + assert!(block_size == 16); + + let long_block = blackbox(std::iter::repeat(0u8).take(1280).collect()); + let using_ecb = long_block[480..496] == long_block[496..512]; + println!(" Using ECB: {}", using_ecb); + assert!(using_ecb); + + let all_zero_ciphertext = blackbox(std::iter::repeat(0u8).take(block_size).collect()); + let first_different_block = |x: usize| -> usize { + let data: Vec = std::iter::repeat(0u8) + .take(x) + .chain(std::iter::once(1u8)) + .chain(std::iter::repeat(0u8).take(block_size - 1 - x)) + .collect(); + let data: Vec = blackbox(data); + data.chunks_exact(block_size) + .zip(all_zero_ciphertext.chunks_exact(block_size)) + .enumerate() + .find(|(_, (a, b))| a != b) + .unwrap() + .0 + }; + let start_block = first_different_block(0); + let mut prefix_size = start_block * block_size + block_size - 1; + for i in 2..block_size { + let diff_block = first_different_block(i); + if diff_block == start_block { + prefix_size = start_block * block_size + block_size - 1 - i; + } + } + println!(" Prefix length is {}", prefix_size); + assert!(prefix_size == prefix.len()); + + let data_length = starting_size - first_increment - prefix_size; + let mut known: Vec = Vec::with_capacity(data_length); + println!(" Data length: {}", data_length); + assert!(data_length == plaintext.len()); + + let prefix_total = (prefix_size + block_size - 1) / block_size * block_size; + let prefix_nulls = prefix_total - prefix_size; + let starting_size = (prefix_total + data_length + block_size - 1) / block_size * block_size; + + for _ in 0..data_length { + let nulls: Vec = std::iter::repeat(0u8) + .take(starting_size - prefix_size - known.len() - 1) + .collect(); + let target_block = &blackbox(nulls.clone())[prefix_total..starting_size]; + let mut found = false; + 'byteiter: for b in 0u8..=255u8 { + let this_block = &blackbox( + nulls + .iter() + .chain(known.iter()) + .chain(std::iter::once(&b)) + .cloned() + .take(starting_size) + .collect(), + )[prefix_total..starting_size]; + if this_block == target_block { + known.push(b); + found = true; + break 'byteiter; + } + } + assert!(found); + } + assert!(known == plaintext); +} + +fn q15() { + println!("Running q15"); + let input = b"ICE ICE BABY\x05\x05\x05\x05"; + let output = Bytes::from(&input[..]); + let output = output.unpad(); + assert!(output == None); + + let input = b"ICE ICE BABY\x04\x04\x04\x04"; + let output = Bytes::from(&input[..]); + let output = output.unpad().unwrap(); + assert!(output.to_string().unwrap() == "ICE ICE BABY"); +} + fn main() { q01(); q02(); @@ -299,4 +430,6 @@ fn main() { q11(); q12(); q13(); + q14(); + q15(); }