Finish challenge 14.
This commit is contained in:
@@ -6,6 +6,10 @@ use std::fmt::Write; // need to import this trait
|
|||||||
pub struct Bytes(pub Vec<u8>);
|
pub struct Bytes(pub Vec<u8>);
|
||||||
|
|
||||||
impl Bytes {
|
impl Bytes {
|
||||||
|
pub fn empty() -> Bytes {
|
||||||
|
Bytes(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_utf8(s: &str) -> Bytes {
|
pub fn from_utf8(s: &str) -> Bytes {
|
||||||
Bytes(s.as_bytes().iter().map(|c| c.clone()).collect())
|
Bytes(s.as_bytes().iter().map(|c| c.clone()).collect())
|
||||||
}
|
}
|
||||||
@@ -16,6 +20,10 @@ impl Bytes {
|
|||||||
String::from(std::str::from_utf8(&v).unwrap())
|
String::from(std::str::from_utf8(&v).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_block(&self, block_index: usize, block_size: usize) -> Bytes {
|
pub fn get_block(&self, block_index: usize, block_size: usize) -> Bytes {
|
||||||
Bytes(self.0[(block_index * block_size)..(block_index + 1) * block_size].to_vec())
|
Bytes(self.0[(block_index * block_size)..(block_index + 1) * block_size].to_vec())
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/main.rs
26
src/main.rs
@@ -7,18 +7,18 @@ mod set1;
|
|||||||
mod set2;
|
mod set2;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// set1::challenge1();
|
set1::challenge1();
|
||||||
// set1::challenge2();
|
set1::challenge2();
|
||||||
// set1::challenge3();
|
set1::challenge3();
|
||||||
// set1::challenge4();
|
set1::challenge4();
|
||||||
// set1::challenge5();
|
set1::challenge5();
|
||||||
// set1::challenge6();
|
set1::challenge6();
|
||||||
// set1::challenge7();
|
set1::challenge7();
|
||||||
// set1::challenge8();
|
set1::challenge8();
|
||||||
// set2::challenge9();
|
set2::challenge9();
|
||||||
// set2::challenge10();
|
set2::challenge10();
|
||||||
// set2::challenge11();
|
set2::challenge11();
|
||||||
// set2::challenge12();
|
set2::challenge12();
|
||||||
// set2::challenge13();
|
set2::challenge13();
|
||||||
set2::challenge14();
|
set2::challenge14();
|
||||||
}
|
}
|
||||||
|
|||||||
112
src/set2.rs
112
src/set2.rs
@@ -183,17 +183,17 @@ pub fn challenge12() {
|
|||||||
let key = Bytes::random(16); // consistent but unknown key
|
let key = Bytes::random(16); // consistent but unknown key
|
||||||
assert_eq!(get_block_size(&key), 16); // 1. discover block size
|
assert_eq!(get_block_size(&key), 16); // 1. discover block size
|
||||||
assert_eq!(is_encryption_ecb(&key), true); // 2. confirm oracle uses ecb
|
assert_eq!(is_encryption_ecb(&key), true); // 2. confirm oracle uses ecb
|
||||||
let rountrip_text = decode(&key); // 3.-6.
|
let roundtrip_text = decode(&key); // 3.-6.
|
||||||
let clear_text = read("data/12.txt");
|
let clear_text = read("data/12.txt");
|
||||||
|
|
||||||
// 138 (instead of 139); I think we get one additional byte because we guess
|
// 138 (instead of 139); I think we get one additional byte because we guess
|
||||||
// the first padding byte. The right approach would be to remove the last
|
// the first padding byte. The right approach would be to remove the last
|
||||||
// byte, encrypt it, and then compare it to the result of the encryption
|
// byte, encrypt it, and then compare it to the result of the encryption
|
||||||
// oracle, but this approach is fine too.
|
// oracle, but this approach is fine too.
|
||||||
assert_eq!(rountrip_text.0[..138], clear_text.0);
|
assert_eq!(roundtrip_text.0[..138], clear_text.0);
|
||||||
println!(
|
println!(
|
||||||
"[okay] Challenge 12: {}",
|
"[okay] Challenge 12: {}",
|
||||||
rountrip_text.to_utf8()[..17].to_string()
|
roundtrip_text.to_utf8()[..17].to_string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,27 +284,109 @@ pub fn challenge14() {
|
|||||||
|
|
||||||
for i in 1..10 {
|
for i in 1..10 {
|
||||||
let block_size = i * 16;
|
let block_size = i * 16;
|
||||||
let chunks: Vec<&[u8]> = cipher.0.chunks(block_size).collect();
|
match get_duplicated_block_indices(&cipher, block_size) {
|
||||||
for i in 0..chunks.len() {
|
Some((_, _)) => {
|
||||||
for j in (i + 1)..chunks.len() {
|
|
||||||
if chunks[i] == chunks[j] {
|
|
||||||
return block_size;
|
return block_size;
|
||||||
}
|
}
|
||||||
}
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
let prefix = Bytes::random_range(0, 200);
|
fn get_duplicated_block_indices(cipher: &Bytes, block_size: usize) -> Option<(usize, usize)> {
|
||||||
let key = Bytes::random(16); // consistent but unknown key
|
let chunks: Vec<&[u8]> = cipher.0.chunks(block_size).collect();
|
||||||
assert_eq!(get_block_size(&prefix, &key), 16);
|
for i in 0..chunks.len() {
|
||||||
let _clear_text = read("data/12.txt");
|
for j in (i + 1)..chunks.len() {
|
||||||
|
if chunks[i] == chunks[j] {
|
||||||
|
return Some((i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_prefix_size(prefix: &Bytes, key: &Bytes) -> usize {
|
||||||
|
let block_size = get_block_size(&prefix, &key);
|
||||||
|
let duplicated_text = Bytes::from_utf8("aaaabbbbccccddddaaaabbbbccccdddd").0;
|
||||||
|
|
||||||
|
for i in 0..block_size {
|
||||||
|
let mut padding = vec![b'a'; i];
|
||||||
|
padding.append(&mut duplicated_text.to_vec());
|
||||||
|
let cipher = encryption_oracle(prefix, key, &Bytes(padding));
|
||||||
|
match get_duplicated_block_indices(&cipher, block_size) {
|
||||||
|
Some((first_block, _)) => {
|
||||||
|
return block_size * first_block - i;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(prefix: &Bytes, key: &Bytes) -> Bytes {
|
||||||
|
let block_size = get_block_size(prefix, key);
|
||||||
|
let prefix_size = get_prefix_size(prefix, key);
|
||||||
|
let prefix_padding_size = block_size - (prefix_size % block_size);
|
||||||
|
let prefix_padding = Bytes(vec![b'a'; prefix_padding_size]);
|
||||||
|
let block_count = encryption_oracle(&prefix, &key, &prefix_padding).len() / block_size;
|
||||||
|
let first_block_index = (prefix_size + prefix_padding_size) / block_size;
|
||||||
|
let mut clear_text = vec![];
|
||||||
|
|
||||||
// AES-128-ECB(random-prefix || attacker-controlled || target-bytes, random-key)
|
// AES-128-ECB(random-prefix || attacker-controlled || target-bytes, random-key)
|
||||||
// rrrrrrrrrrrrrrrrrrraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaathe real text
|
// rrrrrrrrrrrrrrrrrrrpppppppppppppaaaaaaaaaaaaaaathe real text
|
||||||
// 0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f
|
// 0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f0..34..78..bc..f
|
||||||
// 0 1 2 4 5
|
// 0 1 ^ 2 4 5
|
||||||
|
// | ^ first_block_index
|
||||||
|
// \ prefix_padding
|
||||||
|
for block_index in first_block_index..block_count {
|
||||||
|
let mut clear_text_block = vec![];
|
||||||
|
for padding_length in (0..block_size).rev() {
|
||||||
|
let full_padding_text = vec![b'-'; prefix_padding_size + padding_length];
|
||||||
|
let expected = encryption_oracle(prefix, key, &Bytes(full_padding_text.to_vec()));
|
||||||
|
let expected_block = expected.get_block(block_index, block_size);
|
||||||
|
|
||||||
println!("[xxxx] Challenge 14: {}", get_block_size(&prefix, &key));
|
let mut known_text = if block_index == first_block_index {
|
||||||
|
full_padding_text.to_vec()
|
||||||
|
} else {
|
||||||
|
let mut prefix_padding = vec![b'-'; prefix_padding_size];
|
||||||
|
let clear_text_offset = ((block_index - first_block_index - 1) * block_size)
|
||||||
|
+ (block_size - padding_length);
|
||||||
|
prefix_padding.append(
|
||||||
|
&mut clear_text[clear_text_offset..(clear_text_offset + padding_length)]
|
||||||
|
.to_vec(),
|
||||||
|
);
|
||||||
|
prefix_padding
|
||||||
|
};
|
||||||
|
known_text.append(&mut clear_text_block.to_vec());
|
||||||
|
|
||||||
|
for i in 0..255 {
|
||||||
|
let mut guess_text = known_text.to_vec();
|
||||||
|
guess_text.push(i);
|
||||||
|
let cipher_block = encryption_oracle(prefix, key, &Bytes(guess_text))
|
||||||
|
.get_block(first_block_index, block_size);
|
||||||
|
if cipher_block.0 == expected_block.0 {
|
||||||
|
clear_text_block.push(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clear_text.append(&mut clear_text_block);
|
||||||
|
}
|
||||||
|
// We get last byte from cbs padding so remove it.
|
||||||
|
Bytes(clear_text[0..(clear_text.len() - 1)].to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
let prefix = Bytes::random_range(0, 200);
|
||||||
|
let key = Bytes::random(16);
|
||||||
|
assert_eq!(get_block_size(&prefix, &key), 16);
|
||||||
|
let prefix_len = get_prefix_size(&prefix, &key);
|
||||||
|
assert_eq!(prefix.len(), prefix_len);
|
||||||
|
let roundtrip_text = decode(&prefix, &key);
|
||||||
|
let clear_text = read("data/12.txt");
|
||||||
|
assert_eq!(roundtrip_text, clear_text);
|
||||||
|
println!(
|
||||||
|
"[okay] Challenge 14: {}",
|
||||||
|
roundtrip_text.to_utf8()[..17].to_string()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user