#![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 = (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 = 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 = 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 { 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 = 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::() 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 = bytes_vector .iter() .enumerate() .filter(|&(_, bytes)| bytes.has_duplicated_cycle(16)) .map(|(i, _)| i) .collect(); assert_eq!(ix[0], expected_index); }