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;
|
mod set3;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
const RUN_ALL: bool = false;
|
const RUN_ALL: bool = true;
|
||||||
if RUN_ALL {
|
if RUN_ALL {
|
||||||
set1::challenge1();
|
set1::challenge1();
|
||||||
set1::challenge2();
|
set1::challenge2();
|
||||||
@@ -32,6 +32,6 @@ fn main() {
|
|||||||
set3::challenge19();
|
set3::challenge19();
|
||||||
set3::challenge20();
|
set3::challenge20();
|
||||||
} else {
|
} 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::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::fs;
|
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
|
|
||||||
pub fn challenge17() {
|
pub fn challenge17() {
|
||||||
@@ -124,18 +123,6 @@ pub fn challenge19() {
|
|||||||
.collect()
|
.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>>> {
|
fn xor_to_char_set(letters: &Vec<u8>) -> HashMap<u8, RefCell<HashSet<u8>>> {
|
||||||
let mut h = HashMap::new();
|
let mut h = HashMap::new();
|
||||||
for i in 0..255_u8 {
|
for i in 0..255_u8 {
|
||||||
@@ -154,17 +141,130 @@ pub fn challenge19() {
|
|||||||
h
|
h
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _attack(_ciphers: Vec<Bytes>) -> Vec<Vec<u8>> {
|
fn u8_lower(s: u8) -> u8 {
|
||||||
vec![vec![]]
|
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();
|
fn ascii_letters(additional: &str) -> Vec<u8> {
|
||||||
letters.append(&mut vec![b'_', b'-', b'.']);
|
let mut letters: Vec<u8> = (0..255_u8).filter(u8::is_ascii_alphabetic).collect();
|
||||||
let lookup = xor_to_char_set(&letters);
|
for b in additional.as_bytes() {
|
||||||
println!("{:?}", lookup);
|
letters.push(*b);
|
||||||
|
}
|
||||||
|
letters
|
||||||
|
}
|
||||||
|
|
||||||
fs::write("data/19_enc.txt", data).expect("Unable to write file");
|
fn attack(ciphers: Vec<Bytes>) -> Vec<RefCell<Vec<u8>>> {
|
||||||
println!("[okay] Challenge 19: encrypted to data/19_enc.txt, attack in set3c19.py.");
|
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() {
|
pub fn challenge20() {
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ def attack(ciphers: List[bytes]) -> List[List[str]]:
|
|||||||
else:
|
else:
|
||||||
LETTERS = string.ascii_letters + " _-.,;:"
|
LETTERS = string.ascii_letters + " _-.,;:"
|
||||||
lookup = xor_lookup_set(LETTERS)
|
lookup = xor_lookup_set(LETTERS)
|
||||||
ciphers_len = len(ciphers)
|
|
||||||
target_bytes = [cipher[byte_index]
|
target_bytes = [cipher[byte_index]
|
||||||
if len(cipher) > byte_index
|
if len(cipher) > byte_index
|
||||||
else None
|
else None
|
||||||
|
|||||||
Reference in New Issue
Block a user