summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock15
-rw-r--r--Cargo.toml1
-rw-r--r--src/aes.rs26
-rw-r--r--src/lib.rs2
-rw-r--r--tests/lib.rs58
5 files changed, 101 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 7a7b9ec..e3a428c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,7 @@ name = "matasano"
version = "0.0.1"
dependencies = [
"openssl 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -25,6 +26,11 @@ dependencies = [
]
[[package]]
+name = "log"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "openssl"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -55,6 +61,15 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "rand"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "rustc-serialize"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 4a8eb22..573fc44 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,3 +7,4 @@ authors = ["Jesse Luehrs <doy@tozt.net>"]
[dependencies]
rustc-serialize = "0.3.3"
openssl = "0.5.1"
+rand = "0.2.0"
diff --git a/src/aes.rs b/src/aes.rs
index 9baf595..e4a7184 100644
--- a/src/aes.rs
+++ b/src/aes.rs
@@ -1,8 +1,16 @@
-use openssl;
+use std;
use std::collections::HashSet;
+use openssl;
+
use primitives::{fixed_xor, pad_pkcs7, unpad_pkcs7};
+#[derive(PartialEq,Eq,Debug)]
+pub enum BlockCipherMode {
+ ECB,
+ CBC,
+}
+
pub fn decrypt_aes_128_ecb (bytes: &[u8], key: &[u8]) -> Vec<u8> {
return openssl::crypto::symm::decrypt(
openssl::crypto::symm::Type::AES_128_ECB,
@@ -65,6 +73,22 @@ pub fn find_aes_128_ecb_encrypted_string (inputs: &[Vec<u8>]) -> Vec<u8> {
return found;
}
+pub fn detect_ecb_cbc<F> (f: F) -> BlockCipherMode where F: Fn(&[u8]) -> Vec<u8> {
+ let plaintext: Vec<u8> = (0..16)
+ .cycle()
+ .take(32)
+ .flat_map(|n| std::iter::repeat(n).take(17))
+ .collect();
+ let ciphertext = f(&plaintext[..]);
+
+ if count_duplicate_blocks(&ciphertext[..]) >= 16 {
+ return BlockCipherMode::ECB;
+ }
+ else {
+ return BlockCipherMode::CBC;
+ }
+}
+
fn count_duplicate_blocks (input: &[u8]) -> usize {
let mut set = HashSet::new();
let mut dups = 0;
diff --git a/src/lib.rs b/src/lib.rs
index 901cbd6..48418ab 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,11 +7,13 @@ mod data;
mod primitives;
mod xor;
+pub use aes::BlockCipherMode;
pub use aes::decrypt_aes_128_ecb;
pub use aes::decrypt_aes_128_cbc;
pub use aes::encrypt_aes_128_ecb;
pub use aes::encrypt_aes_128_cbc;
pub use aes::find_aes_128_ecb_encrypted_string;
+pub use aes::detect_ecb_cbc;
pub use base64::to_base64;
pub use primitives::fixed_xor;
pub use primitives::pad_pkcs7;
diff --git a/tests/lib.rs b/tests/lib.rs
index 553213a..d1bc806 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -1,9 +1,11 @@
extern crate matasano;
extern crate "rustc-serialize" as serialize;
+extern crate rand;
use std::io::prelude::*;
use std::fs::File;
+use rand::{Rng, thread_rng};
use serialize::base64::FromBase64;
use serialize::hex::FromHex;
@@ -29,6 +31,16 @@ fn read (filename: &str) -> Vec<u8> {
return outfh.bytes().map(|c| c.unwrap()).collect();
}
+fn random_aes_128_key () -> [u8; 16] {
+ let mut key = [0; 16];
+ thread_rng().fill_bytes(&mut key);
+ return key;
+}
+
+fn coinflip () -> bool {
+ thread_rng().gen()
+}
+
#[test]
fn problem_1 () {
let hex = "49276d206b696c6c696e6720796f757220627261\
@@ -127,3 +139,49 @@ fn problem_10 () {
let got = matasano::decrypt_aes_128_cbc(&ciphertext[..], key, &[0; 16]);
assert_eq!(got, plaintext);
}
+
+#[test]
+fn problem_11 () {
+ static mut last_mode: matasano::BlockCipherMode = matasano::BlockCipherMode::ECB;
+
+ fn random_padding (input: &[u8]) -> Vec<u8> {
+ let front_padding: Vec<u8> = thread_rng()
+ .gen_iter()
+ .take(thread_rng().gen_range(5, 10))
+ .collect();
+ let back_padding: Vec<u8> = thread_rng()
+ .gen_iter()
+ .take(thread_rng().gen_range(5, 10))
+ .collect();
+ return front_padding
+ .iter()
+ .chain(input.iter())
+ .chain(back_padding.iter())
+ .map(|x| *x)
+ .collect()
+ }
+
+ fn random_encrypter (input: &[u8]) -> Vec<u8> {
+ let key = random_aes_128_key();
+ let padded_input = random_padding(input);
+ if coinflip() {
+ unsafe {
+ last_mode = matasano::BlockCipherMode::ECB;
+ }
+ return matasano::encrypt_aes_128_ecb(&padded_input[..], &key[..]);
+ }
+ else {
+ unsafe {
+ last_mode = matasano::BlockCipherMode::CBC;
+ }
+ let iv = random_aes_128_key();
+ return matasano::encrypt_aes_128_cbc(&padded_input[..], &key[..], &iv[..]);
+ }
+ }
+
+ for _ in 0..100 {
+ let got = matasano::detect_ecb_cbc(random_encrypter);
+ let expected = unsafe { &last_mode };
+ assert_eq!(&got, expected);
+ }
+}