diff --git a/src/main.rs b/src/main.rs index 2017116..0415d95 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,7 @@ mod set1; -use crate::set1::{base64_to_string, bytes_to_base64, to_bytes}; - -fn test_set1() { - let input = to_bytes("49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"); - let expected = String::from("SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"); - match input { - Ok(bytes) => { - let output = base64_to_string(bytes_to_base64(bytes)); - assert_eq!(output, expected); - println!("{:?}", output); - } - Err(e) => println!("Error: {:?}", e), - }; -} +use crate::set1::challenge1; fn main() { - test_set1(); + challenge1(); } diff --git a/src/set1.rs b/src/set1.rs index 1112a96..ab3580c 100644 --- a/src/set1.rs +++ b/src/set1.rs @@ -1,65 +1,122 @@ use std::num::ParseIntError; use std::str; -pub fn to_bytes(s: &str) -> Result, String> { - if s.len() % 2 != 0 { - return Err(String::from("Input string has uneven number of characters")); - } +pub struct HexBytes(Vec); +pub struct Base64Bytes(Vec); - let bytes_result: Result, ParseIntError> = (0..s.len()) - .step_by(2) - .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) - .collect(); - - match bytes_result { - Ok(b) => Ok(b), - Err(_) => Err(String::from("Could not convert all digit pairs to hex.")), - } -} - -fn chunk_to_base64(c: &[u8]) -> Vec { - let (value, iterations) = match c.len() { - 0 => return vec![], - 1 => ((c[0] as u32) << 16, 2), - 2 => ((c[0] as u32) << 16 | (c[1] as u32) << 8, 3), - 3 => ((c[0] as u32) << 16 | (c[1] as u32) << 8 | (c[2] as u32), 4), - _ => panic!("Unexpected number of chunks {}.", c.len()), - }; - (0..iterations) - .map(|i| (value.rotate_right((3 - i) * 6) & 0b111111) as u8) - .collect() -} - -pub fn bytes_to_base64(bytes: Vec) -> Vec { - bytes - .chunks(3) - .map(|c| chunk_to_base64(c)) - .flatten() - .collect() -} - -pub fn base64_to_string(digits: Vec) -> String { - let mut r: Vec = digits - .iter() - .map(|d| match d { - 0..=25 => *d + ('A' as u8), - 26..=51 => *d - 26 + ('a' as u8), - 52..=61 => *d - 52 + ('0' as u8), - 62 => '+' as u8, - 63 => '/' as u8, - _ => panic!("Unexpected base64 digit '{}'", d), - }) - .collect(); - // Handle padding - let pad = '=' as u8; - match r.len() % 4 { - 0 => (), - 2 => { - r.push(pad); - r.push(pad); +impl std::fmt::Display for Base64Bytes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let Base64Bytes(digits) = self; + let mut r: Vec = digits + .iter() + .map(|d| match d { + 0..=25 => *d + ('A' as u8), + 26..=51 => *d - 26 + ('a' as u8), + 52..=61 => *d - 52 + ('0' as u8), + 62 => '+' as u8, + 63 => '/' as u8, + _ => panic!("Unexpected base64 digit '{}'", d), + }) + .collect(); + // Handle padding + let pad = '=' as u8; + match r.len() % 4 { + 0 => (), + 2 => { + r.push(pad); + r.push(pad); + } + 3 => r.push(pad), + _ => (), } - 3 => r.push(pad), - _ => (), + + write!(f, "Base64({})", str::from_utf8(r.as_slice()).unwrap()) } - str::from_utf8(r.as_slice()).unwrap().to_string() +} + +impl Base64Bytes { + pub fn to_string(&self) -> String { + let Base64Bytes(digits) = self; + let mut r: Vec = digits + .iter() + .map(|d| match d { + 0..=25 => *d + ('A' as u8), + 26..=51 => *d - 26 + ('a' as u8), + 52..=61 => *d - 52 + ('0' as u8), + 62 => '+' as u8, + 63 => '/' as u8, + _ => panic!("Unexpected base64 digit '{}'", d), + }) + .collect(); + // Handle padding + let pad = '=' as u8; + match r.len() % 4 { + 0 => (), + 2 => { + r.push(pad); + r.push(pad); + } + 3 => r.push(pad), + _ => (), + } + str::from_utf8(r.as_slice()).unwrap().to_string() + } +} + +impl HexBytes { + pub fn to_base64(&self) -> Base64Bytes { + fn chunk_to_base64(c: &[u8]) -> Vec { + let (value, iterations) = match c.len() { + 0 => return vec![], + 1 => ((c[0] as u32) << 16, 2), + 2 => ((c[0] as u32) << 16 | (c[1] as u32) << 8, 3), + 3 => ((c[0] as u32) << 16 | (c[1] as u32) << 8 | (c[2] as u32), 4), + _ => panic!("Unexpected number of chunks {}.", c.len()), + }; + + (0..iterations) + .map(|i| (value.rotate_right((3 - i) * 6) & 0b111111) as u8) + .collect() + } + let HexBytes(bytes) = self; + Base64Bytes( + bytes + .chunks(3) + .map(|c| chunk_to_base64(c)) + .flatten() + .collect(), + ) + } + + pub fn from_str(s: &str) -> Result { + if s.len() % 2 != 0 { + return Err(String::from("Input string has uneven number of characters")); + } + + let bytes_result: Result, ParseIntError> = (0..s.len()) + .step_by(2) + .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) + .collect(); + + match bytes_result { + Ok(b) => Ok(HexBytes(b)), + Err(_) => Err(String::from("Could not convert all digit pairs to hex.")), + } + } +} + +pub fn challenge1() { + let input = HexBytes::from_str("49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d"); + let expected = String::from("SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"); + match input { + Ok(bytes) => { + let output = bytes.to_base64(); + if output.to_string() != expected { + println!("HexBytes.to_base64 failed.") + } else { + println!("[okay] Challenge 1: {}", output); + } + } + Err(e) => println!("Error: {}", e), + }; }