Solve challenge 19 in Python because my Rust still sucks.
This commit is contained in:
44
src/main.rs
44
src/main.rs
@@ -9,23 +9,29 @@ mod set2;
|
||||
mod set3;
|
||||
|
||||
fn main() {
|
||||
set1::challenge1();
|
||||
set1::challenge2();
|
||||
set1::challenge3();
|
||||
set1::challenge4();
|
||||
set1::challenge5();
|
||||
set1::challenge6();
|
||||
set1::challenge7();
|
||||
set1::challenge8();
|
||||
set2::challenge9();
|
||||
set2::challenge10();
|
||||
set2::challenge11();
|
||||
set2::challenge12();
|
||||
set2::challenge13();
|
||||
set2::challenge14();
|
||||
set2::challenge15();
|
||||
set2::challenge16();
|
||||
set3::challenge17();
|
||||
set3::challenge18();
|
||||
set3::challenge19();
|
||||
const RUN_ALL: bool = true;
|
||||
if RUN_ALL {
|
||||
set1::challenge1();
|
||||
set1::challenge2();
|
||||
set1::challenge3();
|
||||
set1::challenge4();
|
||||
set1::challenge5();
|
||||
set1::challenge6();
|
||||
set1::challenge7();
|
||||
set1::challenge8();
|
||||
set2::challenge9();
|
||||
set2::challenge10();
|
||||
set2::challenge11();
|
||||
set2::challenge12();
|
||||
set2::challenge13();
|
||||
set2::challenge14();
|
||||
set2::challenge15();
|
||||
set2::challenge16();
|
||||
set3::challenge17();
|
||||
set3::challenge18();
|
||||
set3::challenge19();
|
||||
set3::challenge20();
|
||||
} else {
|
||||
set3::challenge20();
|
||||
}
|
||||
}
|
||||
|
||||
30
src/set3.rs
30
src/set3.rs
@@ -3,6 +3,7 @@ use crate::bytes_base64::BytesBase64;
|
||||
use crate::cbc;
|
||||
use crate::ctr;
|
||||
use rand::Rng;
|
||||
use std::fs;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
pub fn challenge17() {
|
||||
@@ -100,7 +101,7 @@ pub fn challenge18() {
|
||||
|
||||
let cleartext = Bytes::from_utf8("Let's see if we can get the party started hard my friends.");
|
||||
let cipher = ctr::encrypt(&key, 42351234, &cleartext);
|
||||
let roundtrip = ctr::encrypt(&key, 42351234, &cipher);
|
||||
let roundtrip = ctr::decrypt(&key, 42351234, &cipher);
|
||||
assert_eq!(cleartext, roundtrip);
|
||||
|
||||
let cipher = BytesBase64::from_base64(
|
||||
@@ -112,5 +113,30 @@ pub fn challenge18() {
|
||||
}
|
||||
|
||||
pub fn challenge19() {
|
||||
println!("[xxxx] Challenge 19: TBD");
|
||||
fn read(path: &str) -> Vec<Bytes> {
|
||||
let file = std::fs::File::open(path).unwrap();
|
||||
let br = BufReader::new(file);
|
||||
br.lines()
|
||||
.map(|line| BytesBase64::from_base64(&line.unwrap()).to_bytes())
|
||||
.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");
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
pub fn challenge20() {
|
||||
println!("[xxxx] Challenge 20: TBD");
|
||||
}
|
||||
|
||||
99
src/set3c19.py
Normal file
99
src/set3c19.py
Normal file
@@ -0,0 +1,99 @@
|
||||
import binascii
|
||||
import string
|
||||
from typing import List, Dict, Set
|
||||
|
||||
|
||||
def load_ciphers() -> List[bytes]:
|
||||
with open("data/19_enc.txt", "r") as f:
|
||||
r = [binascii.a2b_base64(line) for line in f]
|
||||
return r
|
||||
|
||||
|
||||
def xor_lookup_set(letters: str) -> Dict[int, Set[str]]:
|
||||
d = {i: set() for i in range(256)}
|
||||
for c1 in letters:
|
||||
for c2 in letters:
|
||||
xored = ord(c1) ^ ord(c2)
|
||||
d[xored].add(c1)
|
||||
d[xored].add(c2)
|
||||
return d
|
||||
|
||||
|
||||
def attack(ciphers: List[bytes]) -> List[List[str]]:
|
||||
""" Find out possible characters for each cipher pair and hope that there
|
||||
is only one possible character. If no character was found add '?' and
|
||||
if more than one was found add '^'. """
|
||||
|
||||
ciphers_len = len(ciphers)
|
||||
deciphered = [[] for _ in range(ciphers_len)]
|
||||
max_cipher_len = max(map(len, ciphers))
|
||||
for byte_index in range(0, max_cipher_len):
|
||||
# Certain bytes only work with certain letters (found empirically).
|
||||
if byte_index == 10:
|
||||
LETTERS = string.ascii_letters + " _-.,;:'"
|
||||
elif byte_index == 20:
|
||||
LETTERS = string.ascii_letters + " _-.,;:?"
|
||||
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
|
||||
for cipher in ciphers]
|
||||
possible_chars = [set(LETTERS) for _ in range(ciphers_len)]
|
||||
for i in range(ciphers_len):
|
||||
for j in range(i, ciphers_len):
|
||||
if target_bytes[i] is None or target_bytes[j] is None:
|
||||
continue
|
||||
xored = target_bytes[i] ^ target_bytes[j]
|
||||
chars = lookup[xored]
|
||||
possible_chars[i] &= chars
|
||||
possible_chars[j] &= chars
|
||||
for cipher_index in range(ciphers_len):
|
||||
if len(ciphers[cipher_index]) <= byte_index:
|
||||
continue
|
||||
chars = list(possible_chars[cipher_index])
|
||||
match len(chars):
|
||||
case 0:
|
||||
# print(f"No chars for {cipher_index=} {byte_index=}")
|
||||
deciphered[cipher_index].append('?')
|
||||
case 1:
|
||||
deciphered[cipher_index].append(chars[0].lower())
|
||||
case 2:
|
||||
if chars[0].lower() == chars[1].lower():
|
||||
deciphered[cipher_index].append(chars[0].lower())
|
||||
else:
|
||||
# print(f"Two {chars=} {cipher_index=} {byte_index=}")
|
||||
deciphered[cipher_index].append('^')
|
||||
case _:
|
||||
# print(f"Too many {chars=} {cipher_index=} {byte_index=}")
|
||||
deciphered[cipher_index].append('^')
|
||||
return deciphered
|
||||
|
||||
|
||||
def manual(decrypts: List[List[str]]) -> List[bytes]:
|
||||
""" Manually add guessed letters. """
|
||||
decrypts[0][30] = 'y'
|
||||
decrypts[2][30] = 'y'
|
||||
decrypts[4][30] = 'e'
|
||||
decrypts[4][32] = 'h'
|
||||
decrypts[4][33] = 'e'
|
||||
decrypts[4][34] = 'a'
|
||||
decrypts[4][35] = 'd'
|
||||
decrypts[6][30] = 'i'
|
||||
decrypts[13][30] = ' '
|
||||
decrypts[20][30] = ' '
|
||||
decrypts[25][30] = 'n'
|
||||
decrypts[28][30] = ' '
|
||||
decrypts[29][30] = 't'
|
||||
decrypts[37][30] = 'i'
|
||||
decrypts = list(map(lambda l: "".join(l), decrypts))
|
||||
return decrypts
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ciphers = load_ciphers()
|
||||
decrypts = manual(attack(ciphers))
|
||||
for d in decrypts:
|
||||
print(d)
|
||||
Reference in New Issue
Block a user