cryptopals

Matasano's cryptopals challenges (cryptopals.com).
Log | Files | Refs | README | LICENSE

commit e082523b3c72697a90d47b8a4fd30d92401b78a0
parent f3f35c242f9cad7a9550ce9ab21a4ae0f944733e
Author: Jared Tobin <jared@jtobin.ca>
Date:   Thu, 31 Aug 2017 16:47:39 +1200

Renames.

Diffstat:
Mdocs/s2.md | 4+++-
Msrc/main.rs | 20++++++++++----------
Asrc/s1c01.rs | 23+++++++++++++++++++++++
Asrc/s1c02.rs | 20++++++++++++++++++++
Asrc/s1c03.rs | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/s1c07.rs | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/s1c1.rs | 23-----------------------
Dsrc/s1c2.rs | 20--------------------
Dsrc/s1c3.rs | 195-------------------------------------------------------------------------------
Dsrc/s1c7.rs | 77-----------------------------------------------------------------------------
Asrc/s2c09.rs | 21+++++++++++++++++++++
Msrc/s2c10.rs | 2+-
Msrc/s2c11.rs | 4++--
Dsrc/s2c9.rs | 21---------------------
14 files changed, 352 insertions(+), 350 deletions(-)

diff --git a/docs/s2.md b/docs/s2.md @@ -63,5 +63,7 @@ and then make the guess: that's probably CBC-encrypted. In any case, it's not exactly easy to repeat without actually writing a script. -Check out [the Rust source](../src/s1c11.rs) to see a sane version. +Check out [the Rust source][src] to see a sane version. + +[src]: https://github.com/jtobin/cryptopals/blob/master/src/s2c11.rs diff --git a/src/main.rs b/src/main.rs @@ -1,19 +1,19 @@ -mod s1c1; -mod s1c2; -mod s1c3; -mod s1c7; +mod s1c01; +mod s1c02; +mod s1c03; +mod s1c07; -mod s2c9; +mod s2c09; mod s2c10; mod s2c11; fn main() { - println!("s1c1:\n{}\n", s1c1::s1c1()); - println!("s1c2:\n{}\n", s1c2::s1c2()); - println!("s1c3:\n{}\n", s1c3::s1c3()); - println!("s1c7:\n{}\n", s1c7::s1c7()); - println!("s2c9:\n{}\n", s2c9::s2c9()); + println!("s1c01:\n{}\n", s1c01::s1c01()); + println!("s1c02:\n{}\n", s1c02::s1c02()); + println!("s1c03:\n{}\n", s1c03::s1c03()); + println!("s1c07:\n{}\n", s1c07::s1c07()); + println!("s2c09:\n{}\n", s2c09::s2c09()); println!("s2c10:\n{}\n", s2c10::s2c10()); println!("s2c11:\n{}\n", s2c11::s2c11()); } diff --git a/src/s1c01.rs b/src/s1c01.rs @@ -0,0 +1,23 @@ + +extern crate base64; +extern crate hex; + +use self::hex::{FromHex, FromHexError}; +use std::process; + +const INPUT: &str = + "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6\ + f7573206d757368726f6f6d"; + +pub fn hex_to_b64(input: &str) -> Result<String, FromHexError> { + let raw: Result<Vec<u8>, _> = FromHex::from_hex(&input); + raw.map(|contents| base64::encode(&contents)) +} + +pub fn s1c01() -> String { + hex_to_b64(&INPUT).unwrap_or_else(|err| { + println!("error (cryptopals): {}", err); + process::exit(1); + }) +} + diff --git a/src/s1c02.rs b/src/s1c02.rs @@ -0,0 +1,20 @@ + +extern crate hex; + +use self::hex::{FromHex, ToHex}; + +const TARGET: &str = "1c0111001f010100061a024b53535009181c"; +const PARTNER: &str = "686974207468652062756c6c277320657965"; + +fn fixed_xor(target: &str, partner: &str) -> String { + let mut l: Vec<u8> = FromHex::from_hex(&target).unwrap(); + let r: Vec<u8> = FromHex::from_hex(&partner).unwrap(); + + for (lb, rb) in l.iter_mut().zip(r) { *lb ^= rb } + + l.to_hex() +} + +pub fn s1c02() -> String { + fixed_xor(&TARGET, &PARTNER) +} diff --git a/src/s1c03.rs b/src/s1c03.rs @@ -0,0 +1,195 @@ + +extern crate hex; + +use self::hex::{FromHex, ToHex}; +use std::collections::HashMap; +use std::f32; +use std::u8; + +const INPUT: &'static str = + "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"; + +fn tally(vec: Vec<u8>) -> HashMap<u8, u8> { + let mut hashmap = HashMap::new(); + + for byte in vec { + let count = hashmap.entry(byte).or_insert(0); + *count += 1; + } + + hashmap +} + +fn normalize(hashmap: HashMap<u8, u8>) -> HashMap<u8, f32> { + let total = hashmap.iter().fold(0.0, |sum, (_, val)| sum + *val as f32); + + hashmap.iter() + .map(|(&key, val)| (key, *val as f32 / total)) + .collect() +} + +fn frequency_distribution(vec: Vec<u8>) -> HashMap<u8, f32> { + let tallied = tally(vec); + normalize(tallied) +} + +pub fn freqs_ascii() -> HashMap<u8, f32> { + [ (9, 0.000057) + , (23, 0.000000) + , (32, 0.171662) + , (33, 0.000072) + , (34, 0.002442) + , (35, 0.000179) + , (36, 0.000561) + , (37, 0.000160) + , (38, 0.000226) + , (39, 0.002447) + , (40, 0.002178) + , (41, 0.002233) + , (42, 0.000628) + , (43, 0.000215) + , (44, 0.007384) + , (45, 0.013734) + , (46, 0.015124) + , (47, 0.001549) + , (48, 0.005516) + , (49, 0.004594) + , (50, 0.003322) + , (51, 0.001847) + , (52, 0.001348) + , (53, 0.001663) + , (54, 0.001153) + , (55, 0.001030) + , (56, 0.001054) + , (57, 0.001024) + , (58, 0.004354) + , (59, 0.001214) + , (60, 0.001225) + , (61, 0.000227) + , (62, 0.001242) + , (63, 0.001474) + , (64, 0.000073) + , (65, 0.003132) + , (66, 0.002163) + , (67, 0.003906) + , (68, 0.003151) + , (69, 0.002673) + , (70, 0.001416) + , (71, 0.001876) + , (72, 0.002321) + , (73, 0.003211) + , (74, 0.001726) + , (75, 0.000687) + , (76, 0.001884) + , (77, 0.003529) + , (78, 0.002085) + , (79, 0.001842) + , (80, 0.002614) + , (81, 0.000316) + , (82, 0.002519) + , (83, 0.004003) + , (84, 0.003322) + , (85, 0.000814) + , (86, 0.000892) + , (87, 0.002527) + , (88, 0.000343) + , (89, 0.000304) + , (90, 0.000076) + , (91, 0.000086) + , (92, 0.000016) + , (93, 0.000088) + , (94, 0.000003) + , (95, 0.001159) + , (96, 0.000009) + , (97, 0.051880) + , (98, 0.010195) + , (99, 0.021129) + , (100, 0.025071) + , (101, 0.085771) + , (102, 0.013725) + , (103, 0.015597) + , (104, 0.027444) + , (105, 0.049019) + , (106, 0.000867) + , (107, 0.006753) + , (108, 0.031750) + , (109, 0.016437) + , (110, 0.049701) + , (111, 0.057701) + , (112, 0.015482) + , (113, 0.000747) + , (114, 0.042586) + , (115, 0.043686) + , (116, 0.063700) + , (117, 0.020999) + , (118, 0.008462) + , (119, 0.013034) + , (120, 0.001950) + , (121, 0.011330) + , (122, 0.000596) + , (123, 0.000026) + , (124, 0.000007) + , (125, 0.000026) + , (126, 0.000003) + , (131, 0.000000) + , (149, 0.006410) + , (183, 0.000010) + , (223, 0.000000) + , (226, 0.000000) + , (229, 0.000000) + , (230, 0.000000) + , (237, 0.000000) + ].iter().cloned().collect() +} + +fn mse(reference: HashMap<u8, f32>, target: HashMap<u8, f32>) -> f32 { + let mut result = HashMap::new(); + + for (key, val) in reference.iter() { + if target.contains_key(key) { + + // (jtobin) branch is only entered if 'target' contains 'key' + let tval = target.get(key).unwrap(); + let sqdiff = (tval - val).powf(2.0); + result.insert(key, sqdiff); + } + } + + let size = result.len(); + + result.iter().fold(0.0, |sum, (_, val)| sum + val / size as f32) +} + +fn score(string: &str) -> f32 { + let decoded = FromHex::from_hex(&string).unwrap(); + let freq_dist = frequency_distribution(decoded); + + mse(freqs_ascii(), freq_dist) +} + +pub fn break_single_byte_xor(string: &str) -> (u8, String) { + let bytes: Vec<u8> = FromHex::from_hex(&string).unwrap(); + + let mut min = ("hi!".to_string(), 0, f32::INFINITY); + + for ascii_char in 32..126 { + let mut other_bytes = bytes.clone(); + + for byte in other_bytes.iter_mut() { + *byte ^= ascii_char; + } + + let decoded = String::from_utf8(other_bytes).unwrap(); + let encoded = ToHex::to_hex(&decoded.clone()); + let result = score(&encoded); + + if result < min.2 { min = (decoded, ascii_char, result); } + } + + (min.1, min.0) +} + +pub fn s1c03() -> String { + break_single_byte_xor(INPUT).1 +} + diff --git a/src/s1c07.rs b/src/s1c07.rs @@ -0,0 +1,77 @@ + +extern crate base64; +extern crate openssl; + +use self::openssl::symm::{Cipher, Crypter, Mode}; +use std::fs::File; +use std::io::prelude::Read; +use std::str; +use std::string::String; + +pub fn new_crypter_unpadded( + cipher: Cipher, + mode: Mode, + key: &[u8], + iv: Option<&[u8]> + ) -> Crypter { + + let mut crypter = Crypter::new(cipher, mode, key, iv).unwrap(); + + crypter.pad(false); + + crypter +} + +pub fn aes_128_ecb_crypt(mode: Mode, key: &[u8], content: &[u8]) -> Vec<u8> { + let cipher = Cipher::aes_128_ecb(); + let iv = None; + let bsize = content.len() + cipher.key_len(); + let mut buffer = vec![0; bsize]; + + let mut crypter = new_crypter_unpadded(cipher, mode, key, iv); + + let crypted_len = crypter.update(content, &mut buffer).unwrap(); + let finalized_len = crypter.finalize(&mut buffer).unwrap(); + + buffer[0..crypted_len + finalized_len].to_vec() +} + +pub fn aes_128_ecb_decrypt(key: &str, ciphertext: &str) -> String { + let key: Vec<u8> = base64::decode(&key).unwrap(); + let ciphertext: Vec<u8> = base64::decode(&ciphertext).unwrap(); + + let message = aes_128_ecb_crypt(Mode::Decrypt, &key[..], &ciphertext[..]); + + base64::encode(&message) +} + +pub fn aes_128_ecb_encrypt(key: &str, message: &str) -> String { + let key: Vec<u8> = base64::decode(&key).unwrap(); + let message: Vec<u8> = base64::decode(&message).unwrap(); + + let ciphertext = aes_128_ecb_crypt(Mode::Encrypt, &key[..], &message[..]); + + base64::encode(&ciphertext) +} + +pub fn s1c07() -> String { + let key = base64::encode("YELLOW SUBMARINE"); + let mut handle = File::open("data/s1/q7_input.txt").unwrap(); + let mut buffer = String::new(); + + let bsize = handle.read_to_string(&mut buffer); + + if let Err(err) = bsize { + panic!("{}", err); + } + + let trimmed: String = buffer.chars() + .filter(|&char| char != '\n') + .collect(); + + let decrypted = aes_128_ecb_decrypt(&key, &trimmed); + let decrypted = base64::decode(&decrypted).unwrap(); + + String::from_utf8(decrypted).unwrap() +} + diff --git a/src/s1c1.rs b/src/s1c1.rs @@ -1,23 +0,0 @@ - -extern crate base64; -extern crate hex; - -use self::hex::{FromHex, FromHexError}; -use std::process; - -const INPUT: &str = - "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6\ - f7573206d757368726f6f6d"; - -pub fn hex_to_b64(input: &str) -> Result<String, FromHexError> { - let raw: Result<Vec<u8>, _> = FromHex::from_hex(&input); - raw.map(|contents| base64::encode(&contents)) -} - -pub fn s1c1() -> String { - hex_to_b64(&INPUT).unwrap_or_else(|err| { - println!("error (cryptopals): {}", err); - process::exit(1); - }) -} - diff --git a/src/s1c2.rs b/src/s1c2.rs @@ -1,20 +0,0 @@ - -extern crate hex; - -use self::hex::{FromHex, ToHex}; - -const TARGET: &str = "1c0111001f010100061a024b53535009181c"; -const PARTNER: &str = "686974207468652062756c6c277320657965"; - -fn fixed_xor(target: &str, partner: &str) -> String { - let mut l: Vec<u8> = FromHex::from_hex(&target).unwrap(); - let r: Vec<u8> = FromHex::from_hex(&partner).unwrap(); - - for (lb, rb) in l.iter_mut().zip(r) { *lb ^= rb } - - l.to_hex() -} - -pub fn s1c2() -> String { - fixed_xor(&TARGET, &PARTNER) -} diff --git a/src/s1c3.rs b/src/s1c3.rs @@ -1,195 +0,0 @@ - -extern crate hex; - -use self::hex::{FromHex, ToHex}; -use std::collections::HashMap; -use std::f32; -use std::u8; - -const INPUT: &'static str = - "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"; - -fn tally(vec: Vec<u8>) -> HashMap<u8, u8> { - let mut hashmap = HashMap::new(); - - for byte in vec { - let count = hashmap.entry(byte).or_insert(0); - *count += 1; - } - - hashmap -} - -fn normalize(hashmap: HashMap<u8, u8>) -> HashMap<u8, f32> { - let total = hashmap.iter().fold(0.0, |sum, (_, val)| sum + *val as f32); - - hashmap.iter() - .map(|(&key, val)| (key, *val as f32 / total)) - .collect() -} - -fn frequency_distribution(vec: Vec<u8>) -> HashMap<u8, f32> { - let tallied = tally(vec); - normalize(tallied) -} - -pub fn freqs_ascii() -> HashMap<u8, f32> { - [ (9, 0.000057) - , (23, 0.000000) - , (32, 0.171662) - , (33, 0.000072) - , (34, 0.002442) - , (35, 0.000179) - , (36, 0.000561) - , (37, 0.000160) - , (38, 0.000226) - , (39, 0.002447) - , (40, 0.002178) - , (41, 0.002233) - , (42, 0.000628) - , (43, 0.000215) - , (44, 0.007384) - , (45, 0.013734) - , (46, 0.015124) - , (47, 0.001549) - , (48, 0.005516) - , (49, 0.004594) - , (50, 0.003322) - , (51, 0.001847) - , (52, 0.001348) - , (53, 0.001663) - , (54, 0.001153) - , (55, 0.001030) - , (56, 0.001054) - , (57, 0.001024) - , (58, 0.004354) - , (59, 0.001214) - , (60, 0.001225) - , (61, 0.000227) - , (62, 0.001242) - , (63, 0.001474) - , (64, 0.000073) - , (65, 0.003132) - , (66, 0.002163) - , (67, 0.003906) - , (68, 0.003151) - , (69, 0.002673) - , (70, 0.001416) - , (71, 0.001876) - , (72, 0.002321) - , (73, 0.003211) - , (74, 0.001726) - , (75, 0.000687) - , (76, 0.001884) - , (77, 0.003529) - , (78, 0.002085) - , (79, 0.001842) - , (80, 0.002614) - , (81, 0.000316) - , (82, 0.002519) - , (83, 0.004003) - , (84, 0.003322) - , (85, 0.000814) - , (86, 0.000892) - , (87, 0.002527) - , (88, 0.000343) - , (89, 0.000304) - , (90, 0.000076) - , (91, 0.000086) - , (92, 0.000016) - , (93, 0.000088) - , (94, 0.000003) - , (95, 0.001159) - , (96, 0.000009) - , (97, 0.051880) - , (98, 0.010195) - , (99, 0.021129) - , (100, 0.025071) - , (101, 0.085771) - , (102, 0.013725) - , (103, 0.015597) - , (104, 0.027444) - , (105, 0.049019) - , (106, 0.000867) - , (107, 0.006753) - , (108, 0.031750) - , (109, 0.016437) - , (110, 0.049701) - , (111, 0.057701) - , (112, 0.015482) - , (113, 0.000747) - , (114, 0.042586) - , (115, 0.043686) - , (116, 0.063700) - , (117, 0.020999) - , (118, 0.008462) - , (119, 0.013034) - , (120, 0.001950) - , (121, 0.011330) - , (122, 0.000596) - , (123, 0.000026) - , (124, 0.000007) - , (125, 0.000026) - , (126, 0.000003) - , (131, 0.000000) - , (149, 0.006410) - , (183, 0.000010) - , (223, 0.000000) - , (226, 0.000000) - , (229, 0.000000) - , (230, 0.000000) - , (237, 0.000000) - ].iter().cloned().collect() -} - -fn mse(reference: HashMap<u8, f32>, target: HashMap<u8, f32>) -> f32 { - let mut result = HashMap::new(); - - for (key, val) in reference.iter() { - if target.contains_key(key) { - - // (jtobin) branch is only entered if 'target' contains 'key' - let tval = target.get(key).unwrap(); - let sqdiff = (tval - val).powf(2.0); - result.insert(key, sqdiff); - } - } - - let size = result.len(); - - result.iter().fold(0.0, |sum, (_, val)| sum + val / size as f32) -} - -fn score(string: &str) -> f32 { - let decoded = FromHex::from_hex(&string).unwrap(); - let freq_dist = frequency_distribution(decoded); - - mse(freqs_ascii(), freq_dist) -} - -pub fn break_single_byte_xor(string: &str) -> (u8, String) { - let bytes: Vec<u8> = FromHex::from_hex(&string).unwrap(); - - let mut min = ("hi!".to_string(), 0, f32::INFINITY); - - for ascii_char in 32..126 { - let mut other_bytes = bytes.clone(); - - for byte in other_bytes.iter_mut() { - *byte ^= ascii_char; - } - - let decoded = String::from_utf8(other_bytes).unwrap(); - let encoded = ToHex::to_hex(&decoded.clone()); - let result = score(&encoded); - - if result < min.2 { min = (decoded, ascii_char, result); } - } - - (min.1, min.0) -} - -pub fn s1c3() -> String { - break_single_byte_xor(INPUT).1 -} - diff --git a/src/s1c7.rs b/src/s1c7.rs @@ -1,77 +0,0 @@ - -extern crate base64; -extern crate openssl; - -use self::openssl::symm::{Cipher, Crypter, Mode}; -use std::fs::File; -use std::io::prelude::Read; -use std::str; -use std::string::String; - -pub fn new_crypter_unpadded( - cipher: Cipher, - mode: Mode, - key: &[u8], - iv: Option<&[u8]> - ) -> Crypter { - - let mut crypter = Crypter::new(cipher, mode, key, iv).unwrap(); - - crypter.pad(false); - - crypter -} - -pub fn aes_128_ecb_crypt(mode: Mode, key: &[u8], content: &[u8]) -> Vec<u8> { - let cipher = Cipher::aes_128_ecb(); - let iv = None; - let bsize = content.len() + cipher.key_len(); - let mut buffer = vec![0; bsize]; - - let mut crypter = new_crypter_unpadded(cipher, mode, key, iv); - - let crypted_len = crypter.update(content, &mut buffer).unwrap(); - let finalized_len = crypter.finalize(&mut buffer).unwrap(); - - buffer[0..crypted_len + finalized_len].to_vec() -} - -pub fn aes_128_ecb_decrypt(key: &str, ciphertext: &str) -> String { - let key: Vec<u8> = base64::decode(&key).unwrap(); - let ciphertext: Vec<u8> = base64::decode(&ciphertext).unwrap(); - - let message = aes_128_ecb_crypt(Mode::Decrypt, &key[..], &ciphertext[..]); - - base64::encode(&message) -} - -pub fn aes_128_ecb_encrypt(key: &str, message: &str) -> String { - let key: Vec<u8> = base64::decode(&key).unwrap(); - let message: Vec<u8> = base64::decode(&message).unwrap(); - - let ciphertext = aes_128_ecb_crypt(Mode::Encrypt, &key[..], &message[..]); - - base64::encode(&ciphertext) -} - -pub fn s1c7() -> String { - let key = base64::encode("YELLOW SUBMARINE"); - let mut handle = File::open("data/s1/q7_input.txt").unwrap(); - let mut buffer = String::new(); - - let bsize = handle.read_to_string(&mut buffer); - - if let Err(err) = bsize { - panic!("{}", err); - } - - let trimmed: String = buffer.chars() - .filter(|&char| char != '\n') - .collect(); - - let decrypted = aes_128_ecb_decrypt(&key, &trimmed); - let decrypted = base64::decode(&decrypted).unwrap(); - - String::from_utf8(decrypted).unwrap() -} - diff --git a/src/s2c09.rs b/src/s2c09.rs @@ -0,0 +1,21 @@ + +extern crate base64; + +pub fn pkcs(block: &[u8], size: usize) -> Vec<u8> { + let mut vec = Vec::with_capacity(size); + let len = block.len(); + + let padding_len = if len < size { (size - len) as u8 } else { 0 }; + let padding = vec![padding_len; padding_len as usize ]; + + vec.extend_from_slice(block); + vec.extend_from_slice(&padding); + vec +} + +pub fn s2c09() -> String { + let message = "YELLOW_SUBMARINE".as_bytes(); + let padded = pkcs(message, 20); + + base64::encode(&padded) +} diff --git a/src/s2c10.rs b/src/s2c10.rs @@ -3,7 +3,7 @@ extern crate base64; extern crate hex; extern crate openssl; -use s1c7::aes_128_ecb_crypt; +use s1c07::aes_128_ecb_crypt; use std::fs::File; use self::openssl::symm::Mode; use std::io::Read; diff --git a/src/s2c11.rs b/src/s2c11.rs @@ -2,8 +2,8 @@ extern crate rand; extern crate openssl; -use s1c7::aes_128_ecb_crypt; -use s2c9::pkcs; +use s1c07::aes_128_ecb_crypt; +use s2c09::pkcs; use s2c10::aes_128_cbc_crypt; use self::openssl::symm::Mode; use self::rand::Rng; diff --git a/src/s2c9.rs b/src/s2c9.rs @@ -1,21 +0,0 @@ - -extern crate base64; - -pub fn pkcs(block: &[u8], size: usize) -> Vec<u8> { - let mut vec = Vec::with_capacity(size); - let len = block.len(); - - let padding_len = if len < size { (size - len) as u8 } else { 0 }; - let padding = vec![padding_len; padding_len as usize ]; - - vec.extend_from_slice(block); - vec.extend_from_slice(&padding); - vec -} - -pub fn s2c9() -> String { - let message = "YELLOW_SUBMARINE".as_bytes(); - let padded = pkcs(message, 20); - - base64::encode(&padded) -}