Ver Fonte

added a first README

Pat Beirne há 7 meses atrás
pai
commit
6abbabddc1
3 ficheiros alterados com 95 adições e 53 exclusões
  1. 32 0
      README.md
  2. 6 0
      pwgen/Cargo.toml
  3. 57 53
      pwgen/src/main.rs

+ 32 - 0
README.md

@@ -0,0 +1,32 @@
+# pwgen
+
+Generate word-based passwords. Based on XKCD/936, word-based passwords are easier to remember than a string of random letters/numbers/punctuation.
+
+[TOC]
+
+### Overview
+
+Password generators can be found all over the internet, but most generate random combinations of letters+numbers+punctuation. If a combination of those is long enough to be secure, it is also long enough to be difficult to remember, and possible difficult to type.
+
+The XKCD comic #936 introduced the idea that a phrase of 3, 4 or 5 common words can be used as a password that is sufficiently difficult to crack in our lifetimes. The program **pwgen** is a locally hosted generator of these kinds of pass phrases.
+
+The program runs locally on a UNIX/Linux based computer, and the passphrase never crosses the network, so you can be sure that you're the only one who has ever seen it.
+
+### Usage
+
+
+
+
+
+
+#### Other implementations
+
+http://www.egansoft.com/password/index.php
+
+https://passwords-generator.org/words
+
+https://passwordcreator.org/commonwords.html
+
+https://mdigi.tools/memorable-password/
+
+https://www.mapletech.co.uk/tools/password-generator/ 

+ 6 - 0
pwgen/Cargo.toml

@@ -7,3 +7,9 @@ edition = "2021"
 
 
 [dependencies]
 [dependencies]
 rand = "0.8.4"
 rand = "0.8.4"
+
+[profile.release]
+strip = true
+opt-level = "z"
+lto = true
+

+ 57 - 53
pwgen/src/main.rs

@@ -7,6 +7,7 @@ usage: pwgen -c [number of phrases, default=1]
     
     
 */
 */
 use std::env;
 use std::env;
+use std::cmp;
 use std::fs::File;
 use std::fs::File;
 use std::io::{BufReader, BufRead};
 use std::io::{BufReader, BufRead};
 use rand::Rng;
 use rand::Rng;
@@ -14,7 +15,7 @@ use rand::Rng;
 #[derive(Debug)]
 #[derive(Debug)]
 struct Settings {
 struct Settings {
   target_len: usize,
   target_len: usize,
-  target_len_set: bool,
+  collecting_parameter: bool,
   use_conjunction: bool,
   use_conjunction: bool,
   number: u16,
   number: u16,
   help: bool,
   help: bool,
@@ -23,23 +24,21 @@ struct Settings {
 }
 }
 
 
 const CONJUNCTIONS: &[&str] = &["and", "and a", "and the","or", 
 const CONJUNCTIONS: &[&str] = &["and", "and a", "and the","or", 
-	"or the", "or a", "with a", "with", "with the", "by a", "by the",
+  "or the", "or a", "with a", "with", "with the", "by a", "by the",
    "on a", "on the","on", "in a", "in the", "in",
    "on a", "on the","on", "in a", "in the", "in",
    "for", "for a", "for the", "but", "so"];
    "for", "for a", "for the", "but", "so"];
 
 
-
-
 fn main() -> std::io::Result<()> {
 fn main() -> std::io::Result<()> {
   let mut set = Settings {
   let mut set = Settings {
     number: 1, 
     number: 1, 
-    target_len: 20, target_len_set: false, 
+    target_len: 20, 
+    collecting_parameter: false, 
     use_conjunction: false, 
     use_conjunction: false, 
-    // debug: false,
     help: false, 
     help: false, 
-		// owasp.org/www-community/password-special-characters
+      // owasp.org/www-community/password-special-characters
     pad_chars: " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".to_string() 
     pad_chars: " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".to_string() 
-		// princeton IT
-	 //pad_chars: " ~`!@#$%^&*()-_+={}[]|\\;:\"<>,./?".to_string()
+      // princeton IT
+    //pad_chars: " ~`!@#$%^&*()-_+={}[]|\\;:\"<>,./?".to_string()
   };
   };
 
 
   let args: Vec<String> = env::args().collect();
   let args: Vec<String> = env::args().collect();
@@ -48,50 +47,55 @@ fn main() -> std::io::Result<()> {
     usage();
     usage();
     return Ok(())
     return Ok(())
   }
   }
-  
+    
   let file = File::open("/usr/share/dict/words")
   let file = File::open("/usr/share/dict/words")
-		.expect("Dictionary file /usr/share/dict/words not found");
+    .expect("Dictionary file /usr/share/dict/words not found");
   let buf = BufReader::new(file);
   let buf = BufReader::new(file);
-  let words: Vec<String> = buf.lines().map(|x| x.expect("bad line")).collect();
+  let words: Vec<String> = buf.lines()
+    .map(|x| x.expect("bad line"))
+    .filter(|x| !x.ends_with("'s"))
+    .collect();
   let num_words = words.len();
   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);
-		}
+      
+  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 mut words_len = w0.len() + w1.len();
+      
+    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".contains(x)) {
+        conj.push('n');
+      }
+      println!("{} {} {}", w0, conj, w1);
+    } else {
+      let pw;
+      let r:usize;
+      // if 2 words is enough
+      if words_len+3 >= set.target_len {
+        r = cmp::max(set.target_len, words_len+1) - words_len;
+        pw = format!("{}{}{}",w0,padding.repeat(r),w1); 
+      } else {
+          // we need 3 words
+          words_len = words_len + w2.len();
+          r = cmp::max(set.target_len, words_len+2) - words_len;
+          pw = format!("{}{}{}{}{}",w0,padding.repeat(r/2),w1,padding.repeat(r-r/2),w2);
+      }
+      println!("{}",pw);
+    }
   }
   }
   Ok(())
   Ok(())
 }
 }
 
 
 fn usage() {
 fn usage() {
-  println!("pygen    v1.0    generate passphrase; inspired by xkcd/936
+  println!("pygen    v1.1    generate passphrase; inspired by xkcd/936
 Usage: pygen [-p | -P | -n | -N] [-L <n> | --length <n>] [num_phrases]
 Usage: pygen [-p | -P | -n | -N] [-L <n> | --length <n>] [num_phrases]
        pygen -c [num_phrases]
        pygen -c [num_phrases]
        pygen -h | --help
        pygen -h | --help
@@ -109,23 +113,23 @@ Usage: pygen [-p | -P | -n | -N] [-L <n> | --length <n>] [num_phrases]
 fn parse_args(a:&Vec<String>, s:&mut Settings) {
 fn parse_args(a:&Vec<String>, s:&mut Settings) {
   // dbg!(a);
   // dbg!(a);
   for item in &a[1 .. ] {
   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>()) {
+    if s.collecting_parameter == false {
+      match (item.as_str(),item.parse::<u16>()) {
       ("-p", ..) => s.pad_chars = " ".to_string(),
       ("-p", ..) => s.pad_chars = " ".to_string(),
       ("-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(),
       ("-N", ..) => s.pad_chars = " ,.0123456789".to_string(),
       ("-N", ..) => s.pad_chars = " ,.0123456789".to_string(),
-      ("-L", ..) | ("--length", ..) => s.target_len_set = true, 
+      ("-L", ..) | ("--length", ..) => s.collecting_parameter = true, 
       ("-c", ..) => s.use_conjunction = true,
       ("-c", ..) => s.use_conjunction = true,
       ("-h", ..) | ("--help", ..) => s.help = true,
       ("-h", ..) | ("--help", ..) => s.help = true,
       (.., Ok(x)) => s.number = x,
       (.., Ok(x)) => s.number = x,
       _ => println!("invalid option: {}", item),
       _ => println!("invalid option: {}", item),
+      }
+    } else {
+      s.collecting_parameter = false;
+      s.target_len = item.parse::<usize>().expect("length must be a positive integer");
     }
     }
   }
   }
-  //dbg!(s);
+  // dbg!(s);
 }
 }