Implement challenge 24 and finish set 3.
This commit is contained in:
@@ -4,13 +4,16 @@ mod cbc;
|
|||||||
mod ctr;
|
mod ctr;
|
||||||
mod ecb;
|
mod ecb;
|
||||||
mod mt19937;
|
mod mt19937;
|
||||||
|
mod mtcipher;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod set1;
|
mod set1;
|
||||||
mod set2;
|
mod set2;
|
||||||
mod set3;
|
mod set3;
|
||||||
|
mod set4;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
const RUN_ALL: bool = true;
|
const RUN_ALL: bool = false;
|
||||||
if RUN_ALL {
|
if RUN_ALL {
|
||||||
set1::challenge1();
|
set1::challenge1();
|
||||||
set1::challenge2();
|
set1::challenge2();
|
||||||
@@ -36,7 +39,8 @@ fn main() {
|
|||||||
set3::challenge22();
|
set3::challenge22();
|
||||||
set3::challenge23();
|
set3::challenge23();
|
||||||
set3::challenge24();
|
set3::challenge24();
|
||||||
|
set4::challenge25();
|
||||||
} else {
|
} else {
|
||||||
set3::challenge24();
|
set4::challenge25();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ impl MT19937 {
|
|||||||
self.twist();
|
self.twist();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extract_bytes(&mut self) -> [u8; 4] {
|
||||||
|
let n = self.extract_number();
|
||||||
|
n.to_ne_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn extract_number(&mut self) -> u32 {
|
pub fn extract_number(&mut self) -> u32 {
|
||||||
if self.index == N {
|
if self.index == N {
|
||||||
self.twist();
|
self.twist();
|
||||||
@@ -60,10 +65,9 @@ impl MT19937 {
|
|||||||
|
|
||||||
fn twist(&mut self) {
|
fn twist(&mut self) {
|
||||||
const M: usize = 397;
|
const M: usize = 397;
|
||||||
const R: u32 = 31;
|
const A: u32 = 0x9908_B0DF;
|
||||||
const A: u32 = 0x9908B0DF;
|
const LOWER_MASK: u32 = 0x7fff_ffff;
|
||||||
const LOWER_MASK: u32 = (1 << R) - 1; // 0x7fffffff
|
const UPPER_MASK: u32 = 0x8000_0000;
|
||||||
const UPPER_MASK: u32 = !LOWER_MASK; // 0x80000000
|
|
||||||
const FIRST_HALF: usize = N - M;
|
const FIRST_HALF: usize = N - M;
|
||||||
|
|
||||||
for i in 0..FIRST_HALF {
|
for i in 0..FIRST_HALF {
|
||||||
|
|||||||
26
src/mtcipher.rs
Normal file
26
src/mtcipher.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
use crate::{bytes::Bytes, mt19937};
|
||||||
|
|
||||||
|
pub fn decrypt(key: u16, data: &Bytes) -> Bytes {
|
||||||
|
encrypt(key, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encrypt(key: u16, Bytes(data): &Bytes) -> Bytes {
|
||||||
|
// You can create a trivial stream cipher out of any PRNG; use it to
|
||||||
|
// generate a sequence of 8 bit outputs and call those outputs a keystream.
|
||||||
|
// XOR each byte of plaintext with each successive byte of keystream.
|
||||||
|
// Write the function that does this for MT19937 using a 16-bit seed.
|
||||||
|
|
||||||
|
let mut mt = mt19937::MT19937::new();
|
||||||
|
mt.seed(key as u32);
|
||||||
|
let mut result: Vec<u8> = vec![];
|
||||||
|
|
||||||
|
for chunk in data.chunks(4) {
|
||||||
|
let key = mt.extract_bytes();
|
||||||
|
for i in 0..chunk.len() {
|
||||||
|
let cipher_char = chunk[i] ^ key[i];
|
||||||
|
result.push(cipher_char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytes(result)
|
||||||
|
}
|
||||||
106
src/set3.rs
106
src/set3.rs
@@ -3,12 +3,13 @@ use crate::bytes_base64::BytesBase64;
|
|||||||
use crate::cbc;
|
use crate::cbc;
|
||||||
use crate::ctr;
|
use crate::ctr;
|
||||||
use crate::mt19937;
|
use crate::mt19937;
|
||||||
|
use crate::mtcipher;
|
||||||
|
use crate::utils;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
|
||||||
|
|
||||||
pub fn challenge17() {
|
pub fn challenge17() {
|
||||||
fn read(path: &str) -> Vec<Bytes> {
|
fn read(path: &str) -> Vec<Bytes> {
|
||||||
@@ -317,17 +318,8 @@ pub fn challenge21() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn challenge22() {
|
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.
|
// 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 wait_time: u32 = rand::thread_rng().gen_range(40..1000);
|
||||||
let seed = now + wait_time;
|
let seed = now + wait_time;
|
||||||
|
|
||||||
@@ -341,7 +333,7 @@ pub fn challenge22() {
|
|||||||
// From the 32 bit RNG output, discover the seed.
|
// From the 32 bit RNG output, discover the seed.
|
||||||
fn find_seed(rngout: u32) -> Option<u32> {
|
fn find_seed(rngout: u32) -> Option<u32> {
|
||||||
let mut mt = mt19937::MT19937::new();
|
let mut mt = mt19937::MT19937::new();
|
||||||
let start = unix_timestamp() - 2000;
|
let start = utils::unix_timestamp() - 2000;
|
||||||
for seed in start..(start + 4000) {
|
for seed in start..(start + 4000) {
|
||||||
mt.seed(seed);
|
mt.seed(seed);
|
||||||
if rngout == mt.extract_number() {
|
if rngout == mt.extract_number() {
|
||||||
@@ -448,5 +440,93 @@ pub fn challenge23() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn challenge24() {
|
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");
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/set4.rs
Normal file
3
src/set4.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub fn challenge25() {
|
||||||
|
println!("[xxxx] Challenge 25: TBD");
|
||||||
|
}
|
||||||
9
src/utils.rs
Normal file
9
src/utils.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
pub 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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user