> {
+ let data = crate::bytes::Bytes::from(data);
+ let mut data = data.pad_to_multiple(Self::BLOCK_SIZE);
+ let mut crypter = self.crypter(mode);
+ let mut iv_ = [0u8; Self::BLOCK_SIZE];
+ let mut output_buffer = [0u8; 2 * Self::BLOCK_SIZE];
+
+ iv_.clone_from_slice(iv);
+ let mut iv = iv_;
+ for chunk in data.chunks_exact_mut(Self::BLOCK_SIZE) {
+ match mode {
+ Mode::Decrypt => {
+ let mut old_chunk = [0u8; Self::BLOCK_SIZE];
+ old_chunk.copy_from_slice(chunk);
+
+ let len = crypter.update(chunk, &mut output_buffer);
+ Self::xor_into(&output_buffer, &iv, chunk);
+
+ println!("IV: {:?}\nIB: {:?}\nOB: {:?} ({:?})\n\n", iv, old_chunk, chunk, len);
+
+ iv.clone_from_slice(&old_chunk);
+ }
+ Mode::Encrypt => {
+ Self::xor(chunk, &iv);
+
+ crypter.update(chunk, &mut output_buffer);
+ chunk.copy_from_slice(&output_buffer[0..Self::BLOCK_SIZE]);
+
+ iv.copy_from_slice(chunk);
+ }
+ }
+ }
+ println!("{:?}", data);
+ Some(data.data.clone())
+ }
+}
diff --git a/src/io.rs b/src/io.rs
new file mode 100644
index 0000000..7ea6a1a
--- /dev/null
+++ b/src/io.rs
@@ -0,0 +1,19 @@
+use std::fs::File;
+use std::io::{self, BufRead};
+use std::path::Path;
+
+pub fn read_lines(filename: P) -> io::Result>>
+where
+ P: AsRef,
+{
+ let file = File::open(filename)?;
+ Ok(io::BufReader::new(file).lines())
+}
+
+pub fn read_without_lines(filename: P) -> Result
+where
+ P: AsRef,
+{
+ let lines = read_lines(filename)?;
+ Ok(lines.fold(String::new(), |acc, line| acc + &line.unwrap()))
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..8264ef9
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,170 @@
+mod bytes;
+mod crypto;
+mod io;
+
+use bytes::Bytes;
+use openssl::symm::Mode;
+
+fn q01() {
+ println!("Running q01");
+
+ let input = "49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d";
+ let output = Bytes::from_hex(input).to_base64();
+ let expected = String::from("SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t");
+ assert!(expected == output);
+}
+
+fn q02() {
+ println!("Running q02");
+ let input1 = Bytes::from_hex("1c0111001f010100061a024b53535009181c");
+ let input2 = Bytes::from_hex("686974207468652062756c6c277320657965");
+
+ let expected = Bytes::from_hex("746865206b696420646f6e277420706c6179");
+ let xor = input1.xor_fixed(&input2).unwrap();
+
+ assert!(expected == xor);
+}
+
+fn q03() {
+ println!("Running q03");
+ let input =
+ Bytes::from_hex("1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736");
+ let (best_cipher, _) = input.find_best_single_xor_cipher();
+ let output = input.xor_byte(best_cipher);
+ println!(
+ " 0x{:X}: {}",
+ best_cipher,
+ String::from_utf8(output.data).unwrap()
+ );
+}
+
+fn q04() {
+ println!("Running q04");
+ let mut best_line = Bytes::new();
+ let mut best_score = 0.0f32;
+ let mut best_cipher = 0u8;
+
+ for line in io::read_lines("data/4.txt").unwrap() {
+ if let Ok(line) = line {
+ let line = Bytes::from_hex(&line);
+ let (cipher, score) = line.find_best_single_xor_cipher();
+ if score > best_score {
+ best_score = score;
+ best_cipher = cipher;
+ best_line = line;
+ }
+ }
+ }
+
+ println!(
+ " 0x{:2X} {}",
+ best_cipher,
+ String::from_utf8(best_line.xor_byte(best_cipher).data).unwrap()
+ );
+}
+
+fn q05() {
+ println!("Running q05");
+ let input = Bytes::from_string(
+ "Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal",
+ );
+ let cipher = Bytes::from_string("ICE");
+ let expected = Bytes::from_hex("0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f");
+
+ let output = input.xor_repeating(&cipher);
+
+ assert!(expected == output);
+}
+
+fn q06() {
+ println!("Running q06");
+
+ assert!(
+ 37u32
+ == Bytes::from_string("this is a test")
+ .hamming_distance(&Bytes::from_string("wokka wokka!!!"))
+ );
+
+ let input = io::read_without_lines("data/6.txt").unwrap();
+ let input = Bytes::from_base64(&input);
+
+ let key = input.find_best_xor_cipher();
+ let _output = input.xor_repeating(&key).to_string().unwrap();
+ // println!(" {}", output);
+}
+
+fn q07() {
+ println!("Running q07");
+
+ let input = io::read_without_lines("data/7.txt").unwrap();
+ let input = Bytes::from_base64(&input);
+ let key = Bytes::from_string("YELLOW SUBMARINE");
+ let _output = Bytes {
+ data: crypto::Aes::new(&key).unwrap()
+ .ecb(input.into(), Mode::Decrypt)
+ .unwrap(),
+ };
+ // println!(" {}", _output.to_string().unwrap());
+}
+
+fn q08() {
+ println!("Running q08");
+ let input = io::read_lines("data/8.txt");
+
+ let line_chunks: Vec>> = input
+ .unwrap()
+ .map(|s| {
+ hex::decode(s.unwrap())
+ .unwrap()
+ .chunks_exact(16)
+ .map(|c| Box::from(c))
+ .collect()
+ })
+ .collect();
+ let line_repetitions = line_chunks.into_iter().map(|mut ch| {
+ ch.sort_unstable();
+ ch.iter()
+ .fold((0u32, None), |(n, last), curr| {
+ (n + (if Some(curr) == last { 1 } else { 0 }), Some(curr))
+ })
+ .0
+ });
+ let best_line = line_repetitions
+ .enumerate()
+ .max_by_key(|(_, num)| *num)
+ .unwrap()
+ .0;
+ println!(" Line {} is AES-ECB", best_line);
+}
+
+fn q09() {
+ println!("Running q09");
+ let input = Bytes::from_string("YELLOW SUBMARINE");
+ let len = 20 as usize;
+ let output = input.pad_to_multiple(len);
+ assert!(Bytes::from_string("YELLOW SUBMARINE\x04\x04\x04\x04") == output);
+}
+
+fn q10() {
+ println!("Running q10");
+ let input = io::read_without_lines("data/10.txt").unwrap();
+ let input = Bytes::from_base64(&input);
+ let key = b"YELLOW SUBMARINE";
+ let iv = [0u8; 16];
+
+ let output = crypto::Aes::new(key).unwrap().cbc(input.into(), &iv, Mode::Decrypt);
+ println!("{}", Bytes::from(output.unwrap()).to_string().unwrap());
+}
+
+fn main() {
+ q01();
+ q02();
+ q03();
+ q04();
+ q05();
+ q06();
+ q07();
+ q08();
+ q09();
+ q10();
+}