aboutsummaryrefslogtreecommitdiffstats
path: root/makepass.bash
diff options
context:
space:
mode:
authorDennis Eriksen <d@ennis.no>2023-09-07 22:08:01 +0200
committerDennis Eriksen <d@ennis.no>2023-09-07 22:08:01 +0200
commit1db76b659c79e4da1e21f200b76a80f55b7e4777 (patch)
tree233de96f2fa9dd8bf5686c93fb076b897c73c8c5 /makepass.bash
parentRedid the perl-version (diff)
downloadmakepass-1db76b659c79e4da1e21f200b76a80f55b7e4777.tar.gz
Redid makepass.bash
Without any polish... But makepass.bash is now about 5.5x faster than previously!
Diffstat (limited to 'makepass.bash')
-rwxr-xr-xmakepass.bash274
1 files changed, 243 insertions, 31 deletions
diff --git a/makepass.bash b/makepass.bash
index c2edf82..16dffa9 100755
--- a/makepass.bash
+++ b/makepass.bash
@@ -5,52 +5,264 @@
# Bug-Reports: Email <git@dnns.no>
# License: This file is licensed under the BSD 3-Clause license.
################################################################################
-# This file takes randomness from /dev/urandom and turns it into random
-# passwords.
+# This script generates random passwords.
################################################################################
+# Copyright (c) 2018-2023 Dennis Eriksen • d@ennis.no
+MAX=255 # max length of passwords
+RANGE_MAX=42 # max length when using random length
+RANGE_MIN=8 # min length when using random length
+PASS_WORDS=8 # number of words in passphrases
-# Copyright (c) 2018-2023 Dennis Eriksen • d@ennis.no
+LOWER='abcdefghijklmnopqrstuvwxyz'
+UPPER='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+DIGIT='0123456789'
+OTHER='!#$%&/()=?+-_,.;:<>[]{}|@*'
+ALPHA=${LOWER}${UPPER}
+ALNUM=${ALPHA}${DIGIT}
+EVERY=${ALNUM}${OTHER}
+
+LENGTH=${MAKEPASS_LENGTH:-0} # length of passwords. 0 means "random number between RANGE_MIN and RANGE_MAX"
+NUMBER=${MAKEPASS_NUMBER:-10} # number of passwords
+PRINTLEN=${MAKEPASS_PRINTLEN:-0} # print length of passwords
+NORMAL=${MAKEPASS_NORMAL:-$ALNUM'-_'}
+SPECIAL=${MAKEPASS_SPECIAL:-$EVERY}
+WORDLIST=${MAKEPASS_WORDLIST:-/usr/share/dict/words}
+XWIDTH=$(tput cols)
+WORDS=()
+
+#
+# Functions
+#
# makepass-function
-function makepass {
- local l=$1
- local words
- local first
- local last
- MAKEPASS_WORDLIST=${MAKEPASS_WORDLIST:-/usr/share/dict/words}
- # if $l is not a number, then exit
- [[ ! $l =~ ^[0-9]+$ ]] && [[ ! "$l" == "" ]] && echo "not a number" && return 1
- (( l <= 1 || l > 255 )) && echo "Argument must be between 0 and 255" && return 1
- # if $1 is actually empty, set $l to random value for each output
-
- echo "Normal passwords:"
- for _ in {1..10}; do
- [ "$1" = "" ] && l=$(shuf -i 8-44 -n 1)
- head -n10 /dev/urandom | tr -dc _A-Z-a-z-0-9 | cut -c-"${1:-$l}";
- done | column
+function main {
+
+ # Getopts
+ while getopts 'hl:n:p' opt; do
+ case $opt in
+ h)
+ help && return 0;;
+ l)
+ if ! int_in_range "$OPTARG" 0 "$MAX"; then die "-l takes a number between 0 and $MAX"; fi
+ LENGTH=$OPTARG;;
+ n)
+ if ! int_in_range "$OPTARG" 1 "$MAX"; then die "-n takes a number between 1 and $MAX"; fi
+ NUMBER=$OPTARG;;
+ p)
+ PRINTLEN=1;;
+ *)
+ die "Unknown argument";;
+ esac
+ done
+ shift $((OPTIND - 1))
+
+
+ #
+ # Some error-checking
+ #
+
+ if [[ $# -gt 1 ]]; then die "only one argument"; fi
+
+ if [[ -n $1 ]]; then LENGTH=$1; fi
+
+ # Check $LENGTH and $NUMBER
+ if ! int_in_range "$LENGTH" 0 "$MAX"; then die "length must be a number between 0 and $MAX"; fi
+ if ! int_in_range "$NUMBER" 0 "$MAX"; then die "number-argument must be between and $MAX"; fi
+
+
+ #
+ # Some other work
+ #
+
+ if (( PRINTLEN )); then PRINTLEN=$((LENGTH < 100 ? 2 : 3)); fi
+
+ COL_WIDTH=$(( ( LENGTH ? LENGTH : RANGE_MAX ) + 2 ))
+ COL_NUM=$(( XWIDTH / ( COL_WIDTH + ( PRINTLEN ? PRINTLEN + 1 : 0 )) ))
+
+
+
+ #
+ # Print!
+ #
+
+ print_columns "Normal passwords" "$NUMBER" "$NORMAL"
+
echo ""
- echo "Passwords with special characters:"
- for _ in {1..6}; do
- [ "$1" = "" ] && l=$(shuf -i 16-64 -n 1)
- first=$(head -n10 /dev/urandom | tr -dc A-Za-z | cut -c-1)
- words=$(head -n10 /dev/urandom | tr -dc '!#$%&/()=?+-_,.;:<>[]{}|\@*^A-Z-a-z-0-9' | cut -c-"${1:-$l}")
- last=$(head -n10 /dev/urandom | tr -dc A-Za-z | cut -c-1)
- echo "${first}${words}${last}"
- done | column
+ print_columns "Passwords with special characters" "$((NUMBER*2/3))" "$SPECIAL"
if [ -r "${MAKEPASS_WORDLIST}" ]; then
echo ""
echo "Passphrases:"
- for _ in {1..5}; do
- words=$(shuf -n 8 "${MAKEPASS_WORDLIST}" | tr '\n' '-' | tr -dc '_A-Z-a-z-0-9')
- echo "${words:0:-1}"
+ mapfile -t WORDS < "${WORDLIST}"
+
+ for ((i=0; i < (NUMBER / 2); i++)); do
+ passphrase
done;
fi
+
+
+}
+
+# Function to print passwords in neat columns
+function print_columns {
+ local title=$1
+ local num=$2
+ local chars=$3
+ local i
+
+ local strings=()
+ for ((i=0; i < num; i++)); do
+ strings+=("$(randstring "$chars")")
+ done
+
+ echo "${title}:"
+
+ i=0
+ for s in "${strings[@]}"; do
+ ((i++))
+ if (( PRINTLEN )); then printf "%0${PRINTLEN}i " "${#s}"; fi
+ printf "%-${COL_WIDTH}s" "$s"
+ if ((i % COL_NUM == 0 || (i == num && i % COL_NUM > 0) )); then echo ""; fi
+ done
+}
+
+# Function to create random strings
+function randstring {
+ local chars=${1:-$NORMAL}
+ local string
+ local len=$(( LENGTH ? LENGTH : RANDOM % (RANGE_MAX - RANGE_MIN + 1) + RANGE_MIN ))
+
+ string+="${ALPHA:$((RANDOM % ${#ALPHA})):1}"
+ while (( ${#string} <= len - 2 )); do string+="${chars:$((RANDOM % ${#chars})):1}"; done
+ if (( len >= 2 )); then string+="${ALNUM:$((RANDOM % ${#ALNUM})):1}"; fi
+
+ echo "$string"
+}
+
+# PASSPHRASE!!
+function passphrase {
+ local string
+ local i
+ for ((i=0; i < PASS_WORDS; i++)); do
+ string+="${WORDS[$((RANDOM % ${#WORDS[@]}))]}-"
+ done
+ string="${string:0:-1}"
+
+ echo "$string"
+}
+
+# Check if int and if in range
+function int_in_range {
+ local num=$1
+ local min=$2
+ local max=$3
+
+ if [[ $num =~ ^[0-9]+$ && $min -le $num && $num -le $max ]]; then
+ return 0
+ fi
+ return 1
+}
+
+# Function to die
+function die() {
+ echo -- "$@"
+ echo -- "Maybe try running \`makepass -h\` for help"
+ exit 1
+}
+
+# Help-function
+function help {
+ echo $'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.
+
+NOTES
+ This scripts makes use of $RANDOM - a builtin in bash which produces a
+ pseudo-random integer, newly generated each time the parameter is
+ referenced. This should provide enough randomnes to generate sufficiently
+ secure passwords.
+
+AUTHOR
+ Dennis Eriksen <https://dnns.no>'
+
+ return 0
}
-makepass "${@:-}"
+# RUN IT!
+main "${@:-}"
## END OF FILE #################################################################