There are many ways to generate passwords, and to avoid passwords in the first place (which you really should do), but I wanted a little practice in writing interactive bash scripts. So I chose to create a password generator based on the Diceware algorithm, even though I don’t carry any dice with me.
Find of the day is the excellent gum tool that makes it easy to create pretty user interaction. Thanks Ari!
#!/bin/bash
set -eo pipefail
# Ref: https://theworld.com/~reinhold/diceware.html
if [[ ! -x "$(command -v curl)" ]]; then
echo "Please install curl"
exit 1
fi
if [[ ! -x "$(command -v shuf)" ]]; then
echo "Please install coreutils"
exit 1
fi
if [[ ! -x "$(command -v gum)" ]]; then
echo "Please install https://github.com/charmbracelet/gum"
exit 1
fi
echo 'Which worldlist to use?'
WORDLIST=$(gum choose 'diceware (original)' 'beale (alternative)')
echo 'How many words to pick?'
COUNT=$(gum input --value='6')
echo 'What is your smallest word length?'
MINLEN=$(gum input --value='3')
echo 'Which word separator to use?'
SEPARATOR=$(gum choose 'space' 'dash' 'underscore')
WORDLIST=$(echo "$WORDLIST" | awk '{print $1}')
echo "Using $WORDLIST wordlist to create ${COUNT} words of ${MINLEN} or more characters each, separated by ${SEPARATOR} characters."
CACHE_DIR="${HOME}/.diceware"
if [[ ! -d "$CACHE_DIR" ]]; then
mkdir "$CACHE_DIR"
fi
WORDFILE="${CACHE_DIR}/${WORDLIST}.wordlist.asc"
if [[ ! -f "$WORDFILE" ]]; then
# vendored from https://theworld.com/%7Ereinhold/
curl -sSf "https://tomczarniecki.com/assets/${WORDLIST}.wordlist.asc" \
-o "$WORDFILE"
fi
separator=' '
if [[ "$SEPARATOR" == "dash" ]]; then
separator='-'
elif [[ "$SEPARATOR" == "underscore" ]]; then
separator='_'
fi
phrase=''
for i in $(seq "$COUNT"); do
word=''
until [[ "${#word}" -ge "${MINLEN}" ]]; do
group=''
for j in {1..5}; do
roll=$(shuf -i 1-6 -n 1 --random-source=/dev/random)
if [[ -z "$group" ]]; then
group="$roll"
else
group="${group}${roll}"
fi
done
word=$(grep "$group" "$WORDFILE" | awk '{print $2}')
done
if [[ -z "$phrase" ]]; then
phrase="$word"
else
phrase="${phrase}${separator}${word}"
fi
done
gum style \
--border double --align center \
--margin '1 2' --padding '2 4' \
"$phrase"
Sample output:
$> ./passphrase.sh
Which worldlist to use?
> diceware (original)
beale (alternative)
How many words to pick?
> 6
What is your smallest word length?
> 3
Which word separator to use?
> space
Using diceware wordlist to create 6 words of 3 or more characters each, separated by space characters.
╔══════════════════════════════════════════╗
║ ║
║ ║
║ neuron pansy cicada mood young ani ║
║ ║
║ ║
╚══════════════════════════════════════════╝
Hope you find this useful.