195 lines
6.7 KiB
Rust
195 lines
6.7 KiB
Rust
#![allow(dead_code)]
|
|
use crate::bytes::Bytes;
|
|
use crate::bytes_base64::BytesBase64;
|
|
use crate::{ecb, utils};
|
|
use std::io::Write;
|
|
use std::str;
|
|
|
|
pub fn challenge1() {
|
|
let input = Bytes::from_hex("49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d");
|
|
let expected = String::from("SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t");
|
|
let result = BytesBase64::from_bytes(input);
|
|
if result.to_string() == expected {
|
|
println!("[okay] Challenge 1: {}", result);
|
|
} else {
|
|
println!("[fail] Challenge 1")
|
|
}
|
|
}
|
|
|
|
pub fn challenge2() {
|
|
let input_1 = Bytes::from_hex("1c0111001f010100061a024b53535009181c");
|
|
let input_2 = Bytes::from_hex("686974207468652062756c6c277320657965");
|
|
let result = Bytes::xor(&input_1, &input_2);
|
|
let expected = Bytes::from_hex("746865206b696420646f6e277420706c6179");
|
|
if result == expected {
|
|
println!("[okay] Challenge 2: {}", result.to_hex());
|
|
} else {
|
|
println!("[fail] Challenge 2")
|
|
}
|
|
}
|
|
|
|
pub fn challenge3() {
|
|
let a = Bytes::from_hex("1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736");
|
|
let mut h: Vec<Bytes> = (0..=255).map(|i| Bytes::xor_byte(&a, i)).collect();
|
|
h.sort_by(|a, b| a.ascii_score().partial_cmp(&b.ascii_score()).unwrap());
|
|
// let h: Vec<Bytes> = h.into_iter().filter(|b| b.is_ascii()).collect();
|
|
let r = h.last().unwrap().to_utf8();
|
|
println!("[okay] Challenge 3: {}", r);
|
|
}
|
|
|
|
pub fn challenge4() {
|
|
let bytes_vector = utils::read_hex_lines("data/4.txt");
|
|
let mut xored_bytes_vector: Vec<Bytes> = vec![];
|
|
for i in 32..=127 {
|
|
for bytes in bytes_vector.iter() {
|
|
xored_bytes_vector.push(Bytes::xor_byte(bytes, i));
|
|
}
|
|
}
|
|
xored_bytes_vector.sort_by(|a, b| a.ascii_score().partial_cmp(&b.ascii_score()).unwrap());
|
|
let result = xored_bytes_vector.last().unwrap().to_utf8();
|
|
println!("[okay] Challenge 4: {}", result.trim());
|
|
}
|
|
|
|
pub fn challenge5() {
|
|
let msg = Bytes::from_utf8(
|
|
"Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal",
|
|
);
|
|
let key = Bytes::from_utf8("ICE");
|
|
let enc = Bytes::xor_cycle(&msg, &key);
|
|
let exp = Bytes::from_hex("0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f");
|
|
if enc == exp {
|
|
println!("[okay] Challenge 5: {}", enc.to_hex());
|
|
} else {
|
|
println!("[fail] Challenge 5")
|
|
}
|
|
}
|
|
|
|
pub fn challenge6() {
|
|
fn _write(path: &str, bytes_base64: &BytesBase64) {
|
|
let width = 60;
|
|
let bytes = bytes_base64.to_string();
|
|
let mut output_vec = vec![];
|
|
|
|
for e in Iterator::zip(bytes.as_bytes().iter(), 0..bytes.len()) {
|
|
if e.1 % width == 0 && e.1 > 0 {
|
|
output_vec.push('\n' as u8)
|
|
}
|
|
output_vec.push(*e.0);
|
|
}
|
|
output_vec.push('\n' as u8);
|
|
let mut f = std::fs::File::create(path).unwrap();
|
|
f.write(&output_vec).unwrap();
|
|
}
|
|
|
|
fn _test_roundtrip() {
|
|
// Test that conversion from to bytes and back works
|
|
let bytes = utils::read_base64("data/6.txt");
|
|
let bytes_base64 = BytesBase64::from_bytes(bytes);
|
|
_write("data/6.txt", &bytes_base64);
|
|
}
|
|
|
|
fn rate(Bytes(v): &Bytes, size: usize) -> f32 {
|
|
let (mut rating, mut iterations) = (0, 0);
|
|
for c in v.chunks(size * 2) {
|
|
if c.len() < size * 2 {
|
|
break;
|
|
}
|
|
let (a, b) = c.split_at(size);
|
|
rating += Bytes::hemming(&Bytes(a.to_vec()), &Bytes(b.to_vec()));
|
|
iterations += 1;
|
|
}
|
|
(rating as f32 / iterations as f32) / size as f32
|
|
}
|
|
|
|
fn guess_key_size(bytes: &Bytes) -> usize {
|
|
let (mut lowest_rating, mut lowest_size) = (f32::MAX, 0);
|
|
for key_size in 2..40 {
|
|
let rating = rate(&bytes, key_size);
|
|
if rating < lowest_rating {
|
|
lowest_rating = rating;
|
|
lowest_size = key_size;
|
|
}
|
|
}
|
|
lowest_size
|
|
}
|
|
|
|
fn transpose(Bytes(v): &Bytes, size: usize) -> Vec<Bytes> {
|
|
let mut result = vec![];
|
|
for i in 0..size {
|
|
let mut column = vec![];
|
|
for j in (i..v.len()).step_by(size) {
|
|
column.push(v[j]);
|
|
}
|
|
result.push(Bytes(column));
|
|
}
|
|
result
|
|
}
|
|
|
|
_test_roundtrip();
|
|
let bytes = utils::read_base64("data/6.txt");
|
|
let key_size = guess_key_size(&bytes);
|
|
let bytes_tranposed = transpose(&bytes, key_size);
|
|
let key = Bytes(
|
|
bytes_tranposed
|
|
.iter()
|
|
.map(|b| Bytes::guess_key(&b))
|
|
.collect(),
|
|
);
|
|
let msg = Bytes::xor_cycle(&bytes, &key).to_utf8();
|
|
|
|
let partial_msg = msg[..20].to_string();
|
|
println!("[okay] Challenge 6: {}...", partial_msg);
|
|
}
|
|
|
|
pub fn challenge7() {
|
|
let text = Bytes::from_utf8("We meet at burger king!");
|
|
let key = Bytes::from_utf8("YELLOW SUBMARINE");
|
|
let ciphertext = ecb::encrypt(&key, &text);
|
|
let roundtrip = ecb::decrypt(&key, &ciphertext);
|
|
if text != roundtrip {
|
|
panic!("ECB roundtrip not working.");
|
|
}
|
|
let ciphertext = utils::read_base64("data/7.txt");
|
|
let data = ecb::decrypt(&key, &ciphertext);
|
|
let partial_msg = data.to_utf8()[..20].to_string();
|
|
println!("[okay] Challenge 7: {}...", partial_msg);
|
|
}
|
|
|
|
pub fn challenge8() {
|
|
fn rate(Bytes(v): &Bytes, size: usize) -> u32 {
|
|
let mut rating = 0;
|
|
let chunks: Vec<&[u8]> = v.chunks(size).collect();
|
|
for i in 0..chunks.len() {
|
|
for j in (i + 1)..chunks.len() {
|
|
rating += Bytes::hemming(&Bytes(chunks[i].to_vec()), &Bytes(chunks[j].to_vec()));
|
|
}
|
|
}
|
|
rating
|
|
}
|
|
|
|
let expected_index: usize = 132;
|
|
let bytes_vector = utils::read_hex_lines("data/8.txt");
|
|
let ratings: Vec<u32> = bytes_vector.iter().map(|b| rate(&b, 16)).collect();
|
|
let min_rating = ratings.iter().min().unwrap();
|
|
let index = ratings.iter().position(|e| e == min_rating).unwrap();
|
|
let average = ratings.iter().sum::<u32>() as f32 / ratings.len() as f32;
|
|
let partial_cipher = bytes_vector[index].to_hex()[..10].to_string();
|
|
if index != expected_index {
|
|
panic!("Regression in challenge 8.");
|
|
}
|
|
println!(
|
|
"[okay] Challenge 8: Cipher {} [{}...] with rating {} (average = {}) is the solution.",
|
|
index, partial_cipher, min_rating, average
|
|
);
|
|
|
|
// More elegant solution.
|
|
let bytes_vector = utils::read_hex_lines("data/8.txt");
|
|
let ix: Vec<usize> = bytes_vector
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|&(_, bytes)| bytes.has_duplicated_cycle(16))
|
|
.map(|(i, _)| i)
|
|
.collect();
|
|
assert_eq!(ix[0], expected_index);
|
|
}
|