Implement challenge 24 and finish set 3.

This commit is contained in:
2022-08-12 12:28:19 -04:00
parent 98ad4c7712
commit fbf26efa44
6 changed files with 145 additions and 19 deletions

View File

@@ -3,12 +3,13 @@ use crate::bytes_base64::BytesBase64;
use crate::cbc;
use crate::ctr;
use crate::mt19937;
use crate::mtcipher;
use crate::utils;
use rand::Rng;
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::HashSet;
use std::io::{BufRead, BufReader};
use std::time::{SystemTime, UNIX_EPOCH};
pub fn challenge17() {
fn read(path: &str) -> Vec<Bytes> {
@@ -317,17 +318,8 @@ pub fn challenge21() {
}
pub fn challenge22() {
// let mut mt = mt19937::MT19937::new();
fn unix_timestamp() -> u32 {
let start = SystemTime::now();
let since_the_epoch = start
.duration_since(UNIX_EPOCH)
.expect("Time went backwards");
since_the_epoch.as_secs() as u32
}
// Wait a random number of seconds between, I don't know, 40 and 1000.
let now = unix_timestamp();
let now = utils::unix_timestamp();
let wait_time: u32 = rand::thread_rng().gen_range(40..1000);
let seed = now + wait_time;
@@ -341,7 +333,7 @@ pub fn challenge22() {
// From the 32 bit RNG output, discover the seed.
fn find_seed(rngout: u32) -> Option<u32> {
let mut mt = mt19937::MT19937::new();
let start = unix_timestamp() - 2000;
let start = utils::unix_timestamp() - 2000;
for seed in start..(start + 4000) {
mt.seed(seed);
if rngout == mt.extract_number() {
@@ -448,5 +440,93 @@ pub fn challenge23() {
}
pub fn challenge24() {
println!("[xxxx] Challenge 24: xxx");
// Verify that you can encrypt and decrypt properly. This code should look
// similar to your CTR code.
let key: u16 = 111;
let cleartext = Bytes::from_utf8("Let's see if we can get the party started hard my friends.");
let cipher = mtcipher::encrypt(key, &cleartext);
let roundtrip = mtcipher::decrypt(key, &cipher);
assert_eq!(cleartext, roundtrip);
// Use your function to encrypt a known plaintext (say, 14 consecutive 'A'
// characters) prefixed by a random number of random characters.
fn get_plaintext() -> Bytes {
let length: usize = rand::thread_rng().gen_range(30..100);
let mut data = Bytes::random(length);
data.0.append(&mut Bytes(vec![b'A'; 14]).0);
data
}
let key: u16 = rand::thread_rng().gen::<u16>();
let plaintext = get_plaintext();
let cipher = mtcipher::encrypt(key, &plaintext);
// From the ciphertext, recover the "key" (the 16 bit seed).
fn recover_key(cipher: &Bytes) -> u16 {
let cipher_len = cipher.len();
// brute force bb!
for key in 0..u16::MAX {
let mut found_key = true;
let roundtrip = mtcipher::decrypt(key, &cipher);
// check if the last 14 chars are 'A' - if yes, we found the key
for i in (cipher_len - 14)..cipher_len {
if roundtrip.0[i] != b'A' {
found_key = false;
break;
}
}
if found_key {
return key;
}
}
0
}
let recovered_key = recover_key(&cipher);
assert_eq!(key, recovered_key);
// Use the same idea to generate a random "password reset token" using
// MT19937 seeded from the current time.
fn get_reset_token(time: Option<u32>) -> Bytes {
const TOKEN_LENGTH: usize = 16;
let time = match time {
Some(time) => time,
None => utils::unix_timestamp(),
};
let mut token = vec![];
let mut mt = mt19937::MT19937::new();
mt.seed(time);
while token.len() < (TOKEN_LENGTH - 1) {
for b in mt.extract_bytes() {
if token.len() >= TOKEN_LENGTH {
break;
}
if b.is_ascii_alphanumeric() {
token.push(b);
}
}
}
Bytes(token)
}
let token = get_reset_token(None);
// println!("{}", token.to_utf8());
// Write a function to check if any given password token is actually the
// product of an MT19937 PRNG seeded with the current time.
fn is_time_token(token: &Bytes) -> bool {
let current_time = utils::unix_timestamp();
for time in (current_time - 10)..(current_time + 10) {
let time_token = get_reset_token(Some(time));
if *token == time_token {
return true;
}
}
false
}
assert_eq!(is_time_token(&token), true);
let non_token = Bytes(vec![b'z', 16]);
assert_eq!(is_time_token(&non_token), false);
println!("[okay] Challenge 24: MT19937 stream cipher implemented and cracked");
}