summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2015-03-28 03:28:25 -0400
committerJesse Luehrs <doy@tozt.net>2015-03-28 03:28:25 -0400
commit1505a949589b17837319a292a07e1c15ed9296e4 (patch)
tree551764e450e4a8f0b9e367dc331c1bd1b87aab62
parentcf487bb3c09d4e0532bd452ad3aa5eb292b176ba (diff)
downloadmatasano-1505a949589b17837319a292a07e1c15ed9296e4.tar.gz
matasano-1505a949589b17837319a292a07e1c15ed9296e4.zip
problem 17
-rw-r--r--src/aes.rs33
-rw-r--r--src/lib.rs1
-rw-r--r--tests/lib.rs48
3 files changed, 82 insertions, 0 deletions
diff --git a/src/aes.rs b/src/aes.rs
index a4b5059..0d262cb 100644
--- a/src/aes.rs
+++ b/src/aes.rs
@@ -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;
diff --git a/src/lib.rs b/src/lib.rs
index 198ce83..d11f20e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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);
+ }
+}