Skip to content

Instantly share code, notes, and snippets.

@imaami
Last active August 30, 2023 12:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save imaami/34634902ad7facf5ef67146e3e43b5b0 to your computer and use it in GitHub Desktop.
Save imaami/34634902ad7facf5ef67146e3e43b5b0 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
word_max=9 # Exclude words longer than this
nphrases=3 # Generate this many passphrases
defwords=4 # Default number of words/phrase
pls() {
local -i n=${#1} r=$2
local s
printf -v s -- ' %s%*s%s\n' \
"$1" 2 '' '-h | --help print this help text' \
"$1" 2 '' "[NUM [DICT]] create $nphrases passphrases" \
' ' $n '' ' │ │' \
' ' $n '' ' │ └────── dictionary file' \
' ' $n '' " └─────────── words/phrase ($defwords)"
printf 'Usage:\n%s' "$s"
exit $r
}
if (( "$#" )); then
case "$1" in
'-h'|'--help') pls "${0##*/}" ;;
*) (( "$1" > 0 )) || pls "${0##*/}" 1 ;;
esac
fi
num_words="$1"
orig_dict="$2"
dict_file=''
ret_value=0
(( num_words )) || (( num_words = defwords ))
[[ "$orig_dict" ]] || {
for sfx in '-small' '' '-large' '-huge' '-insane'; do
orig_dict="/usr/share/dict/american-english$sfx"
[[ ! -e "$orig_dict" ]] || break
orig_dict="/usr/share/dict/british-english$sfx"
[[ ! -e "$orig_dict" ]] || break
done
}
[[ -e "$orig_dict" ]] || {
echo 'No dictionary found' >&2
exit 1
}
dict_file="$(mktemp -p /tmp tmp-dict.XXXXXXXX)"
LANG=C egrep "^[[:alpha:]]{1,$((word_max))}$" "$orig_dict" > "$dict_file"
dict_size=$(wc -l "$dict_file" 2>/dev/null | cut -d' ' -f1)
if (( dict_size < 1 )); then
echo 'Empty dictionary' >&2
ret_value=1
else
if [[ -t 2 ]]; then
B='\e[1;32m'; L='\e[38;5;158m'; E='\e[0;33m'; R='\e[38;5;177m'; N='\e[0m'
else
B=''; L=''; E=''; R=''; N=''
fi
entropy=$(bc -l <<< "scale=3; l($dict_size^$num_words)/l(2)")
printf "$B[$N ${L}%s$E:$N $R%7s$N %s $B]$N\n" \
'phrase size' "$num_words" 'words' \
' dictionary' "$dict_size" 'words' \
' entropy' "$entropy" 'bits ' >&2
rand_mask='0xffffffff'
while (( rand_mask >= dict_size )); do
(( m = rand_mask >> 1 ))
(( m >= dict_size )) || break
rand_mask=$(printf '0x%x\n' "$m")
done
for (( i = 0; i < nphrases; i++ )); do
(( n = num_words ))
while (( n > 0 )); do
R=$(od -N4 -tu4 -An -w4 /dev/urandom | tr -d ' ')
R=$((R & rand_mask))
(( R < dict_size )) || continue
W=$(tail -n+$((R+1)) "$dict_file" | head -1)
W="$(tr '[:lower:]' '[:upper:]' <<< ${W:0:1})${W:1}"
echo -n "$W"
(( n-- ))
done
echo
done
fi
rm -f "$dict_file"
exit $ret_value
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment