aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/bin/makepass.sh2
-rwxr-xr-xbin/bin/makepass.zsh185
2 files changed, 119 insertions, 68 deletions
diff --git a/bin/bin/makepass.sh b/bin/bin/makepass.sh
index 78e3b75..7db96fd 100755
--- a/bin/bin/makepass.sh
+++ b/bin/bin/makepass.sh
@@ -105,6 +105,6 @@ _random() (
return 0
)
-makepass "${@:-}" && return 0
+makepass "${@:-}"
## END OF FILE #################################################################
diff --git a/bin/bin/makepass.zsh b/bin/bin/makepass.zsh
index 0d56c6a..319a200 100755
--- a/bin/bin/makepass.zsh
+++ b/bin/bin/makepass.zsh
@@ -9,17 +9,10 @@ emulate zsh
# This file takes randomness from /dev/urandom and turns it into random
# passwords.
################################################################################
-#
-# This script relies on the following external commands, in addition to zsh:
-# `head`, `tail`, and `tr`. All three are used in a way which is defined in
-# POSIX.1-2017. This script should therefore work on *most* systems with zsh.
-#
-# head - https://pubs.opengroup.org/onlinepubs/9699919799/utilities/head.html
-# tail - https://pubs.opengroup.org/onlinepubs/9699919799/utilities/tail.html
-# tr - https://pubs.opengroup.org/onlinepubs/9699919799/utilities/tr.html
-#
# Copyright (c) 2018-2022 Dennis Eriksen • d@ennis.no
+
+# Help-function
function _makepass_help() {
local string='NAME
makepass - create several random passwords
@@ -34,8 +27,9 @@ SYNOPSIS
- Normal passwords - 10 random strings with letters (both lower and upper
case), numbers, and dashes and underscores.
- - Passwords with special characters - six random strings from the following
- range: !#$%&/()=?+-_,.;:<>[]{}|\@*^A-Z-a-z-0-9
+ - Passwords with special characters - six random strings generated from
+ lower and upper case letters, numbers, and the following characters:
+ !#$%&/()=?+-_,.;:<>[]{}|\@*
- Passphrases - if we find a dictionary, five series of eight random words
from the dictionary, separated by dashes. The number of words can not be
@@ -57,23 +51,27 @@ ENVIRONMENT
argument, that NUM overrides this variable.
MAKEPASS_NORMAL
- Specifies the array of characters from which to generate "normal"
- passwords. See below for valid values. Defaults to: _A-Z-a-z-0-9
+ String of characters from which to generate "normal" passwords.
+ Defaults to:
+ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_
MAKEPASS_SPECIAL
- Specifies the array of characters from which to generate passwords with
- special characters. See below for valid values. Defaults to:
- !#$%&/()=?+-_,.;:<>[]{}|\@*^A-Z-a-z-0-9
+ 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.
-ABOUT CHARACTER ARRAYS
- makepass uses `tr` to translate and/or delete characters. Therefore, values
- that would be valid for `tr` is valid for makepass. This includes
- character-classes like [:alnum:] and [:print:].
+NOTES
+ This scripts makes use of $RANDOM - a builtin in zsh which produces a
+ pseudo-random integer between 0 and 32767, newly generated each time the
+ parameter is referenced. We initially seed the random number generator with
+ a random 32bit integer generated from /dev/urandom. This should provide
+ enough randomnes to generate sufficiently secure passwords.
AUTHOR
Dennis Eriksen <https://dnns.no>'
@@ -82,15 +80,77 @@ AUTHOR
return 0
}
+
+
+# Create random passphrase
+function randphrase() {
+ setopt localoptions rematch_pcre
+ local -i len=${1:-8}
+ local prestring string
+
+ # Put together $len random words, separated by '-'
+ repeat $len prestring+=$words[$((RANDOM % $#words + 1))]'-'
+ prestring=$prestring[1,-2] # remove trailing dash
+
+ # This while-loop removes any characters NOT in '[^0-9a-zA-Z_-]'
+ while [[ -n $prestring ]]; do
+ if [[ $prestring =~ '[^0-9a-zA-Z_-]' ]]; then
+ string+=${prestring[1,MBEGIN-1]}
+ prestring=${prestring[MEND+1,-1]}
+ else
+ break
+ fi
+ done
+ string+=$prestring # append the rest of $prestring
+
+ printf '%s\n' $string; return
+}
+
+
+
+# Function to create random strings
+function randstring() {
+ # Default is a number between 8 and 44
+ local -i len=$(( ${1:-0} > 0 ? ${1:-0} : RANDOM % (44 - 8 + 1) + 8))
+ local chars=${2:-$alnum}
+ local flc=${3:-} # first-last-character
+ local string
+ repeat $len string+=$chars[$((RANDOM % $#chars + 1))]
+
+ # If a third value is provided, it is used as the forst and last character in
+ # the string
+ if [[ -n $flc ]]; then
+ string=$flc[$((RANDOM % $#flc + 1))]$string[2,-2]
+ (( len >= 2 )) && string+=$flc[$((RANDOM % $#flc + 1))]
+ fi
+
+ printf '%s\n' "$string"; return
+}
+
+
+
# makepass-function. This is where the magic happens
function makepass() {
- MAKEPASS_DEFAULT_LENGTH=${MAKEPASS_DEFAULT_LENGTH:-random}
- MAKEPASS_NORMAL=${MAKEPASS_NORMAL:-'_A-Z-a-z-0-9'}
- MAKEPASS_SPECIAL=${MAKEPASS_SPECIAL:-'!#$%&/()=?+-_,.;:<>[]{}|\@*^A-Z-a-z-0-9'}
- MAKEPASS_WORDLIST=${MAKEPASS_WORDLIST:-/usr/share/dict/words}
- RANDOM=$(head -n 10 /dev/urandom | tr -cd '[:digit:]' | tail -c 10) # seed RANDOM
+ setopt localoptions
+ local lower='abcdefghijklmnopqrstuvwxyz'
+ local upper='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ local digit='0123456789'
+ local other='!#$%&/()=?+-_,.;:<>[]{}|\@*'
+ local alpha=${lower}${upper}
+ local alnum=${alpha}${digit}
+ local every=${alnum}${other}
+
+ local length=${MAKEPASS_DEFAULT_LENGTH:-random}
+ local normal=${MAKEPASS_NORMAL:-$alnum'-_'}
+ local special=${MAKEPASS_SPECIAL:-$every}
+ local wordlist=${MAKEPASS_WORDLIST:-/usr/share/dict/words}
+
+ # Seed $RANDOM with a random 32bit integer from /dev/urandom
+ local r4 # will be filled with 4 random bytes from /dev/urandom
+ IFS= read -rk4 -u0 r4 < /dev/urandom || return
+ local b1=$r4[1] b2=$r4[2] b3=$r4[3] b4=$r4[4]
+ RANDOM=$(( #b1 << 24 | #b2 << 16 | #b3 << 8 | #b4 ))
- local -i len
# Some error-checking
# We only take one argument
@@ -98,64 +158,55 @@ function makepass() {
# if $1 == -h
[[ $1 == '-h' ]] && _makepass_help && return 0
# if $1 is not empty and is not a number
- [[ ! -z "$1" && ! $1 = <1-> ]] && print -u2 -- "not a number above 0\nmaybe try running \`makepass -h\` for help" && return 1
+ [[ -n $1 && ! $1 = <1-> ]] && print -u2 -- "not a number above 0\nmaybe try running \`makepass -h\` for help" && return 1
- [[ $MAKEPASS_DEFAULT_LENGTH == <0-> && -z $1 ]] && len=$MAKEPASS_DEFAULT_LENGTH || len=${1:-0}
+ # length of passwords
+ local -i len=$(( length > 0 && ! ${1:-0} > 0 ? length : ${1:-0} ))
# Normal passwords
print "Normal passwords:"
- print -c -- $(repeat 10 _random $len $MAKEPASS_NORMAL)
+ print -c -- $(repeat 10 { randstring $len $normal; : $RANDOM })
+ : $RANDOM
print
# Passowrds with special characters
print "Passwords with special characters:"
- print -c -- $(repeat 6 _random $len $MAKEPASS_SPECIAL true)
+ print -c -- $(repeat 6 { randstring $len $special $alpha; : $RANDOM })
+ : $RANDOM
# Passphrases - but only if a wordlist is available
- if [[ -r "$MAKEPASS_WORDLIST" ]]; then
+ if [[ -r $wordlist ]]; then
print
print "Passphrases:"
- local -a words=(${(f)"$(<"$MAKEPASS_WORDLIST")"})
-
- for i in {1..5}; do
- print "$RANDOM" > /dev/null # The values of RANDOM form an intentionally-repeatable pseudo-random sequence; subshells that reference RANDOM will result in identical pseudo-random values unless the value of RANDOM is referenced or seeded in the parent shell in between subshell invocations.
- print $(repeat 8 print "$words[RANDOM % $#words +1]") | tr ' ' '-' | tr -dc '_A-Z-a-z-0-9'; print
- done
+ local -a words=(${(f)"$(<"$wordlist")"})
+ repeat 6 randphrase
fi
}
-# Function to create random stuff
-function _random() {
- setopt local_options
- RANDOM=$(head -n 10 /dev/urandom | tr -cd "[:digit:]" | tail -c 8) # seed RANDOM
-
- # Default is a number between 8 and 44
- local -i len=$1
- (( len == 0 )) && len=$((RANDOM % (44 - 8 + 1) + 8))
-
- # Default to [:alnum:] if no chars are specified
- local chars=${2:-'[:alnum:]'}
+makepass "${@:-}"
- # First-Last-Alpha - if you want the first and the last letter to be from [:alpha:]
- local fla=${3:-'false'}
+# Last time I redid this script I benchmarked some ways to generate random
+# strings. Here's the results:
- local string
-
- if [[ $fla == true ]]; then
- if (( len <= 2 )); then
- string="$(head -n 10 /dev/urandom | tr -cd '[:alpha:]' | tail -c $len)"
- else
- string="$( head -n 10 /dev/urandom | tr -cd '[:alpha:]' | tail -c 1)"
- string+="$(head -n 100 /dev/urandom | tr -cd "$chars" | tail -c $((len-2)))"
- string+="$(head -n 10 /dev/urandom | tr -cd '[:alpha:]' | tail -c 1)"
- fi
- else
- string="$(head -n 100 /dev/urandom | tr -cd "$chars" | tail -c $len)"
- fi
-
- printf '%s\n' "$string"; return
-}
+# subshell with tr | fold | head
+# % time (repeat 10000 { string=''; string=$(tr -cd '[:alpha:]' </dev/urandom | fold -w 20 | head -n1 ) } )
+# 45.32s user 22.38s system 201% cpu 33.598 total
+#
+# subshell with head | tr | tail
+# % time (repeat 10000 { string=''; string=$(head -n100 /dev/urandom | tr -cd '[:alpha:]' | tail -c20 ) } )
+# 40.88s user 17.87s system 187% cpu 31.249 total
+#
+# subshell sysread | tr in brace-expansion instead of head/tail
+# % time (repeat 10000 { string=''; string=${${:-"$(sysread -i 1 </dev/urandom | tr -cd '[:alpha:]')"}[1,20]} } )
+# 24.13s user 11.38s system 120% cpu 29.442 total
+#
+# string-addition with randint32()-function to get random 32bit integer from /dev/urandom
+# % time (repeat 10000 { string='';repeat 20 string+=$alpha[$((randint32() % $#alpha + 1))] })
+# 8.17s user 3.28s system 99% cpu 11.475 total
+#
+# string-addition and $RANDOM
+# % time (repeat 10000 { string='';repeat 20 string+=$alpha[$((RANDOM % $#alpha + 1))] } )
+# 0.74s user 0.00s system 99% cpu 0.741 total
-makepass "${@:-}"
## END OF FILE #################################################################