Implement challenge 19 in Rust so that I can move on with good conscience.
This commit is contained in:
@@ -9,7 +9,7 @@ mod set2;
|
||||
mod set3;
|
||||
|
||||
fn main() {
|
||||
const RUN_ALL: bool = false;
|
||||
const RUN_ALL: bool = true;
|
||||
if RUN_ALL {
|
||||
set1::challenge1();
|
||||
set1::challenge2();
|
||||
@@ -32,6 +32,6 @@ fn main() {
|
||||
set3::challenge19();
|
||||
set3::challenge20();
|
||||
} else {
|
||||
set3::challenge19();
|
||||
set3::challenge20();
|
||||
}
|
||||
}
|
||||
|
||||
142
src/set3.rs
142
src/set3.rs
@@ -6,7 +6,6 @@ use rand::Rng;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
pub fn challenge17() {
|
||||
@@ -124,18 +123,6 @@ pub fn challenge19() {
|
||||
.collect()
|
||||
}
|
||||
|
||||
let plaintexts = read("data/19.txt");
|
||||
let key = Bytes::from_utf8("YELLOW SUBMARINE");
|
||||
let encrypt = |plaintext: &Bytes| -> Bytes { ctr::encrypt(&key, 0, plaintext) };
|
||||
let ciphers: Vec<Bytes> = plaintexts.iter().map(|ct| encrypt(&ct)).collect();
|
||||
let mut data = String::new();
|
||||
for cipher in ciphers.iter() {
|
||||
let copy_cipher = Bytes(cipher.0.to_vec());
|
||||
let cipher_base64 = BytesBase64::from_bytes(copy_cipher);
|
||||
data.push_str(&cipher_base64.to_string());
|
||||
data.push_str("\n");
|
||||
}
|
||||
|
||||
fn xor_to_char_set(letters: &Vec<u8>) -> HashMap<u8, RefCell<HashSet<u8>>> {
|
||||
let mut h = HashMap::new();
|
||||
for i in 0..255_u8 {
|
||||
@@ -154,17 +141,130 @@ pub fn challenge19() {
|
||||
h
|
||||
}
|
||||
|
||||
fn _attack(_ciphers: Vec<Bytes>) -> Vec<Vec<u8>> {
|
||||
vec![vec![]]
|
||||
fn u8_lower(s: u8) -> u8 {
|
||||
if s >= b'A' && s <= b'Z' {
|
||||
return s + 32;
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
let mut letters: Vec<u8> = (0..255_u8).filter(u8::is_ascii_alphabetic).collect();
|
||||
letters.append(&mut vec![b'_', b'-', b'.']);
|
||||
let lookup = xor_to_char_set(&letters);
|
||||
println!("{:?}", lookup);
|
||||
fn ascii_letters(additional: &str) -> Vec<u8> {
|
||||
let mut letters: Vec<u8> = (0..255_u8).filter(u8::is_ascii_alphabetic).collect();
|
||||
for b in additional.as_bytes() {
|
||||
letters.push(*b);
|
||||
}
|
||||
letters
|
||||
}
|
||||
|
||||
fs::write("data/19_enc.txt", data).expect("Unable to write file");
|
||||
println!("[okay] Challenge 19: encrypted to data/19_enc.txt, attack in set3c19.py.");
|
||||
fn attack(ciphers: Vec<Bytes>) -> Vec<RefCell<Vec<u8>>> {
|
||||
let ciphers_len = ciphers.len();
|
||||
let deciphered = vec![RefCell::new(vec![]); ciphers_len];
|
||||
let max_cipher_len = ciphers.iter().map(|c| c.len()).max().unwrap_or(0);
|
||||
|
||||
for byte_index in 0..max_cipher_len {
|
||||
let letters = match byte_index {
|
||||
// chars that work for 10 and 20 found via trial and error
|
||||
10 => ascii_letters(" _-.,;:'"),
|
||||
20 => ascii_letters(" _-.,;:?"),
|
||||
_ => ascii_letters(" _-.,;:"),
|
||||
};
|
||||
let lookup = xor_to_char_set(&letters);
|
||||
|
||||
let target_bytes: Vec<Option<u8>> = ciphers
|
||||
.iter()
|
||||
.map(|c| {
|
||||
if c.len() > byte_index {
|
||||
Some(c.0[byte_index])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let mut possible_chars: Vec<HashSet<u8>> = ciphers
|
||||
.iter()
|
||||
.map(|_| HashSet::from_iter(letters.iter().cloned()))
|
||||
.collect();
|
||||
|
||||
for i in 0..ciphers_len {
|
||||
for j in i..ciphers_len {
|
||||
if target_bytes[i] == None || target_bytes[j] == None {
|
||||
continue;
|
||||
}
|
||||
let xored = target_bytes[i].unwrap() ^ target_bytes[j].unwrap();
|
||||
let chars = lookup.get(&xored).unwrap().borrow();
|
||||
possible_chars[i] = possible_chars[i].intersection(&chars).cloned().collect();
|
||||
possible_chars[j] = possible_chars[j].intersection(&chars).cloned().collect();
|
||||
}
|
||||
}
|
||||
|
||||
for cipher_index in 0..ciphers_len {
|
||||
if ciphers[cipher_index].len() <= byte_index {
|
||||
continue;
|
||||
}
|
||||
let chars: Vec<u8> = possible_chars[cipher_index].iter().cloned().collect();
|
||||
match chars.len() {
|
||||
0 => {
|
||||
// println!("No chars for {cipher_index} {byte_index}");
|
||||
deciphered[cipher_index].borrow_mut().push(b'?');
|
||||
}
|
||||
1 => {
|
||||
deciphered[cipher_index]
|
||||
.borrow_mut()
|
||||
.push(u8_lower(chars[0]));
|
||||
}
|
||||
2 => {
|
||||
if u8_lower(chars[0]) == u8_lower(chars[1]) {
|
||||
deciphered[cipher_index]
|
||||
.borrow_mut()
|
||||
.push(u8_lower(chars[0]));
|
||||
} else {
|
||||
// println!("Two {chars:?} {cipher_index} {byte_index}");
|
||||
deciphered[cipher_index].borrow_mut().push(b'^');
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// println!("Two {chars:?} {cipher_index} {byte_index}");
|
||||
deciphered[cipher_index].borrow_mut().push(b'^');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deciphered
|
||||
}
|
||||
|
||||
fn manual(decrypts: &Vec<RefCell<Vec<u8>>>) {
|
||||
// Add manually guessed letters
|
||||
decrypts[0].borrow_mut()[30] = b'y';
|
||||
decrypts[2].borrow_mut()[30] = b'y';
|
||||
let mut d4 = decrypts[4].borrow_mut();
|
||||
d4[30] = b'e';
|
||||
d4[32] = b'h';
|
||||
d4[33] = b'e';
|
||||
d4[34] = b'a';
|
||||
d4[35] = b'd';
|
||||
decrypts[6].borrow_mut()[30] = b'i';
|
||||
decrypts[13].borrow_mut()[30] = b' ';
|
||||
decrypts[20].borrow_mut()[30] = b' ';
|
||||
decrypts[25].borrow_mut()[30] = b'n';
|
||||
decrypts[28].borrow_mut()[30] = b' ';
|
||||
decrypts[29].borrow_mut()[30] = b't';
|
||||
decrypts[37].borrow_mut()[30] = b'i';
|
||||
}
|
||||
|
||||
let plaintexts = read("data/19.txt");
|
||||
let key = Bytes::from_utf8("YELLOW SUBMARINE");
|
||||
let encrypt = |plaintext: &Bytes| -> Bytes { ctr::encrypt(&key, 0, plaintext) };
|
||||
let ciphers: Vec<Bytes> = plaintexts.iter().map(|ct| encrypt(&ct)).collect();
|
||||
let decrypts = attack(ciphers);
|
||||
manual(&decrypts);
|
||||
for row in decrypts {
|
||||
println!(
|
||||
"[okay] Challenge 19: {}",
|
||||
Bytes(row.borrow().to_vec()).to_utf8()
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn challenge20() {
|
||||
|
||||
@@ -36,7 +36,6 @@ def attack(ciphers: List[bytes]) -> List[List[str]]:
|
||||
else:
|
||||
LETTERS = string.ascii_letters + " _-.,;:"
|
||||
lookup = xor_lookup_set(LETTERS)
|
||||
ciphers_len = len(ciphers)
|
||||
target_bytes = [cipher[byte_index]
|
||||
if len(cipher) > byte_index
|
||||
else None
|
||||
|
||||
Reference in New Issue
Block a user