|
@@ -0,0 +1,131 @@
|
|
|
+/* pwgen
|
|
|
+
|
|
|
+ generate passwords in useful ways
|
|
|
+
|
|
|
+usage: pwgen -c [number of phrases, default=1]
|
|
|
+ pwgen [len default=20] [number of phrases, default=1] //no conjunctions
|
|
|
+
|
|
|
+*/
|
|
|
+use std::env;
|
|
|
+use std::fs::File;
|
|
|
+use std::io::{BufReader, BufRead};
|
|
|
+use rand::Rng;
|
|
|
+
|
|
|
+#[derive(Debug)]
|
|
|
+struct Settings {
|
|
|
+ target_len: usize,
|
|
|
+ target_len_set: bool,
|
|
|
+ use_conjunction: bool,
|
|
|
+ number: u16,
|
|
|
+ help: bool,
|
|
|
+ pad_chars: String,
|
|
|
+ // debug: bool,
|
|
|
+}
|
|
|
+
|
|
|
+const CONJUNCTIONS: &[&str] = &["and", "and a", "and the","or",
|
|
|
+ "or the", "or a", "with a", "with", "with the", "by a", "by the",
|
|
|
+ "on a", "on the","on", "in a", "in the", "in",
|
|
|
+ "for", "for a", "for the", "but", "so"];
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+fn main() -> std::io::Result<()> {
|
|
|
+ let mut set = Settings {
|
|
|
+ number: 1,
|
|
|
+ target_len: 20, target_len_set: false,
|
|
|
+ use_conjunction: false,
|
|
|
+ // debug: false,
|
|
|
+ help: false,
|
|
|
+ // owasp.org/www-community/password-special-characters
|
|
|
+ pad_chars: " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".to_string()
|
|
|
+ // princeton IT
|
|
|
+ //pad_chars: " ~`!@#$%^&*()-_+={}[]|\\;:\"<>,./?".to_string()
|
|
|
+ };
|
|
|
+
|
|
|
+ let args: Vec<String> = env::args().collect();
|
|
|
+ parse_args(&args,&mut set);
|
|
|
+ if set.help == true {
|
|
|
+ usage();
|
|
|
+ return Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ let file = File::open("/usr/share/dict/words")
|
|
|
+ .expect("Dictionary file /usr/share/dict/words not found");
|
|
|
+ let buf = BufReader::new(file);
|
|
|
+ let words: Vec<String> = buf.lines().map(|x| x.expect("bad line")).collect();
|
|
|
+ let num_words = words.len();
|
|
|
+
|
|
|
+ let mut rng = rand::thread_rng();
|
|
|
+
|
|
|
+ for _i in 0 .. set.number {
|
|
|
+ let w0 = &words[rng.gen_range(0 .. num_words)];
|
|
|
+ let w1 = &words[rng.gen_range(0 .. num_words)];
|
|
|
+ let w2 = &words[rng.gen_range(0 .. num_words)];
|
|
|
+ let w3 = &words[rng.gen_range(0 .. num_words)];
|
|
|
+
|
|
|
+ let padding = set.pad_chars.chars()
|
|
|
+ .nth(rng.gen_range(0 .. set.pad_chars.len()))
|
|
|
+ .unwrap()
|
|
|
+ .to_string();
|
|
|
+
|
|
|
+ if set.use_conjunction {
|
|
|
+ let mut conj = CONJUNCTIONS[rng.gen_range(0 .. CONJUNCTIONS.len())].to_string();
|
|
|
+ if conj.ends_with(" a") && w1.starts_with(|x| "aAeEiIoOuU".find(x).is_some()) {
|
|
|
+ conj.push('n');
|
|
|
+ }
|
|
|
+ println!("{} {} {}", w0, conj, w1);
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ let mut pw = format!("{}{}{}",w0,padding,w1);
|
|
|
+ if pw.len() < set.target_len {
|
|
|
+ pw = format!("{}{}{}",pw,padding,w2);
|
|
|
+ if pw.len() < set.target_len {
|
|
|
+ pw = format!("{}{}{}",pw,padding,w3);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ println!("{}",pw);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Ok(())
|
|
|
+}
|
|
|
+
|
|
|
+fn usage() {
|
|
|
+ println!("pygen v1.0 generate passphrase; inspired by xkcd/936
|
|
|
+Usage: pygen [-p | -P | -n | -N] [-L <n> | --length <n>] [num_phrases]
|
|
|
+ pygen -c [num_phrases]
|
|
|
+ pygen -h | --help
|
|
|
+ -p pad with only spaces (for smartphone)
|
|
|
+ -P pad with spaces, period, comma (for smartphone)
|
|
|
+ -n pad with numbers
|
|
|
+ -N pad with numbers, spaces, periods, comma
|
|
|
+ -L --length add extra filler to reach length (default=20)
|
|
|
+ -c make 2 word phrases with a conjunction filler
|
|
|
+ num_phrases make multiple passphrases (default=1)");
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+fn parse_args(a:&Vec<String>, s:&mut Settings) {
|
|
|
+ // dbg!(a);
|
|
|
+ for item in &a[1 .. ] {
|
|
|
+ if s.target_len_set {
|
|
|
+ s.target_len_set = true;
|
|
|
+ s.target_len = item.parse::<usize>().expect("length must be a positive integer");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ match (item.as_str(),item.parse::<u16>()) {
|
|
|
+ ("-p", ..) => s.pad_chars = " ".to_string(),
|
|
|
+ ("-P", ..) => s.pad_chars = " ,.".to_string(),
|
|
|
+ ("-n", ..) => s.pad_chars = "0123456789".to_string(),
|
|
|
+ ("-N", ..) => s.pad_chars = " ,.0123456789".to_string(),
|
|
|
+ ("-L", ..) | ("--length", ..) => s.target_len_set = true,
|
|
|
+ ("-c", ..) => s.use_conjunction = true,
|
|
|
+ ("-h", ..) | ("--help", ..) => s.help = true,
|
|
|
+ (.., Ok(x)) => s.number = x,
|
|
|
+ _ => println!("invalid option: {}", item),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //dbg!(s);
|
|
|
+}
|