Finish challenge 14.

This commit is contained in:
2022-06-25 18:28:38 -04:00
parent 52222e90e9
commit f6b4c98826
3 changed files with 121 additions and 31 deletions

View File

@@ -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())
} }

View File

@@ -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();
} }

View File

@@ -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()
);
} }