diff options
-rw-r--r-- | src/aes.rs | 39 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | tests/lib.rs | 30 |
3 files changed, 62 insertions, 8 deletions
@@ -140,6 +140,23 @@ pub fn crack_padded_aes_128_ecb<F> (f: &F) -> Vec<u8> where F: Fn(&[u8]) -> Vec< return unpad_pkcs7(&plaintext[..]).to_vec(); } +pub fn crack_padded_aes_128_ecb_with_prefix<F> (f: &F) -> Vec<u8> where F: Fn(&[u8]) -> Vec<u8> { + let (block_size, prefix_len) = find_block_size_and_fixed_prefix_len(f); + let wrapped_f = |input: &[u8]| { + let alignment_padding = block_size - (prefix_len % block_size); + let padded_input: Vec<u8> = std::iter::repeat(b'A') + .take(alignment_padding) + .chain(input.iter().map(|x| *x)) + .collect(); + return f(&padded_input[..]) + .iter() + .skip(prefix_len + alignment_padding) + .map(|x| *x) + .collect(); + }; + return crack_padded_aes_128_ecb(&wrapped_f); +} + pub fn crack_querystring_aes_128_ecb<F> (encrypter: F) -> (String, Vec<Vec<u8>>) where F: Fn(&str) -> Vec<u8> { fn incr_map_element (map: &mut HashMap<Vec<u8>, usize>, key: Vec<u8>) { if let Some(val) = map.get_mut(&key) { @@ -240,10 +257,15 @@ fn count_duplicate_blocks (input: &[u8], block_size: usize) -> usize { } fn find_block_size<F> (f: &F) -> usize where F: Fn(&[u8]) -> Vec<u8> { - let fixed_prefix_len = find_fixed_prefix_len(f); + let (block_size, _) = find_block_size_and_fixed_prefix_len(f); + return block_size; +} + +fn find_block_size_and_fixed_prefix_len<F> (f: &F) -> (usize, usize) where F: Fn(&[u8]) -> Vec<u8> { + let fixed_prefix_len = find_fixed_block_prefix_len(f); let byte = b'A'; - let mut prev = f(&[byte]); - let mut len = 2; + let mut prev = f(&[b'f']); + let mut len = 0; loop { let prefix: Vec<u8> = std::iter::repeat(byte) .take(len) @@ -251,11 +273,12 @@ fn find_block_size<F> (f: &F) -> usize where F: Fn(&[u8]) -> Vec<u8> { let next = f(&prefix[..]); let prefix_len = shared_prefix_len( - prev.iter().skip(fixed_prefix_len), - next.iter().skip(fixed_prefix_len) + prev.iter(), + next.iter() ); - if prefix_len > 0 { - return prefix_len; + if prefix_len > fixed_prefix_len { + let block_size = prefix_len - fixed_prefix_len; + return (block_size, fixed_prefix_len + block_size - (len - 1)); } prev = next; @@ -263,7 +286,7 @@ fn find_block_size<F> (f: &F) -> usize where F: Fn(&[u8]) -> Vec<u8> { } } -fn find_fixed_prefix_len<F> (f: &F) -> usize where F: Fn(&[u8]) -> Vec<u8> { +fn find_fixed_block_prefix_len<F> (f: &F) -> usize where F: Fn(&[u8]) -> Vec<u8> { let ciphertext1 = f(b""); let ciphertext2 = f(b"A"); return shared_prefix_len(ciphertext1.iter(), ciphertext2.iter()); @@ -16,6 +16,7 @@ pub use aes::encrypt_aes_128_cbc; pub use aes::find_aes_128_ecb_encrypted_string; pub use aes::detect_ecb_cbc; pub use aes::crack_padded_aes_128_ecb; +pub use aes::crack_padded_aes_128_ecb_with_prefix; pub use aes::crack_querystring_aes_128_ecb; pub use base64::to_base64; pub use http::parse_query_string; diff --git a/tests/lib.rs b/tests/lib.rs index 3ddeae1..3f66d16 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -252,3 +252,33 @@ fn problem_13 () { decrypter(ciphertext).map(|params| params == expected).unwrap_or(false) })); } + +#[test] +fn problem_14 () { + let padding = b"Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWct\ + dG9wIGRvd24gc28gbXkgaGFpciBjYW4gYmxvdwpU\ + aGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq\ + dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5v\ + LCBJIGp1c3QgZHJvdmUgYnkK".from_base64().unwrap(); + let front_padding: Vec<u8> = thread_rng() + .gen_iter() + .take(thread_rng().gen_range(1, 100)) + .collect(); + let fixed_padding = |input: &[u8]| -> Vec<u8> { + return front_padding + .iter() + .chain(input.iter()) + .chain(padding.iter()) + .map(|x| *x) + .collect() + }; + + let key = random_aes_128_key(); + let random_encrypter = |input: &[u8]| { + let padded_input = fixed_padding(input); + return matasano::encrypt_aes_128_ecb(&padded_input[..], &key[..]); + }; + + let got = matasano::crack_padded_aes_128_ecb_with_prefix(&random_encrypter); + assert_eq!(got, padding); +} |