Finish challenge 17.
This commit is contained in:
@@ -107,10 +107,13 @@ impl Bytes {
|
|||||||
}
|
}
|
||||||
let last_block_index = self.len() / block_size - 1;
|
let last_block_index = self.len() / block_size - 1;
|
||||||
let last_block = self.get_block(last_block_index, block_size).0;
|
let last_block = self.get_block(last_block_index, block_size).0;
|
||||||
let pad_byte_count = last_block[block_size - 1];
|
let pad_byte = last_block[block_size - 1];
|
||||||
for i in 0..(pad_byte_count as usize) {
|
if pad_byte < 1 || pad_byte > 16 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for i in 0..(pad_byte as usize) {
|
||||||
let byte = last_block[block_size - 1 - i];
|
let byte = last_block[block_size - 1 - i];
|
||||||
if byte != pad_byte_count {
|
if byte != pad_byte {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/main.rs
32
src/main.rs
@@ -8,21 +8,21 @@ mod set2;
|
|||||||
mod set3;
|
mod set3;
|
||||||
|
|
||||||
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();
|
||||||
// set2::challenge15();
|
set2::challenge15();
|
||||||
// set2::challenge16();
|
set2::challenge16();
|
||||||
set3::challenge17();
|
set3::challenge17();
|
||||||
}
|
}
|
||||||
|
|||||||
72
src/set3.rs
72
src/set3.rs
@@ -13,7 +13,8 @@ pub fn challenge17() {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt(key: &Bytes) -> (Bytes, Bytes) {
|
let key = Bytes::random(16);
|
||||||
|
let encrypt = || -> (Bytes, Bytes, usize) {
|
||||||
// The first function should select at random one of the ten strings
|
// The first function should select at random one of the ten strings
|
||||||
let cleartexts = read("data/17.txt");
|
let cleartexts = read("data/17.txt");
|
||||||
let index: usize = rand::thread_rng().gen_range(0..cleartexts.len());
|
let index: usize = rand::thread_rng().gen_range(0..cleartexts.len());
|
||||||
@@ -23,37 +24,72 @@ pub fn challenge17() {
|
|||||||
cleartext.pad_pkcs7(16);
|
cleartext.pad_pkcs7(16);
|
||||||
|
|
||||||
// CBC-encrypt it under that key, providing the caller the ciphertext
|
// CBC-encrypt it under that key, providing the caller the ciphertext
|
||||||
// and IV.
|
// and IV and cleartext index for check.
|
||||||
let iv = Bytes::random(16);
|
let iv = Bytes::random(16);
|
||||||
(cbc::encrypt(&key, &iv, &cleartext), iv)
|
(cbc::encrypt(&key, &iv, &cleartext), iv, index)
|
||||||
}
|
};
|
||||||
|
|
||||||
// generate a random AES key (which it should save for all future encryptions)
|
// generate a random AES key (which it should save for all future encryptions)
|
||||||
let key = Bytes::random(16);
|
let (cipher, iv, cleartext_index) = encrypt();
|
||||||
let (cipher, _iv) = encrypt(&key);
|
|
||||||
let decryption_oracle = |iv: &Bytes, cipher: &Bytes| -> bool {
|
let decryption_oracle = |iv: &Bytes, cipher: &Bytes| -> bool {
|
||||||
// The second function should consume the ciphertext produced by the
|
// The second function should consume the ciphertext produced by the
|
||||||
// first function, decrypt it, check its padding, and return true or
|
// first function, decrypt it, check its padding, and return true or
|
||||||
// false depending on whether the padding is valid.
|
// false depending on whether the padding is valid.
|
||||||
let roundtrip = cbc::decrypt(&key, iv, cipher);
|
let cleartext = cbc::decrypt(&key, iv, cipher);
|
||||||
roundtrip.has_valid_pkcs7(16)
|
cleartext.has_valid_pkcs7(16)
|
||||||
};
|
};
|
||||||
|
|
||||||
let attack_block = |cipher_block: &Bytes| -> Bytes {
|
let attack_block = |previous_block: &Bytes, cipher_block: &Bytes| -> Bytes {
|
||||||
|
// Good explanation: https://robertheaton.com/2013/07/29/padding-oracle-attack/
|
||||||
let block_size = cipher_block.len();
|
let block_size = cipher_block.len();
|
||||||
let mut iv = Bytes::random(block_size);
|
let mut attack_vector = Bytes::random(block_size);
|
||||||
|
let mut intermittent_result = vec![];
|
||||||
|
|
||||||
for i in 0..=255 {
|
for pad_byte in 1..=block_size {
|
||||||
iv.0[15] = i;
|
// preset attack vector so that paddinig is [1], [2, 2], [3, 3, 3], and so on.
|
||||||
|
attack_vector.0[block_size - pad_byte] = pad_byte as u8;
|
||||||
|
for i in 0..(pad_byte - 1) {
|
||||||
|
attack_vector.0[block_size - 1 - i] = (pad_byte as u8) ^ intermittent_result[i];
|
||||||
|
}
|
||||||
|
|
||||||
if decryption_oracle(&iv, cipher_block) {
|
// guess attack vector so that padding is valid
|
||||||
println!("{i:#016b}");
|
let guess_index = block_size - pad_byte;
|
||||||
|
for guess in 0..=255 {
|
||||||
|
attack_vector.0[guess_index] = guess;
|
||||||
|
if decryption_oracle(&attack_vector, cipher_block) {
|
||||||
|
// println!("{guess:#016b}");
|
||||||
|
let c = (guess as u8) ^ (pad_byte as u8);
|
||||||
|
intermittent_result.push(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Bytes(vec![])
|
|
||||||
|
// transform intermittent result by xoring it with previous block
|
||||||
|
intermittent_result.reverse();
|
||||||
|
let xored: Vec<u8> = Iterator::zip(previous_block.0.iter(), intermittent_result)
|
||||||
|
.map(|z| z.0 ^ z.1)
|
||||||
|
.collect();
|
||||||
|
assert_eq!(xored.len(), block_size);
|
||||||
|
Bytes(xored)
|
||||||
};
|
};
|
||||||
|
|
||||||
let _block_count = cipher.len() / 16;
|
// Attack block by block.
|
||||||
let first_block = attack_block(&cipher.get_block(0, 16));
|
let mut roundtrip = Bytes(vec![]);
|
||||||
println!("[xxxx] Challenge 17: {:?}", first_block);
|
let block_count = cipher.len() / 16;
|
||||||
|
for block in 0..block_count {
|
||||||
|
let mut clear_block = if block == 0 {
|
||||||
|
attack_block(&iv, &cipher.get_block(0, 16))
|
||||||
|
} else {
|
||||||
|
attack_block(
|
||||||
|
&cipher.get_block(block - 1, 16),
|
||||||
|
&cipher.get_block(block, 16),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
roundtrip.0.append(&mut clear_block.0);
|
||||||
|
}
|
||||||
|
roundtrip.remove_pkcs7(16);
|
||||||
|
let cleartexts = read("data/17.txt");
|
||||||
|
let cleartext = Bytes(cleartexts[cleartext_index].0.to_vec());
|
||||||
|
assert_eq!(roundtrip, cleartext);
|
||||||
|
println!("[okay] Challenge 17: {}", roundtrip.to_utf8());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user