cryptopals

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

commit b624127d8ab1e425ea865732ab989cab0613fc16
parent d413c86de35859b6f555a1b10c81a6cf5f1b2e00
Author: Jared Tobin <jared@jtobin.ca>
Date:   Sat,  2 Sep 2017 14:36:33 +1200

Misc cleanup.

Diffstat:
Msrc/errors.rs | 16+++++++++++++---
Msrc/main.rs | 4++--
Msrc/s1c01.rs | 3+--
Msrc/s1c02.rs | 31+++++++++++++++++++++++--------
Msrc/s1c03.rs | 52++++++++++++++++++++++++++++++----------------------
Msrc/s1c07.rs | 39+++++++++------------------------------
Msrc/s2c09.rs | 4++--
Msrc/s2c10.rs | 38++++++++------------------------------
Msrc/s2c11.rs | 4++--
Msrc/s2c12.rs | 40+++++++++++++++++++++++++++++++++-------
10 files changed, 123 insertions(+), 108 deletions(-)

diff --git a/src/errors.rs b/src/errors.rs @@ -4,15 +4,20 @@ extern crate hex; use std::error; use std::fmt; use std::process; +use std::string; pub enum CryptopalsError { - HexConversionError(hex::FromHexError) + HexConversionError(hex::FromHexError), + Utf8ConversionError(string::FromUtf8Error) } impl fmt::Display for CryptopalsError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { CryptopalsError::HexConversionError(ref err) => + fmt::Display::fmt(err, f), + + CryptopalsError::Utf8ConversionError(ref err) => fmt::Display::fmt(err, f) } } @@ -21,13 +26,15 @@ impl fmt::Display for CryptopalsError { impl error::Error for CryptopalsError { fn description(&self) -> &str { match *self { - CryptopalsError::HexConversionError(ref err) => err.description() + CryptopalsError::HexConversionError(ref err) => err.description(), + CryptopalsError::Utf8ConversionError(ref err) => err.description() } } fn cause(&self) -> Option<&error::Error> { match *self { - CryptopalsError::HexConversionError(ref err) => Some(err) + CryptopalsError::HexConversionError(ref err) => Some(err), + CryptopalsError::Utf8ConversionError(ref err) => Some(err) } } } @@ -36,6 +43,9 @@ impl fmt::Debug for CryptopalsError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { CryptopalsError::HexConversionError(ref err) => + fmt::Debug::fmt(err, f), + + CryptopalsError::Utf8ConversionError(ref err) => fmt::Debug::fmt(err, f) } } diff --git a/src/main.rs b/src/main.rs @@ -13,8 +13,8 @@ mod errors; fn main() { println!("s1c01:\n{}\n", errors::handle(s1c01::s1c01())); - println!("s1c02:\n{}\n", s1c02::s1c02()); - println!("s1c03:\n{}\n", s1c03::s1c03()); + println!("s1c02:\n{}\n", errors::handle(s1c02::s1c02())); + println!("s1c03:\n{}\n", errors::handle(s1c03::s1c03())); println!("s1c07:\n{}\n", s1c07::s1c07()); println!("s2c09:\n{}\n", s2c09::s2c09()); println!("s2c10:\n{}\n", s2c10::s2c10()); diff --git a/src/s1c01.rs b/src/s1c01.rs @@ -3,8 +3,7 @@ extern crate base64; extern crate hex; use errors::CryptopalsError; -use self::hex::{FromHex, FromHexError}; -use std::process; +use self::hex::FromHex; const INPUT: &str = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6\ diff --git a/src/s1c02.rs b/src/s1c02.rs @@ -1,20 +1,35 @@ extern crate hex; +use errors::CryptopalsError; 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(); +pub fn fixed_xor(target: &[u8], partner: &[u8]) -> Vec<u8> { + target.iter() + .zip(partner) + .map(|(l, r)| l ^ r) + .collect() +} - for (lb, rb) in l.iter_mut().zip(r) { *lb ^= rb } +pub fn s1c02() -> Result<String, CryptopalsError> { + let target: Result<Vec<u8>, _> = FromHex::from_hex(&TARGET) + .map_err(|err| CryptopalsError::HexConversionError(err)); - l.to_hex() -} + let target = match target { + Ok(val) => val, + Err(err) => return Err(err) + }; + + let partner: Result<Vec<u8>, _> = FromHex::from_hex(&PARTNER) + .map_err(|err| CryptopalsError::HexConversionError(err)); + + let partner = match partner { + Ok(val) => val, + Err(err) => return Err(err) + }; -pub fn s1c02() -> String { - fixed_xor(&TARGET, &PARTNER) + Ok(fixed_xor(&target, &partner).to_hex()) } diff --git a/src/s1c03.rs b/src/s1c03.rs @@ -1,7 +1,7 @@ extern crate hex; -use self::hex::{FromHex, ToHex}; +use errors::CryptopalsError; use std::collections::HashMap; use std::f32; use std::u8; @@ -148,7 +148,10 @@ fn mse(reference: HashMap<u8, f32>, target: HashMap<u8, f32>) -> f32 { for (key, val) in reference.iter() { if target.contains_key(key) { - // (jtobin) branch is only entered if 'target' contains 'key' + // NB. (jtobin) + // + // Branch is only entered if 'target' contains 'key', so 'unwrap' + // can't be called. let tval = target.get(key).unwrap(); let sqdiff = (tval - val).powf(2.0); result.insert(key, sqdiff); @@ -160,36 +163,41 @@ fn mse(reference: HashMap<u8, f32>, target: HashMap<u8, f32>) -> f32 { 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) +fn score(input: &[u8]) -> f32 { + mse(freqs_ascii(), frequency_distribution(input.to_vec())) } -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); +pub fn break_single_byte_xor(bytes: &[u8]) -> (u8, Vec<u8>) { + let mut min = (Vec::new(), 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 xored: Vec<u8> = bytes.iter() + .map(|byte| byte ^ ascii_char) + .collect(); - let decoded = String::from_utf8(other_bytes).unwrap(); - let encoded = ToHex::to_hex(&decoded.clone()); - let result = score(&encoded); + let result = score(&xored); - if result < min.2 { min = (decoded, ascii_char, result); } + if result < min.2 { + min = (xored, ascii_char, result); + } } (min.1, min.0) } -pub fn s1c03() -> String { - break_single_byte_xor(INPUT).1 +pub fn s1c03() -> Result<String, CryptopalsError> { + let ciphertext: Result<Vec<u8>, CryptopalsError> = + hex::FromHex::from_hex(INPUT) + .map_err(|err| CryptopalsError::HexConversionError(err)); + + let ciphertext = match ciphertext { + Ok(val) => val, + Err(err) => return Err(err) + }; + + let message = break_single_byte_xor(&ciphertext).1; + + String::from_utf8(message) + .map_err(|err| CryptopalsError::Utf8ConversionError(err)) } diff --git a/src/s1c07.rs b/src/s1c07.rs @@ -5,7 +5,6 @@ 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( @@ -36,42 +35,22 @@ pub fn aes_128_ecb_crypt(mode: Mode, key: &[u8], content: &[u8]) -> Vec<u8> { 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 _ = handle.read_to_string(&mut buffer).unwrap(); - let trimmed: String = buffer.chars() - .filter(|&char| char != '\n') + let ciphertext: String = buffer.chars() + .filter(|&c| c != '\n') .collect(); - let decrypted = aes_128_ecb_decrypt(&key, &trimmed); - let decrypted = base64::decode(&decrypted).unwrap(); + let ciphertext = base64::decode(&ciphertext).unwrap(); + + let key = b"YELLOW SUBMARINE"; + + let message = aes_128_ecb_crypt(Mode::Decrypt, &key[..], &ciphertext); - String::from_utf8(decrypted).unwrap() + String::from_utf8(message).unwrap() } diff --git a/src/s2c09.rs b/src/s2c09.rs @@ -1,7 +1,7 @@ extern crate base64; -pub fn pkcs(block: &[u8], size: usize) -> Vec<u8> { +pub fn pad_pkcs7(block: &[u8], size: usize) -> Vec<u8> { let mut vec = Vec::with_capacity(size); let len = block.len(); @@ -15,7 +15,7 @@ pub fn pkcs(block: &[u8], size: usize) -> Vec<u8> { pub fn s2c09() -> String { let message = "YELLOW_SUBMARINE".as_bytes(); - let padded = pkcs(message, 20); + let padded = pad_pkcs7(message, 20); base64::encode(&padded) } diff --git a/src/s2c10.rs b/src/s2c10.rs @@ -53,31 +53,7 @@ pub fn aes_128_cbc_crypt( buffer } -pub fn aes_128_cbc_decrypt(key: &str, iv: &str, ciphertext: &str) -> String { - let key: Vec<u8> = base64::decode(&key).unwrap(); - let iv: Vec<u8> = base64::decode(&iv).unwrap(); - let ciphertext: Vec<u8> = base64::decode(&ciphertext).unwrap(); - - let message = - aes_128_cbc_crypt(Mode::Decrypt, &key[..], &iv[..], &ciphertext[..]); - - base64::encode(&message) -} - -pub fn aes_128_cbc_encrypt(key: &str, iv: &str, message: &str) -> String { - let key: Vec<u8> = base64::decode(&key).unwrap(); - let iv: Vec<u8> = base64::decode(&iv).unwrap(); - let message: Vec<u8> = base64::decode(&message).unwrap(); - - let ciphertext = - aes_128_cbc_crypt(Mode::Encrypt, &key[..], &iv[..], &message[..]); - - base64::encode(&ciphertext) -} - - pub fn s2c10() -> String { - let key = base64::encode("YELLOW SUBMARINE"); let mut handle = File::open("data/s2/q10_input.txt").unwrap(); let mut buffer = String::new(); @@ -87,16 +63,18 @@ pub fn s2c10() -> String { panic!("{}", err); } - let trimmed: String = buffer.chars() + let ciphertext: String = buffer.chars() .filter(|&char| char != '\n') .collect(); - let iv = vec![0u8; BLOCK_SIZE]; - let iv = base64::encode(&iv); + let ciphertext = base64::decode(&ciphertext).unwrap(); + + let key = b"YELLOW SUBMARINE"; + + let iv = vec![0u8; BLOCK_SIZE]; - let decrypted = aes_128_cbc_decrypt(&key, &iv, &trimmed); - let decrypted = base64::decode(&decrypted).unwrap(); + let message = aes_128_cbc_crypt(Mode::Decrypt, &key[..], &iv, &ciphertext); - String::from_utf8(decrypted).unwrap() + String::from_utf8(message).unwrap() } diff --git a/src/s2c11.rs b/src/s2c11.rs @@ -3,7 +3,7 @@ extern crate rand; extern crate openssl; use s1c07::aes_128_ecb_crypt; -use s2c09::pkcs; +use s2c09::pad_pkcs7; use s2c10::aes_128_cbc_crypt; use self::openssl::symm::Mode; use self::rand::Rng; @@ -43,7 +43,7 @@ pub fn black_box_encrypter(message: &[u8]) -> Vec<u8> { ciphertext.extend_from_slice(message); ciphertext.extend_from_slice(&append); - ciphertext = pkcs(&ciphertext, c_size); + ciphertext = pad_pkcs7(&ciphertext, c_size); let key = gen_bytes(KEY_SIZE); diff --git a/src/s2c12.rs b/src/s2c12.rs @@ -3,19 +3,23 @@ extern crate base64; extern crate openssl; use s1c07::aes_128_ecb_crypt; -use s2c09::pkcs; -use s2c11; +use s2c09::pad_pkcs7; use self::openssl::symm::Mode; const BLOCK_SIZE: usize = 16; const APPENDER: &str = - "Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg - aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq - dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg + "Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg\ + aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq\ + dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg\ YnkK"; -pub fn mystery_crypter(message: &[u8], key: &[u8]) -> Vec<u8> { +// FIXME (jtobin) +// +// Consider just adjusting the original encryption function such that messages +// are always padded appropriately. +// +pub fn aes_128_ecb_crypt_padding(message: &[u8], key: &[u8]) -> Vec<u8> { let m_size = message.len() + APPENDER.len(); let c_size = m_size + BLOCK_SIZE - m_size % BLOCK_SIZE; @@ -26,12 +30,34 @@ pub fn mystery_crypter(message: &[u8], key: &[u8]) -> Vec<u8> { ciphertext.extend_from_slice(message); ciphertext.extend_from_slice(&appender); - ciphertext = pkcs(&ciphertext, c_size); + ciphertext = pad_pkcs7(&ciphertext, c_size); aes_128_ecb_crypt(Mode::Encrypt, key, &ciphertext) } +pub fn blocksize_oracle(message: &[u8], key: &[u8]) -> usize { + let mut input = Vec::new(); + let head = message[0]; + + loop { + input.push(head); + + let ciphertext = aes_128_ecb_crypt_padding(&message, &key); + + if ciphertext.len() > message.len() { + return ciphertext.len() - message.len(); + } + } +} + pub fn s2c12() -> String { + + let tester = String::from("hurbitty gurbitty"); + let key = String::from("YELLOW SUBMARINE"); + let foo = blocksize_oracle(&tester.as_bytes(), &key.as_bytes()); + + println!("{}", foo); + String::from("foo") }