Implement challenge 19 in Rust so that I can move on with good conscience.

This commit is contained in:
2022-07-24 18:19:32 -04:00
parent b8fa60cb05
commit 3248609119
3 changed files with 123 additions and 24 deletions

View File

@@ -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();
}
}

View File

@@ -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() {

View File

@@ -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