cryptopals/src/set1.rs

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