diff --git a/Cargo.toml b/Cargo.toml index d74816a..47f5564 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] hex = "^0.3" -openssl = "^0.10" base64 = "^0.2" typenum = "^1.0" +openssl = "^0.10" +rand = "^0.7" \ No newline at end of file diff --git a/src/bytes.rs b/src/bytes.rs index d6db992..b907f4c 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -149,6 +149,20 @@ impl Bytes { self } + pub fn unpad(mut self) -> Option { + let len = self.len(); + + let last_byte = self.data[len-1]; + let end_start = len - (last_byte as usize); + match self.data.iter().skip(end_start).all(|x| *x == last_byte) { + true => { + self.data.resize(end_start, 0u8); + Some(self) + }, + false => None, + } + } + pub fn pad_to_multiple(self, num: usize) -> Self { let num = (num - (self.len() % num)) as u8; self.pad(num) diff --git a/src/crypto.rs b/src/crypto.rs index f8c2e31..3632c82 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,51 +1,121 @@ use crate::bytes::Bytes; -pub use openssl::symm::Mode; +pub use openssl::symm::{Cipher, Crypter, Mode}; pub struct Aes { key: Vec, + pad: bool, } impl Aes { const BLOCK_SIZE: usize = 16; - pub fn new(key: &[u8]) -> Option { + pub fn new(key: &[u8], pad: bool) -> Option { let len = key.len(); if len == 16 || len == 24 || len == 32 { - return Some(Aes { key: key.to_vec() }); + return Some(Aes { + key: key.to_vec(), + pad, + }); }; None } - fn cipher(&self) -> openssl::symm::Cipher { - match self.key.len() { - 16 => Some(openssl::symm::Cipher::aes_128_ecb()), - 24 => Some(openssl::symm::Cipher::aes_192_ecb()), - 32 => Some(openssl::symm::Cipher::aes_256_ecb()), - _ => None, + pub fn cbc(&self, data: Vec, iv: &[u8], mode: Mode) -> Option> { + if (!self.pad) && (data.len() % Self::BLOCK_SIZE != 0) { + return None; } - .unwrap() - } + let mut data = self.maybe_pad(data, mode); + let mut iv_ = [0u8; Self::BLOCK_SIZE]; + let mut output_buffer = [0u8; 2 * Self::BLOCK_SIZE]; - fn crypter(&self, mode: openssl::symm::Mode) -> openssl::symm::Crypter { - let cipher = self.cipher(); - openssl::symm::Crypter::new(cipher, mode, &self.key, None).unwrap() + iv_.clone_from_slice(iv); + let mut iv = iv_; + + let mut crypter = self.crypter(mode); + for chunk in data.chunks_exact_mut(Self::BLOCK_SIZE) { + match mode { + Mode::Decrypt => { + let mut old_chunk = [0u8; Self::BLOCK_SIZE]; + old_chunk.copy_from_slice(chunk); + + Self::update(&mut crypter, chunk, &mut output_buffer); + Self::xor_into(&output_buffer, &iv, chunk); + + iv.clone_from_slice(&old_chunk); + } + Mode::Encrypt => { + Self::xor(chunk, &iv); + + Self::update(&mut crypter, chunk, &mut output_buffer); + chunk.copy_from_slice(&output_buffer[0..Self::BLOCK_SIZE]); + + iv.copy_from_slice(chunk); + } + } + } + let data = self.maybe_unpad(data, mode); + Some(data.clone()) } pub fn ecb(&self, mut data: Vec, mode: Mode) -> Option> { - if data.len() % Self::BLOCK_SIZE != 0 { + if (!self.pad) && (data.len() % Self::BLOCK_SIZE != 0) { return None; } let mut crypter = self.crypter(mode); let mut output_buffer = [0u8; 2 * Self::BLOCK_SIZE]; + let mut data = self.maybe_pad(data, mode); for chunk in data.chunks_exact_mut(Self::BLOCK_SIZE) { - crypter.update(chunk, &mut output_buffer); + Self::update(&mut crypter, chunk, &mut output_buffer); chunk.copy_from_slice(&output_buffer[0..Self::BLOCK_SIZE]); } + + let data = self.maybe_unpad(data, mode); Some(data) } + fn cipher(&self) -> Cipher { + match self.key.len() { + 16 => Some(Cipher::aes_128_ecb()), + 24 => Some(Cipher::aes_192_ecb()), + 32 => Some(Cipher::aes_256_ecb()), + _ => None, + } + .unwrap() + } + + fn crypter(&self, mode: Mode) -> Crypter { + let cipher = self.cipher(); + let mut crypter = Crypter::new(cipher, mode, &self.key, None).unwrap(); + crypter.pad(false); + crypter + } + + fn maybe_pad(&self, data: Vec, mode: Mode) -> Vec { + match (self.pad, mode) { + (true, Mode::Encrypt) => Bytes { data }.pad_to_multiple(Self::BLOCK_SIZE).into(), + _ => data, + } + } + + fn maybe_unpad(&self, data: Vec, mode: Mode) -> Vec { + match (self.pad, mode) { + (true, Mode::Decrypt) => Bytes { data }.unpad().unwrap().into(), + _ => data, + } + } + + + fn update(crypter: &mut Crypter, chunk: &[u8], output: &mut [u8]) { + assert!( + Self::BLOCK_SIZE + == crypter + .update(chunk, output) + .expect("Error while updating crypter") + ); + } + fn xor(a: &mut [u8], b: &[u8]) { for (l, r) in a.iter_mut().zip(b.iter()) { *l = *l ^ r; @@ -57,40 +127,4 @@ impl Aes { *t = l ^ r; } } - - pub fn cbc(&self, data: Vec, iv: &[u8], mode: Mode) -> Option> { - let data = crate::bytes::Bytes::from(data); - let mut data = data.pad_to_multiple(Self::BLOCK_SIZE); - let mut crypter = self.crypter(mode); - let mut iv_ = [0u8; Self::BLOCK_SIZE]; - let mut output_buffer = [0u8; 2 * Self::BLOCK_SIZE]; - - iv_.clone_from_slice(iv); - let mut iv = iv_; - for chunk in data.chunks_exact_mut(Self::BLOCK_SIZE) { - match mode { - Mode::Decrypt => { - let mut old_chunk = [0u8; Self::BLOCK_SIZE]; - old_chunk.copy_from_slice(chunk); - - let len = crypter.update(chunk, &mut output_buffer); - Self::xor_into(&output_buffer, &iv, chunk); - - println!("IV: {:?}\nIB: {:?}\nOB: {:?} ({:?})\n\n", iv, old_chunk, chunk, len); - - iv.clone_from_slice(&old_chunk); - } - Mode::Encrypt => { - Self::xor(chunk, &iv); - - crypter.update(chunk, &mut output_buffer); - chunk.copy_from_slice(&output_buffer[0..Self::BLOCK_SIZE]); - - iv.copy_from_slice(chunk); - } - } - } - println!("{:?}", data); - Some(data.data.clone()) - } } diff --git a/src/main.rs b/src/main.rs index 8264ef9..5003f2c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,9 @@ mod bytes; mod crypto; mod io; -use bytes::Bytes; +use crate::bytes::Bytes; use openssl::symm::Mode; +use rand::Rng; fn q01() { println!("Running q01"); @@ -90,7 +91,7 @@ fn q06() { let key = input.find_best_xor_cipher(); let _output = input.xor_repeating(&key).to_string().unwrap(); - // println!(" {}", output); + // println!(" {}", _output.replace("\n", "\n ")); } fn q07() { @@ -100,11 +101,12 @@ fn q07() { let input = Bytes::from_base64(&input); let key = Bytes::from_string("YELLOW SUBMARINE"); let _output = Bytes { - data: crypto::Aes::new(&key).unwrap() + data: crypto::Aes::new(&key, false) + .unwrap() .ecb(input.into(), Mode::Decrypt) .unwrap(), }; - // println!(" {}", _output.to_string().unwrap()); + // println!(" {}", _output.to_string().unwrap().replace("\n", "\n ")); } fn q08() { @@ -152,8 +154,89 @@ fn q10() { let key = b"YELLOW SUBMARINE"; let iv = [0u8; 16]; - let output = crypto::Aes::new(key).unwrap().cbc(input.into(), &iv, Mode::Decrypt); - println!("{}", Bytes::from(output.unwrap()).to_string().unwrap()); + let _output = crypto::Aes::new(key, false) + .unwrap() + .cbc(input.into(), &iv, Mode::Decrypt); + // println!("{}", Bytes::from(_output.unwrap()).to_string().unwrap().replace("\n", "\n ")); +} + +fn q11() { + println!("Running q11"); + let black_box = |mut data: Vec| -> (bool, Vec) { + let mut rng = rand::thread_rng(); + let dist = rand::distributions::Standard; + 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(); + + start.append(&mut data); + start.append(&mut end); + + match rand::random() { + false => (false, aes.cbc(start, &iv, Mode::Encrypt).unwrap()), + true => (true, aes.ecb(start, Mode::Encrypt).unwrap()), + } + }; + + let oracle = |data: &Vec| -> bool { + data[16..32] == data[32..48] + }; + + for i in 0..128 { + let data = [0u8;64]; + let (ecb, data) = black_box(data.to_vec()); + assert!(ecb == oracle(&data)); + } +} + +fn q12() +{ + println!("Running q12"); + 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 blackbox = |mut data: Vec| -> Vec { + data.append(&mut plaintext.clone()); + aes.ecb(data, Mode::Encrypt).unwrap() + }; + + 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(|(i,x)| *x != starting_size).unwrap(); + 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(128).collect()); + let using_ecb = long_block[48..64] == long_block[64..80]; + println!(" Using ECB: {}", using_ecb); + assert!(using_ecb); + + println!(" First size increment at {}", first_increment); + + let data_length = starting_size - first_increment; + let mut known: Vec = Vec::with_capacity(data_length); + println!(" Data length: {}", data_length); + + for byte_index 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 + { + known.push(b); + found = true; + break 'byteiter; + } + } + assert!(found); + } + // println!(" {}", String::from_utf8(known).unwrap().replace("\n", "\n ")); } fn main() { @@ -167,4 +250,6 @@ fn main() { q08(); q09(); q10(); + q11(); + q12(); }