Skip to content

Instantly share code, notes, and snippets.

@clevinson
Created July 9, 2019 16:41
Show Gist options
  • Save clevinson/6d28d89482e526ca5fd43e43a2a94da8 to your computer and use it in GitHub Desktop.
Save clevinson/6d28d89482e526ca5fd43e43a2a94da8 to your computer and use it in GitHub Desktop.
[package]
name = "cypher"
version = "0.1.0"
authors = ["Cory Levinson <cjlevinson@gmail.com>"]
edition = "2018"
[dependencies]
hex = "0.3.2"
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