From be04722ce7193922635ddcf61aeb592c059a1562 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Thu, 5 Apr 2018 06:42:44 -0700 Subject: [PATCH] XOR is still a work in progress, but it builds now, and I think the single_byte_xor does actually work... --- src/xor.rs | 136 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 52 deletions(-) diff --git a/src/xor.rs b/src/xor.rs index a3bc413..79b4403 100644 --- a/src/xor.rs +++ b/src/xor.rs @@ -1,62 +1,94 @@ -use std::iter::{Iterator, Map, Zip}; +use std::iter; -//#[derive(Debug)] -//pub enum FixedXORError { -// Bad, -//} +pub type FixedXOR = iter::Map, fn((u8, u8)) -> u8>; +pub type SingleByteXOR = FixedXOR>; -pub trait FixedXORable { - fn fixed_xor(self, other: T) -> Map, fn((u8, u8)) -> u8>; +pub trait FixedXORable { + fn fixed_xor(self, other: U) -> FixedXOR; } -impl<'a, T> FixedXORable for T - where T: Iterator + 'a, +impl<'a, 'b, T, U> FixedXORable for T + where T: iter::Iterator + 'a, + U: iter::Iterator + 'b { - fn fixed_xor(self, other: T) -> Map, fn((u8, u8)) -> u8> { + fn fixed_xor(self, other: U) -> FixedXOR { fn xor(tup: (u8, u8)) -> u8 { tup.0 ^ tup.1 } self.zip(other).map(xor) } } -//pub fn fixed(a: &str, b: &str) -> Result { -// let a_decoded = a.hex_bytes().valid(); -// let b_decoded = b.hex_bytes().valid(); -// let xor: Vec = a_decoded.zip(b_decoded).map(|(x, y)| x ^ y).collect(); -// Ok(xor.hex_digest()) -//} -// -//#[cfg(test)] -//mod tests { -// use hex::AsHexBytes; -// use std::char; -// use super::fixed; -// -// #[test] -// fn cryptopals() { -// let a = "1c0111001f010100061a024b53535009181c"; -// let b = "686974207468652062756c6c277320657965"; -// let ex_output = "746865206b696420646f6e277420706c6179"; -// let output = fixed(a, b); -// assert_eq!(output.unwrap(), ex_output); -// } -// -// static ENGLISH_LETTER_FREQ: &'static str = "EARIOTNSLCUDPMHGBFYWKVXZJQ"; -// -// fn letter_freq_score(input: &str) -> f32 { -// let mut score: f32 = 0.0; -// score -// } -// -// #[test] -// fn cryptopals13() { -// let input = "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"; -// for key in 32u32..127 { -// let possible_output = input.hex_bytes().valid() -// .map(|c| char::from_u32(c as u32 ^ key)) -// .take_while(|c| c.is_some()) -// .map(|c| c.unwrap()) -// .collect::(); -// println!("{}: {}", key, possible_output); -// } -// } -//} +pub trait SingleByteXORable { + fn single_byte_xor(self, byte: u8) -> SingleByteXOR; +} + +impl<'a, T> SingleByteXORable for T + where T: FixedXORable> +{ + fn single_byte_xor(self, byte: u8) -> SingleByteXOR { + self.fixed_xor(iter::repeat(byte)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use hex::{HexDecodable, HexEncodable}; + + #[test] + fn cryptopals() { + println!(); + let a = "1c0111001f010100061a024b53535009181c"; + let b = "686974207468652062756c6c277320657965"; + let ex_output = "746865206b696420646f6e277420706c6179"; + let output: String = a.chars().hex_decoded() + .fixed_xor(b.chars().hex_decoded()) + .hex_encoded().collect(); + assert_eq!(output, ex_output); + } + + static ENGLISH_LETTER_FREQS: &'static [f32] = &[ + 0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, // A-G + 0.06094, 0.06996, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749, // H-N + 0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758, // O-U + 0.00978, 0.02360, 0.00150, 0.01974, 0.00074 // V-Z + ]; + + fn letter_freq_score(input: &str) -> f32 { + let mut freqs: Vec = iter::repeat(0.0f32).take(26).collect(); + let mut num_alphabetic_chars = 0f32; + for c in input.chars() { + num_alphabetic_chars += 1f32; + if !c.is_alphabetic() { continue; } + let c = c.to_ascii_uppercase(); + freqs[c as usize - 'A' as usize] += 1f32; + } + let freqs = freqs.into_iter().map(|sc| sc / num_alphabetic_chars); + // Calculate chi-squared for this string, comparing actual frequencies vs. English letter + // frequencies. + let score = freqs.zip(ENGLISH_LETTER_FREQS.iter()) + .fold(0f32, |acc, (obs, exp)| acc + (obs - exp).powf(2.0) / exp); + score + } + + #[test] + fn cryptopals13() { + let input = "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"; + let mut best_key = 0u8; + let mut best_score = -1f32; + let mut best_output: Option = None; + for key in 32u8..127 { + let possible_output = input.chars().hex_decoded() + .single_byte_xor(key) + .map(|x| char::from(x)) + .collect::(); + let score = letter_freq_score(&possible_output); + if score > best_score { + best_score = score; + best_output = Some(possible_output); + best_key = key; + } + } + let best_output = best_output.expect("expected output"); + println!("{}: {} -> {}", best_key, best_output, best_score); + } +}