Solve challenge 32 and with that problem set 4
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(clippy::cast_possible_truncation)]
|
||||
#![allow(clippy::cast_precision_loss)]
|
||||
#![allow(clippy::items_after_statements)]
|
||||
#![allow(clippy::many_single_char_names)]
|
||||
#![allow(clippy::module_name_repetitions)]
|
||||
#![allow(clippy::cast_possible_truncation)]
|
||||
#![allow(clippy::unnested_or_patterns)]
|
||||
mod bytes;
|
||||
mod bytes_base64;
|
||||
@@ -18,6 +18,7 @@ mod set1;
|
||||
mod set2;
|
||||
mod set3;
|
||||
mod set4;
|
||||
mod set5;
|
||||
mod sha1;
|
||||
mod utils;
|
||||
|
||||
@@ -55,6 +56,7 @@ fn main() {
|
||||
set4::challenge29();
|
||||
set4::challenge30();
|
||||
set4::challenge31();
|
||||
set4::challenge32();
|
||||
}
|
||||
set4::challenge32();
|
||||
set5::challenge33();
|
||||
}
|
||||
|
||||
26
src/set3.rs
26
src/set3.rs
@@ -116,7 +116,6 @@ pub fn challenge18() {
|
||||
println!("[okay] Challenge 18: {cleartext}");
|
||||
}
|
||||
|
||||
|
||||
mod challenge19 {
|
||||
use crate::bytes::Bytes;
|
||||
use std::cell::RefCell;
|
||||
@@ -232,7 +231,6 @@ mod challenge19 {
|
||||
|
||||
deciphered
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn challenge19() {
|
||||
@@ -313,6 +311,18 @@ pub fn challenge21() {
|
||||
}
|
||||
|
||||
pub fn challenge22() {
|
||||
fn find_seed(rngout: u32) -> Option<u32> {
|
||||
let mut mt = mt19937::MT19937::new();
|
||||
let start = utils::unix_timestamp() - 2000;
|
||||
for seed in start..(start + 4000) {
|
||||
mt.seed(seed);
|
||||
if rngout == mt.extract_number() {
|
||||
return Some(seed);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// Wait a random number of seconds between, I don't know, 40 and 1000.
|
||||
let now = utils::unix_timestamp();
|
||||
let wait_time: u32 = rand::thread_rng().gen_range(40..1000);
|
||||
@@ -326,18 +336,6 @@ pub fn challenge22() {
|
||||
let rngout = mt.extract_number();
|
||||
|
||||
// From the 32 bit RNG output, discover the seed.
|
||||
fn find_seed(rngout: u32) -> Option<u32> {
|
||||
let mut mt = mt19937::MT19937::new();
|
||||
let start = utils::unix_timestamp() - 2000;
|
||||
for seed in start..(start + 4000) {
|
||||
mt.seed(seed);
|
||||
if rngout == mt.extract_number() {
|
||||
return Some(seed);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
let found_seed = find_seed(rngout);
|
||||
assert_eq!(seed, found_seed.unwrap());
|
||||
|
||||
|
||||
130
src/set4.rs
130
src/set4.rs
@@ -1,14 +1,7 @@
|
||||
use std::path::Path;
|
||||
use crate::{bytes::Bytes, cbc, ctr, ecb, md4, parser, sha1, utils};
|
||||
use std::path::Path;
|
||||
|
||||
pub fn challenge25() {
|
||||
let cipher = utils::read_base64("data/25.txt");
|
||||
let key = Bytes::from_utf8("YELLOW SUBMARINE");
|
||||
let plaintext = ecb::decrypt(&key, &cipher);
|
||||
|
||||
let key = Bytes::random(16);
|
||||
let nonce: u64 = 0; // otherwise edit would require the nonce too?
|
||||
|
||||
// Now, write the code that allows you to "seek" into the ciphertext,
|
||||
// decrypt, and re-encrypt with different plaintext. Expose this as a
|
||||
// function, like, "edit(ciphertext, key, offset, newtext)".
|
||||
@@ -22,6 +15,13 @@ pub fn challenge25() {
|
||||
ctr::encrypt(key, 0, &plaintext)
|
||||
}
|
||||
|
||||
let cipher = utils::read_base64("data/25.txt");
|
||||
let key = Bytes::from_utf8("YELLOW SUBMARINE");
|
||||
let plaintext = ecb::decrypt(&key, &cipher);
|
||||
|
||||
let key = Bytes::random(16);
|
||||
let nonce: u64 = 0; // otherwise edit would require the nonce too?
|
||||
|
||||
// Imagine the "edit" function was exposed to attackers by means of an API
|
||||
// call that didn't reveal the key or the original plaintext; the attacker
|
||||
// has the ciphertext and controls the offset and "new text". Recover the
|
||||
@@ -35,10 +35,11 @@ pub fn challenge25() {
|
||||
|
||||
println!("[okay] Challenge 25: recovered AES CTR plaintext via edit");
|
||||
|
||||
// A folkloric supposed benefit of CTR mode is the ability to easily "seek
|
||||
// forward" into the ciphertext; to access byte N of the ciphertext, all you
|
||||
// need to be able to do is generate byte N of the keystream. Imagine if
|
||||
// you'd relied on that advice to, say, encrypt a disk.
|
||||
// A folkloric supposed benefit of CTR mode is the ability to easily "seek forward" into the
|
||||
// ciphertext; to access byte N of the ciphertext, all you need to be able to do is generate
|
||||
// byte N of the keystream. Imagine if you'd relied on that advice to, say, encrypt a disk.
|
||||
// Answer: you would have to run through the whole cipher stream till you reach the point where
|
||||
// you want to decrypt.
|
||||
}
|
||||
|
||||
pub fn challenge26() {
|
||||
@@ -229,27 +230,28 @@ pub fn challenge29() {
|
||||
}
|
||||
|
||||
pub fn challenge30() {
|
||||
// test MD4 implementation
|
||||
assert_eq!(
|
||||
md4::hash(&Bytes::from_utf8("")),
|
||||
Bytes::from_hex("31d6cfe0d16ae931b73c59d7e0c089c0"),
|
||||
);
|
||||
assert_eq!(
|
||||
md4::hash(&Bytes::from_utf8("a")),
|
||||
Bytes::from_hex("bde52cb31de33e46245e05fbdbd6fb24"),
|
||||
);
|
||||
assert_eq!(
|
||||
md4::hash(&Bytes::from_utf8("abc")),
|
||||
Bytes::from_hex("a448017aaf21d8525fc10ae87aa6729d"),
|
||||
);
|
||||
assert_eq!(
|
||||
md4::hash(&Bytes::from_utf8("abcdefghijklmnopqrstuvwxyz")),
|
||||
Bytes::from_hex("d79e1c308aa5bbcdeea8ed63df412da9"),
|
||||
);
|
||||
assert_eq!(
|
||||
md4::hash(&Bytes(vec![b'a'; 1337])),
|
||||
Bytes::from_hex("9a4bceae0ae389c4653ad92cfd7bfc3e"),
|
||||
);
|
||||
fn test_md4() {
|
||||
assert_eq!(
|
||||
md4::hash(&Bytes::from_utf8("")),
|
||||
Bytes::from_hex("31d6cfe0d16ae931b73c59d7e0c089c0"),
|
||||
);
|
||||
assert_eq!(
|
||||
md4::hash(&Bytes::from_utf8("a")),
|
||||
Bytes::from_hex("bde52cb31de33e46245e05fbdbd6fb24"),
|
||||
);
|
||||
assert_eq!(
|
||||
md4::hash(&Bytes::from_utf8("abc")),
|
||||
Bytes::from_hex("a448017aaf21d8525fc10ae87aa6729d"),
|
||||
);
|
||||
assert_eq!(
|
||||
md4::hash(&Bytes::from_utf8("abcdefghijklmnopqrstuvwxyz")),
|
||||
Bytes::from_hex("d79e1c308aa5bbcdeea8ed63df412da9"),
|
||||
);
|
||||
assert_eq!(
|
||||
md4::hash(&Bytes(vec![b'a'; 1337])),
|
||||
Bytes::from_hex("9a4bceae0ae389c4653ad92cfd7bfc3e"),
|
||||
);
|
||||
}
|
||||
|
||||
// extend MD4 copy and pasted from SHA-1 #allow!(dont-repeat-yourself)
|
||||
fn hash_fixated(bytes: &Bytes, fixture: &Bytes, byte_len: u64) -> Bytes {
|
||||
@@ -263,6 +265,7 @@ pub fn challenge30() {
|
||||
m.hash(bytes)
|
||||
}
|
||||
|
||||
test_md4();
|
||||
let key = Bytes::random_range(2, 64);
|
||||
let message = Bytes::from_utf8(
|
||||
"comment1=cooking%20MCs;userdata=foo;comment2=%20like%20a%20pound%20of%20bacon",
|
||||
@@ -300,8 +303,8 @@ pub fn challenge30() {
|
||||
}
|
||||
|
||||
mod challenge31 {
|
||||
use std::fs;
|
||||
use crate::{bytes::Bytes, sha1};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::{thread, time};
|
||||
|
||||
@@ -329,13 +332,13 @@ mod challenge31 {
|
||||
if a != b {
|
||||
return false;
|
||||
}
|
||||
// In the loop for "insecure_compare", add a 50ms sleep (sleep 50ms after each byte).
|
||||
// In the loop for "insecure_compare", add a 50ms sleep after each byte.
|
||||
thread::sleep(delay);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn attack(file: &Path, delay: u64) -> Bytes {
|
||||
pub fn _attack(file: &Path, delay: u64) -> Bytes {
|
||||
const BLOCK_SIZE: usize = 20;
|
||||
let mut sig = vec![0x0; BLOCK_SIZE];
|
||||
for i in 0..BLOCK_SIZE {
|
||||
@@ -358,24 +361,61 @@ mod challenge31 {
|
||||
pub fn challenge31() {
|
||||
let key = Bytes::from_utf8("YELLOW SUBMARINE");
|
||||
let message = Bytes::from_utf8("Attact at dawn after tomorrow when it's cold inside.");
|
||||
assert_eq!(sha1::hmac_sha1(&key, &message), Bytes::from_hex("8232f3d05afb6bce7e09fe764885cc158e435e36"));
|
||||
assert_eq!(
|
||||
sha1::hmac_sha1(&key, &message),
|
||||
Bytes::from_hex("8232f3d05afb6bce7e09fe764885cc158e435e36")
|
||||
);
|
||||
|
||||
let path = Path::new("data/12.txt");
|
||||
let expected_sig = Bytes::from_hex("62f4527ea6cb716d0ad1ca0fc69135a49bc2d138");
|
||||
assert!(challenge31::verify(path, &expected_sig.0, 0), "Invalid signature");
|
||||
assert!(challenge31::verify(path, &expected_sig.0, 0), "Invalid");
|
||||
|
||||
// Don't do attack because it interrupts the flow of the other challenges by taking long.
|
||||
// let signature = challenge31::attack(path, 20);
|
||||
// Don't attack because it interrupts the flow of the other challenges by taking long.
|
||||
// const DELAY: u64 = 20;
|
||||
// let signature = challenge31::_attack(path, DELAY);
|
||||
// assert_eq!(expected_sig, signature, "Recovery of HMAC-SHA1 failed");
|
||||
|
||||
println!("[okay] Challenge 31: recovered HMAC-SHA1 via timing attack");
|
||||
}
|
||||
|
||||
pub fn challenge32() {
|
||||
const DELAY: u64 = 1;
|
||||
let path = Path::new("data/12.txt");
|
||||
let expected_sig = Bytes::from_hex("62f4527ea6cb716d0ad1ca0fc69135a49bc2d138");
|
||||
assert!(challenge31::attack(path, DELAY) != expected_sig, "Recovery was unexpectedly successful");
|
||||
use std::time;
|
||||
|
||||
println!("[xxxx] Challenge 32: recovered HMAC-SHA1 with slightly less artificial timing leak");
|
||||
pub fn _attack(file: &Path, delay: u64) -> Bytes {
|
||||
const CYCLES_PER_BYTE: usize = 30;
|
||||
const BLOCK_SIZE: usize = 20;
|
||||
let mut sig = vec![0x0; BLOCK_SIZE];
|
||||
for i in 0..BLOCK_SIZE {
|
||||
let mut max_tuple: (u128, u8) = (u128::MIN, 0);
|
||||
for c in 0_u8..=255_u8 {
|
||||
let mut cummulated_us = 0;
|
||||
for _ in 0..CYCLES_PER_BYTE {
|
||||
let now = time::Instant::now();
|
||||
sig[i] = c;
|
||||
challenge31::verify(file, &sig, delay);
|
||||
cummulated_us += now.elapsed().as_micros();
|
||||
}
|
||||
if cummulated_us > max_tuple.0 {
|
||||
max_tuple = (cummulated_us, c);
|
||||
}
|
||||
}
|
||||
sig[i] = max_tuple.1;
|
||||
}
|
||||
Bytes(sig)
|
||||
}
|
||||
|
||||
// Don't attack because it interrupts the flow of the other challenges by taking long.
|
||||
// const DELAY: u64 = 1;
|
||||
// let path = Path::new("data/12.txt");
|
||||
// let expected_sig = Bytes::from_hex("62f4527ea6cb716d0ad1ca0fc69135a49bc2d138");
|
||||
// assert!(
|
||||
// challenge31::_attack(path, DELAY) != expected_sig,
|
||||
// "Recovery was successful"
|
||||
// );
|
||||
// assert!(
|
||||
// _attack(path, DELAY) == expected_sig,
|
||||
// "Recovery was not successful"
|
||||
// );
|
||||
|
||||
println!("[okay] Challenge 32: recovered HMAC-SHA1 with slightly less artificial timing leak");
|
||||
}
|
||||
|
||||
3
src/set5.rs
Normal file
3
src/set5.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub fn challenge33() {
|
||||
println!("[xxxx] Challenge 33: now we talking!");
|
||||
}
|
||||
Reference in New Issue
Block a user