diff options
-rw-r--r-- | src/aes.rs | 33 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | tests/lib.rs | 48 |
3 files changed, 82 insertions, 0 deletions
@@ -255,6 +255,39 @@ pub fn crack_cbc_bitflipping<F> (f: &F) -> Vec<u8> where F: Fn(&str) -> Vec<u8> return ciphertext; } +pub fn crack_cbc_padding_oracle<F> (iv: &[u8], ciphertext: &[u8], f: &F) -> Vec<u8> where F: Fn(&[u8], &[u8]) -> bool { + let mut prev = iv; + let mut plaintext = vec![]; + for block in ciphertext.chunks(16) { + let mut plaintext_block = vec![]; + 'BYTE: for byte in 0..16u8 { + for c_int in 0..256 { + let c = (255 - c_int) as u8; + let offset = (16 - byte - 1) as usize; + let mut iv: Vec<u8> = prev + .iter() + .take(offset) + .map(|x| *x) + .collect(); + iv.push(prev[offset] ^ c ^ (byte + 1)); + for i in 0..(byte as usize) { + iv.push(prev[offset + i + 1] ^ plaintext_block[i] ^ (byte + 1)); + } + if f(&iv[..], block) { + plaintext_block.insert(0, c); + continue 'BYTE; + } + } + panic!("no byte found! ({})", byte); + } + for c in plaintext_block { + plaintext.push(c); + } + prev = block; + } + return unpad_pkcs7(&plaintext[..]).unwrap().to_vec(); +} + fn count_duplicate_blocks (input: &[u8], block_size: usize) -> usize { let mut set = HashSet::new(); let mut dups = 0; @@ -19,6 +19,7 @@ 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 aes::crack_cbc_bitflipping; +pub use aes::crack_cbc_padding_oracle; pub use base64::to_base64; pub use http::parse_query_string; pub use http::create_query_string; diff --git a/tests/lib.rs b/tests/lib.rs index 4ebeebf..5fb8261 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -344,3 +344,51 @@ fn problem_16 () { let ciphertext = matasano::crack_cbc_bitflipping(&encode); assert!(verify(&ciphertext[..])); } + +#[test] +fn problem_17 () { + let strings = [ + &b"MDAwMDAwTm93IHRoYXQgdGhlIHBhcnR5IGlzIGp1bXBpbmc="[..], + &b"MDAwMDAxV2l0aCB0aGUgYmFzcyBraWNrZWQgaW4gYW5kIHRoZSBWZWdhJ3MgYXJlIHB1bXBpbic="[..], + &b"MDAwMDAyUXVpY2sgdG8gdGhlIHBvaW50LCB0byB0aGUgcG9pbnQsIG5vIGZha2luZw=="[..], + &b"MDAwMDAzQ29va2luZyBNQydzIGxpa2UgYSBwb3VuZCBvZiBiYWNvbg=="[..], + &b"MDAwMDA0QnVybmluZyAnZW0sIGlmIHlvdSBhaW4ndCBxdWljayBhbmQgbmltYmxl"[..], + &b"MDAwMDA1SSBnbyBjcmF6eSB3aGVuIEkgaGVhciBhIGN5bWJhbA=="[..], + &b"MDAwMDA2QW5kIGEgaGlnaCBoYXQgd2l0aCBhIHNvdXBlZCB1cCB0ZW1wbw=="[..], + &b"MDAwMDA3SSdtIG9uIGEgcm9sbCwgaXQncyB0aW1lIHRvIGdvIHNvbG8="[..], + &b"MDAwMDA4b2xsaW4nIGluIG15IGZpdmUgcG9pbnQgb2g="[..], + &b"MDAwMDA5aXRoIG15IHJhZy10b3AgZG93biBzbyBteSBoYWlyIGNhbiBibG93"[..], + ]; + let key = random_aes_128_key(); + + static mut chosen_plaintext: Option<&'static[u8]> = None; + let encrypter = || { + let plaintext = strings[thread_rng().gen_range(0, strings.len())]; + unsafe { chosen_plaintext = Some(plaintext) }; + let iv = random_aes_128_key(); + return ( + iv, + matasano::encrypt_aes_128_cbc(plaintext, &key[..], &iv[..]) + ); + }; + + let validator = |iv: &[u8], ciphertext: &[u8]| { + let plaintext = matasano::decrypt_aes_128_cbc( + ciphertext, + &key[..], + &iv[..] + ); + return plaintext.is_some(); + }; + + let (iv, ciphertext) = encrypter(); + for _ in 0..5 { + let plaintext = matasano::crack_cbc_padding_oracle( + &iv[..], + &ciphertext[..], + &validator + ); + let expected = unsafe { &chosen_plaintext.unwrap() }; + assert_eq!(&plaintext, expected); + } +} |