use rand::seq::SliceRandom; use std::path::{Path}; use std::fs::File; use std::io; use std::io::{BufRead, BufReader}; use clap::{Arg,App}; use regex::Regex; fn no_word_file() -> io::Result { // Fall back to this if we cant find the word file let chars: Vec = "abcdefghijklmnopqrstuvwxyzABCDEFGIJKLMNOPQRSTUVWXYZ0123456789!@$%^&*()-+".chars().collect(); let mut password: String = String::new(); for _x in 0..63 { let random_char = chars.choose(&mut rand::thread_rng()); password.push_str(&random_char.unwrap().to_string()); } Ok(password) } fn character_weave(passphrase: String) -> io::Result { /* * a - @ * i - ! * o - 0 * s - $ * e - 3 */ let mut new_passphrase: String = String::new(); for c in passphrase.chars() { // Probably a better way to do this as opposed to a giant if / elsif if c.to_string() == "a" { new_passphrase.push_str(&"@".to_string()); } else if c.to_string() == "i" { new_passphrase.push_str(&"!".to_string()); } else if c.to_string() == "o" { new_passphrase.push_str(&"0".to_string()); } else if c.to_string() == "s" { new_passphrase.push_str(&"$".to_string()); } else if c.to_string() == "e" { new_passphrase.push_str(&"3".to_string()); } else { new_passphrase.push_str(&c.to_string()); } } Ok(new_passphrase) } // This is stupid TODO fn print_password(password: String) { println!("{}",password); } fn read_phrase_file(word_file: String) -> io::Result> { if ! Path::new(&word_file).exists() { println!("Could not find {}, generating a password and exiting", word_file); print_password(no_word_file().unwrap()); std::process::exit(1); } let words_fh = File::open(word_file)?; let mut words = vec![]; let re = Regex::new(r"^([0-9]{5})\s(.*)$").unwrap(); for line in BufReader::new(words_fh).lines() { for re_capture in re.captures_iter(&line.unwrap()) { // println!("Capture 1: {}, Capture 2: {}", &re_capture[1], &re_capture[2]); // Only push word for now, need to figure out multidimentional arrays words.push(re_capture[2].to_string()); } } Ok(words) } fn main() { // This variable is used as the default word file path, see Arg::with_name("words_file") // This is probably wrong/bad, and limits the functionality to Linux only let mut default_words_path = dirs::home_dir().unwrap().to_str().unwrap().to_string(); default_words_path.push_str(&"/.local/bin/eff_large_wordlist.txt".to_string()); let args = App::new("swmkp") .version("0.1") .about("My pw gen. Based on EFF passphrase guidelines\nhttps://www.eff.org/dice") .arg(Arg::with_name("words-file") .short("f") .long("words-file") .required(true) .takes_value(true) .default_value(&default_words_path) .help("Path to passphrase file, expects file provided by EFF\nSee:\nhttps://www.eff.org/files/2016/07/18/eff_large_wordlist.txt\nhttps://eff.org/files/2016/09/08/eff_short_wordlist_1.txt\nhttps://eff.org/files/2016/09/08/eff_short_wordlist_2_0.txt\n")) .arg(Arg::with_name("length") .short("l") .long("length") .required(false) .takes_value(true) .default_value("5") .help("How many words to use")) .arg(Arg::with_name("delimiter") .short("d") .long("delimiter") .required(false) .takes_value(true) .default_value("-") .help("What word delimiter to use")) .arg(Arg::with_name("with-characters") .short("w") .long("with-characters") .required(false) .takes_value(false) .help("Replaces some letters with special characters")) .arg(Arg::with_name("password") .short("p") .long("password") .required(false) .takes_value(false) .help("Generate a 64 character password, as opposed to a passphrase\nFallback here if no passphrase file provided/found")) .get_matches(); if args.is_present("password") { print_password(no_word_file().unwrap()); std::process::exit(0); } let roll_count: usize = args.value_of("length").unwrap().parse().unwrap(); let delim: String = args.value_of("delimiter").unwrap().to_string(); let words = read_phrase_file(args.value_of("words-file").unwrap().to_string()).unwrap(); let mut passphrase: String = String::new(); for x in 0..roll_count { let random_word = words.choose(&mut rand::thread_rng()); if x == ( roll_count - 1 ) { passphrase.push_str(random_word.unwrap()); } else { passphrase.push_str(random_word.unwrap()); passphrase.push_str(&delim); } } if args.is_present("with-characters") { let new_passphrase = character_weave(passphrase).unwrap(); println!("{}",new_passphrase); } else { println!("{}",passphrase); } }