Rewrite: random password script
This commit is contained in:
parent
816e1a3f2d
commit
74e0adb24c
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -7,6 +7,7 @@ aerc/accounts.conf
|
|||
|
||||
# Bin
|
||||
bin/sway_tree
|
||||
bin/.gitignore
|
||||
|
||||
# Mpv
|
||||
mpv/watch_later
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
print_help() {
|
||||
cat <<HELP
|
||||
Generate a random password from /dev/urandom, with characters from /[A-z0-9;-]/
|
||||
|
||||
Usage: $(basename "$0") <output-length>
|
||||
|
||||
Examples:
|
||||
$(basename "$0") 20 | wl-copy
|
||||
$(basename "$0") 400 > key_file
|
||||
|
||||
Special characters: /[;-]/
|
||||
|
||||
Length must be at least $MIN_PASSWORD_LENGTH. Passwords are generated until one of each a
|
||||
lowercase, uppercase, digit, and special character are in the password.
|
||||
Both special characters are required for passwords >=$LONG_PASSWORD_LENGTH characters long.
|
||||
Really long passwords have at least 2 of each special character.
|
||||
HELP
|
||||
}
|
||||
|
||||
shopt -s lastpipe
|
||||
declare -ri LONG_PASSWORD_LENGTH=15 MIN_PASSWORD_LENGTH=6
|
||||
declare -gri LENGTH="$1"
|
||||
declare password=""
|
||||
|
||||
generate_password() {
|
||||
dd if=/dev/urandom bs=3 count="$(( 10 * LENGTH ))" 2>/dev/null \
|
||||
| base64 -w0 \
|
||||
| tr '/' '-' \
|
||||
| tr '+' ';' \
|
||||
| cut -c "1-$LENGTH" \
|
||||
| read -r password
|
||||
}
|
||||
|
||||
contains_characterset() {
|
||||
local p="$password"
|
||||
if [[ $LENGTH -ge $(( 2*LONG_PASSWORD_LENGTH )) ]]; then
|
||||
[[ $p =~ [A-Z] && $p =~ [a-z] && $p =~ [0-9] && $p =~ \;.+\; && $p =~ -.+- ]]
|
||||
elif [[ $LENGTH -ge $LONG_PASSWORD_LENGTH ]]; then
|
||||
[[ $p =~ [A-Z] && $p =~ [a-z] && $p =~ [0-9] && $p =~ \; && $p =~ - ]]
|
||||
else
|
||||
[[ $p =~ [A-Z] && $p =~ [a-z] && $p =~ [0-9] ]] && [[ $p =~ \; || $p =~ - ]]
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "$1" =~ ^[0-9]+$ && $1 -ge $MIN_PASSWORD_LENGTH ]]; then
|
||||
while ! contains_characterset; do
|
||||
generate_password "$1"
|
||||
done
|
||||
|
||||
echo "$password"
|
||||
else
|
||||
print_help
|
||||
exit 1
|
||||
fi
|
|
@ -18,8 +18,13 @@ path = "src/sway_tree.rs"
|
|||
name = "rename_for_unix"
|
||||
path = "src/rename_for_unix.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "random_password"
|
||||
path = "src/random_password.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
chrono = "0.4"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
rand = "0.8"
|
||||
regex = "1"
|
||||
termion = "2"
|
||||
|
|
116
bin/rewritten_in_rust/src/random_password.rs
Normal file
116
bin/rewritten_in_rust/src/random_password.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
use clap::Parser;
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
use std::io::Write;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "random_password", author, version = "0.1.0")]
|
||||
/// Generates a random password, that's likely to work for most signups
|
||||
struct Args {
|
||||
/// Copy to clipboard instead of printing to stdout
|
||||
#[arg(short = 'c', long = "clipboard")]
|
||||
is_clipboard: bool,
|
||||
/// Length of the generated password
|
||||
#[arg(value_name = "LENGTH")]
|
||||
pass_length: u8,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CharDistro {
|
||||
uppercase: usize,
|
||||
lowercase: usize,
|
||||
numerical: usize,
|
||||
special: usize,
|
||||
}
|
||||
|
||||
impl CharDistro {
|
||||
pub fn from(s: &str) -> Self {
|
||||
let mut d = Self::default();
|
||||
|
||||
for c in s.chars() {
|
||||
if c.is_uppercase() {
|
||||
d.uppercase += 1;
|
||||
} else if c.is_lowercase() {
|
||||
d.lowercase += 1;
|
||||
} else if c.is_digit(10) {
|
||||
d.numerical += 1;
|
||||
} else {
|
||||
d.special += 1;
|
||||
}
|
||||
}
|
||||
|
||||
d
|
||||
}
|
||||
|
||||
pub fn all_nonzero(&self) -> bool {
|
||||
self.uppercase > 0 && self.lowercase > 0 && self.numerical > 0 && self.special > 0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
let mut pass;
|
||||
let mut trimmed;
|
||||
|
||||
let mut key = [0_u8; 256];
|
||||
|
||||
loop {
|
||||
OsRng.fill_bytes(&mut key);
|
||||
pass = encode_to_password(&key);
|
||||
trimmed = &pass[..args.pass_length as usize];
|
||||
|
||||
let distro = CharDistro::from(trimmed);
|
||||
let is_enough_special = distro.special >= (args.pass_length / 10 + 1) as usize;
|
||||
|
||||
if distro.all_nonzero() && is_enough_special && has_two_special(trimmed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if args.is_clipboard {
|
||||
let pbcopy = Command::new("wl-copy")
|
||||
.stdin(Stdio::piped())
|
||||
.spawn()
|
||||
.expect("Failed to start wl-copy");
|
||||
|
||||
pbcopy.stdin.unwrap().write(trimmed.as_bytes()).unwrap();
|
||||
} else {
|
||||
println!("{}", trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_to_password(key: &[u8; 256]) -> String {
|
||||
let mut pass = String::new();
|
||||
|
||||
for bits in key {
|
||||
let c = bits & 0o77;
|
||||
|
||||
if c <= 25 {
|
||||
pass.push((c + 65) as char);
|
||||
} else if c <= 51 {
|
||||
pass.push((c + 97 - 26) as char);
|
||||
} else if c <= 61 {
|
||||
pass.push((c + 48 - 52) as char);
|
||||
} else if c == 62 {
|
||||
pass.push('-');
|
||||
} else {
|
||||
pass.push(';');
|
||||
}
|
||||
}
|
||||
|
||||
pass
|
||||
}
|
||||
|
||||
fn has_two_special(s: &str) -> bool {
|
||||
let mut special_1 = 'a';
|
||||
|
||||
for c in s.chars() {
|
||||
if !c.is_ascii_alphanumeric() && special_1 == 'a'{
|
||||
special_1 = c;
|
||||
} else if !c.is_ascii_alphanumeric() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
Loading…
Reference in a new issue