Created
July 9, 2019 16:41
-
-
Save clevinson/6d28d89482e526ca5fd43e43a2a94da8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "cypher" | |
version = "0.1.0" | |
authors = ["Cory Levinson <[email protected]>"] | |
edition = "2018" | |
[dependencies] | |
hex = "0.3.2" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::str; | |
use std::ops::BitXor; | |
use std::env; | |
use hex; | |
#[derive(Debug, PartialEq)] | |
struct ByteArray(Vec<u8>); | |
#[derive(Debug)] | |
enum Mode { | |
Encrypt, | |
Decrypt, | |
Help } | |
impl Default for Mode { | |
fn default() -> Self { Mode::Help } | |
} | |
#[derive(Debug, Default)] | |
struct Configuration { | |
mode: Mode, | |
key: Option<String>, | |
message: Option<String> | |
} | |
impl BitXor for ByteArray { | |
type Output = Self; | |
fn bitxor(self, ByteArray(rhs): Self) -> Self::Output { | |
let ByteArray(lhs) = self; | |
if lhs.len() != rhs.len() { | |
panic!("Cannot perform `^` (bitxor) on ByteArrays of different length") | |
} else { | |
let res = lhs.iter() | |
.zip(rhs.iter()) | |
.map(|(x, y)| (x ^ y)) | |
.collect(); | |
ByteArray(res) | |
} | |
} | |
} | |
fn repeated_key_xor(bytes: &Vec<u8>, key: &Vec<u8>) -> Vec<u8> { | |
let key_length = key.len(); | |
let bytes_length = bytes.len(); | |
let key_repeat = bytes_length / key_length; | |
let key_remainder = bytes_length % key_length; | |
let mut repeated_key = Vec::with_capacity(bytes_length); | |
for _ in 0..key_repeat { | |
repeated_key.extend(key); | |
} | |
repeated_key.extend(&key[0..key_remainder]); | |
let ByteArray(result) = ByteArray(bytes.to_owned()) ^ ByteArray(repeated_key.to_owned()); | |
result | |
} | |
fn encrypt(message: String, key: String) { | |
let bytes = message.into_bytes(); | |
let kbytes = key.into_bytes(); | |
let encrypted_bytes = repeated_key_xor(&bytes, &kbytes); | |
println!("{}", hex::encode(encrypted_bytes)) | |
} | |
fn decrypt(cyphertext: String, key: String) { | |
match hex::decode(cyphertext) { | |
Ok(result) => { | |
let bytes = result; | |
let kbytes = key.into_bytes(); | |
let decrypted_bytes = repeated_key_xor(&bytes, &kbytes); | |
match str::from_utf8(&decrypted_bytes) { | |
Ok(result) => println!("{}", result), | |
Err(_) => println!("[Error]: Bad input. Decrypted message is not valid UTF8, try another key?") | |
} | |
}, | |
Err(_) => println!("[Error]: Bad input. Message to decrypt must be a valid hex string.") | |
} | |
} | |
fn parse_args(args: Vec<String>) -> Option<Configuration> { | |
let mut config = Configuration {..Default::default()}; | |
// skipping first arg, the initial ./cypher command | |
let mut args_iterator = args.iter().skip(1); | |
config.mode = match args_iterator.next().map(String::as_ref) { | |
Some("encrypt") => Mode::Encrypt, | |
Some("decrypt") => Mode::Decrypt, | |
Some("help") => Mode::Help, | |
Some(_) | None => { | |
println!("First argument must be one of {{encrypt|decrypt|help}}"); | |
return None | |
} | |
}; | |
while let Some(arg) = args_iterator.next() { | |
match arg.as_ref() { | |
"--key" => config.key = args_iterator.next().map(String::to_owned), | |
"--message" => config.message = args_iterator.next().map(String::to_owned), | |
_ => continue | |
} | |
}; | |
Some(config) | |
} | |
fn run_cypher(config: Configuration, command: fn(message: String, key: String)) { | |
if let (Some(message), Some(key)) = (config.message, config.key) { | |
command(message, key); | |
} else { | |
println!("Missing options, make sure both --key and --message are present."); | |
print_usage(); | |
} | |
} | |
fn print_usage() { | |
println!("usage: cypher {{encrypt|decrypt|help}} --key <key> --message <message>") | |
} | |
fn main() { | |
let args: Vec<String> = env::args().collect(); | |
match parse_args(args) { | |
Some(config) => { | |
match config.mode { | |
Mode::Help => print_usage(), | |
Mode::Encrypt => run_cypher(config, encrypt), | |
Mode::Decrypt => run_cypher(config, decrypt) | |
} | |
}, | |
None => print_usage() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment