aboutsummaryrefslogtreecommitdiffstats
path: root/rust/src/main.rs
diff options
context:
space:
mode:
authorDennis Eriksen <d@ennis.no>2023-11-10 09:54:17 +0100
committerDennis Eriksen <d@ennis.no>2023-11-10 09:54:17 +0100
commitcf84fad0955ff07dd6fe3ec30a17dc134e23c356 (patch)
tree6f85e6017e7a5feacd88270cb942e4e2e007ffcb /rust/src/main.rs
parentAdding a python-verion (diff)
downloadmakepass-cf84fad0955ff07dd6fe3ec30a17dc134e23c356.tar.gz
trying out new project structure
Diffstat (limited to 'rust/src/main.rs')
-rw-r--r--rust/src/main.rs393
1 files changed, 0 insertions, 393 deletions
diff --git a/rust/src/main.rs b/rust/src/main.rs
deleted file mode 100644
index 083a99f..0000000
--- a/rust/src/main.rs
+++ /dev/null
@@ -1,393 +0,0 @@
-//
-// Author : Dennis Eriksen <d@ennis.no>
-// File : main.rs
-// Bin : makepass.rs
-// Created : 2023-09-05
-// Licence : BSD-3-Clause
-//
-// Copyright (c) 2018-2023 Dennis Eriksen <d@ennis.no>
-
-//
-// Imports
-//
-use clap::{Arg, ArgAction, Command, crate_authors, value_parser};
-use rand::{seq::SliceRandom, Rng};
-use std::env;
-use std::fs;
-use std::io;
-use std::io::prelude::*;
-use std::process::exit;
-use terminal_size::{terminal_size, Width};
-
-//
-// Constants
-//
-const MAX: u8 = 255;
-const RANGE_MAX: u8 = 42;
-const RANGE_MIN: u8 = 8;
-const PASS_WORDS: u8 = 8;
-
-// Character sets
-const LOWER: &str = "abcdefghijklmnopqrstuvwxyz";
-const UPPER: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-const DIGIT: &str = "0123456789";
-const OTHER: &str = "!$%&#/()=?+-_,.;:<>[]{}|@*";
-const EXTRA: &str = "-_";
-
-// Defaults
-const DEFAULT_LENGTH: u8 = 0;
-const DEFAULT_NUMBER: u8 = 10;
-const DEFAULT_WORDLIST: &str = "/usr/share/dict/words";
-
-// Structure used to pass common data to the print-function
-struct CommonPrintData<'a> {
- length: u8,
- alpha: &'a [char],
- alnum: &'a [char],
- printbool: bool,
- printlen: u8,
- col_width: u8,
- col_num: u16,
-}
-
-//
-// Main function. This is where the magic happens
-//
-fn main() {
- // Construct arrays of the character sets
- let lower = LOWER.chars().collect::<Vec<_>>();
- let upper = UPPER.chars().collect::<Vec<_>>();
- let digit = DIGIT.chars().collect::<Vec<_>>();
- let other = OTHER.chars().collect::<Vec<_>>();
- let extra = EXTRA.chars().collect::<Vec<_>>();
- let alpha = [&lower[..], &upper[..]].concat();
- let alnum = [&alpha[..], &digit[..]].concat();
- let every = [&alnum[..], &other[..]].concat();
-
- // These are the sets we actually use
- let mut normal = [&alnum[..], &extra[..]].concat();
- let mut special = every;
-
- // Set the defaults
- let mut length = DEFAULT_LENGTH;
- let mut number = DEFAULT_NUMBER;
- let mut wordlist = DEFAULT_WORDLIST.to_string();
-
- //
- // env
- // Deal with env before cli-args, because cli-args take precedence.
- //
- let env = (
- env::var("MAKEPASS_LENGTH").unwrap_or_default(),
- env::var("MAKEPASS_NUMBER").unwrap_or_default(),
- env::var("MAKEPASS_WORDLIST").unwrap_or_default(),
- env::var("MAKEPASS_NORMAL").unwrap_or_default(),
- env::var("MAKEPASS_SPECIAL").unwrap_or_default(),
- );
-
- // length
- if !env.0.is_empty() {
- length = env.0.parse().unwrap_or_else(|_| {
- // length is a <u8>, which is 0-255, so no need for further error-checking
- eprintln!(
- "Error: MAKEPASS_LENGTH is not a valid number. Valid numbers are between 0 and 255"
- );
- exit(1);
- });
- }
- // number
- if !env.1.is_empty() {
- // number is a <u8>, but takes a minimum value of 1
- match env.1.parse::<u8>() {
- Ok(n) if n >= 1 => number = n,
- _ => {
- eprintln!("Error: MAKEPASS_NUMBER is not a valid number. Valid numbers are between 1 and 255");
- exit(1);
- }
- }
- }
- if !env.2.is_empty() { // wordlist
- wordlist = env.2;
- }
- if !env.3.is_empty() { // Overwrite normal character array if MAKEPASS_NORMAL is present
- normal = env.3.chars().collect::<Vec<_>>();
- }
- if !env.4.is_empty() { // Overwrite special character array if MAKEPASS_SPECIAL is present
- special = env.4.chars().collect::<Vec<_>>();
- }
-
- //
- // Args
- //
- let opts = cli().get_matches();
-
- // Set to new value if exists, or just set to old value
- length = *opts.get_one::<u8>("length_flag").unwrap_or(&length);
- number = *opts.get_one::<u8>("number").unwrap_or(&number);
- length = *opts.get_one::<u8>("length_arg").unwrap_or(&length);
- let printbool = opts.get_flag("printlen");
-
- //
- // Other logic
- //
- let printlen: u8 = if printbool && length < 100 {
- 2
- } else if printbool {
- 3
- } else {
- 0
- };
-
- // Get the width of the terminal
- let size = terminal_size();
- let term_width: u16 = match size {
- Some((Width(w), _)) => w,
- None => 1,
- };
-
- // Calculate how wide each column has to be
- let col_width: u8 = match length {
- 0 => RANGE_MAX + 2,
- _ => length + 2,
- };
-
- // Number of columns to print
- // If printbool is true, col_num is term_width / (col_width + printlen + 1)
- // Else, col_num is term_width / col_width
- // col_num is never 0. Use std::cmp::max to use the largest value of 1 and the above.
- let col_num: u16 = std::cmp::max(
- 1,
- match printbool {
- true => term_width / (col_width + printlen + 1) as u16,
- false => term_width / col_width as u16,
- },
- );
-
- // Fill in common print data
- let data = CommonPrintData {
- length: length,
- alpha: &alpha,
- alnum: &alnum,
- printbool: printbool,
- printlen: printlen,
- col_width: col_width,
- col_num: col_num,
- };
-
- //
- // Do the dirty!
- //
-
- // Normal passwords
- print_columns("Normal passwords", number, &normal, &data);
-
- println!("");
-
- // Special passwords
- print_columns(
- "Passwords with special characters",
- number / 3 * 2 + 1,
- &special,
- &data,
- );
-
- // Passphrases
- if fs::metadata(&wordlist).is_ok() {
- // Open the file
- let file = fs::File::open(&wordlist).unwrap();
-
- // Create a buffered reader to handle file contents
- let reader = io::BufReader::new(file);
-
- // Read words into a Vector
- let words = reader.lines().map(|l| l.unwrap()).collect();
-
- // Print
- println!("");
- println!("Passphrases:");
-
- for _ in 0..number / 2 {
- println!("{}", passphrase(&words));
- }
- }
-}
-
-//
-// Print passwords in neat columns
-//
-fn print_columns(title: &str, num: u8, chars: &Vec<char>, data: &CommonPrintData) {
- let mut strings: Vec<String> = Vec::new();
- for _ in 0..num {
- strings.push(randstring(data.length, chars, data.alpha, data.alnum));
- }
-
- println!("{}:", title);
-
- let mut i: u16 = 0;
- for s in &strings {
- i += 1;
-
- if data.printbool {
- print!("{1:00$} ", data.printlen as usize, s.len());
- }
- print!("{1:<0$}", data.col_width as usize, s);
- if i % data.col_num == 0 || (i == num as u16 && i % data.col_num > 0) {
- println!();
- }
- }
-}
-
-//
-// Generate random strings to use as passwords
-//
-fn randstring(len: u8, chars: &[char], alpha: &[char], alnum: &[char]) -> String {
- let mut rng = rand::thread_rng();
- let length = if len == 0 {
- rng.gen_range(RANGE_MIN..=RANGE_MAX)
- } else {
- len
- };
-
- let mut s = String::with_capacity(length as usize);
- for i in 0..length {
- let c = if i == 0 {
- alpha.choose(&mut rng)
- } else if i == length - 1 {
- alnum.choose(&mut rng)
- } else {
- chars.choose(&mut rng)
- };
- s.push(*c.unwrap());
- }
- s
-}
-
-//
-// Generate passphrases
-//
-fn passphrase(wordlist: &Vec<String>) -> String {
- let mut rng = rand::thread_rng();
- let mut passphrase = Vec::new();
-
- for _ in 0..PASS_WORDS {
- let word = wordlist.choose(&mut rng).unwrap().clone();
- passphrase.push(word);
- }
-
- passphrase.join("-")
-}
-
-//
-// Handle command line arguments
-//
-fn cli() -> Command {
- Command::new("makepass")
- .about("makepass - create random passwords")
- .author(crate_authors!())
- .arg(
- Arg::new("length_flag")
- .short('l')
- .value_name("LENGTH")
- .help(format!("Length of passwords (0..={0}). 0 = random.", MAX))
- .value_parser(value_parser!(u8).range(0..)),
- )
- .arg(
- Arg::new("number")
- .short('n')
- .value_name("NUM")
- .help(format!("Number of passwords (1..={0}). [default: 10]", MAX))
- .value_parser(value_parser!(u8).range(1..))
- .long_help("this is the long help. What is it?"),
- )
- .arg(
- Arg::new("printlen")
- .short('p')
- .help("Print length")
- .action(ArgAction::SetTrue),
- )
- .arg(
- Arg::new("length_arg")
- .value_name("LENGTH")
- .help(format!(
- "Length of passwords (0..={MAX}). 0 = random length."
- ))
- .value_parser(value_parser!(u8).range(0..)),
- )
- .override_help("NAME
- makepass - create several random passwords
-
-SYNOPSIS
- makepass [OPTIONS] [NUM]
-
- If a NUM is provided, passwords will be NUM characters long.
-
- By default `makepass` will output passwords from the three following classes:
-
- - Normal passwords - random strings with letters (both lower and upper
- case), numbers, and dashes and underscores.
-
- - Passwords with special characters - random strings generated from lower
- and upper case letters, numbers, and the following characters:
- !#$%&/()=?+-_,.;:<>[]{}|@*
-
- - Passphrases - if we find a dictionary, a series of eight random words
- from the dictionary, separated by dashes. The number of words can not be
- changed, but you do not have to use all of them. Use as mane as you want.
-
- The first character will always be alphabetic, and the last will always be
- alphanumeric.
-
-DESCRIPTION
- makepass has the following options:
-
- -h
- output this help-text
- -l
- length of passwords. See MAKEPASS_LENGTH below
- -n
- number of passwords. See MAKEPASS_NUMBER below
- -p
- print length of number
-
-ENVIRONMENT
- makepass examines the following environmental variables.
-
- MAKEPASS_LENGTH
- Specifies the length of passwords. Valid values are 0-255. If 0, a
- random value between 8 and 42 will be used for each password. -l
- overrides this environmental variable, and the argument NUM overrides
- that again. So `MAKEPASS_LENGTH=10 makepass -l 12 14` will give
- passwords that are 14 characters long, even though both -l and
- MAKEPASS_LENGTH also specifies a length.
-
- MAKEPASS_NUMBER
- The number of passwords to generate. This formula is used to determine
- how many passwords from each group should be generated:
- - (n) normal passwords
- - (n / 3 * 2 + 1) special passwords
- - (n / 2) passphrases
- Where n is 10 by default. Valid values for n are 1-255. Floating-poing
- math is not used, so results may vary.
-
- MAKEPASS_PRINTLEN
- If 1, print length of all passwords. If 0, don't.
-
- MAKEPASS_NORMAL
- String of characters from which to generate \"normal\" passwords.
- Defaults to:
- abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_
-
- MAKEPASS_SPECIAL
- String of characters from which to generate passwords with special
- characters. Defaults to the same characters as in MAKEPASS_NORMAL, plus
- these:
- !#$%&/()=?+-_,.;:<>[]{}|@*
-
- MAKEPASS_WORDLIST
- Specifies the dictionary we find words for passphrases in. If this is
- unset or empty, we try \"/usr/share/dict/words\". If that file does not
- exist, no passphrases will be provided.
-
-AUTHOR
- Dennis Eriksen <https://dnns.no>")
-}