diff options
author | Tuowen Zhao <ztuowen@gmail.com> | 2020-12-16 22:13:45 -0700 |
---|---|---|
committer | Tuowen Zhao <ztuowen@gmail.com> | 2020-12-16 22:13:45 -0700 |
commit | fb45741fc1dbd40dd2be72bc35a28c6ee8f3f7a5 (patch) | |
tree | dd7746c9910755dfeb5bf28bda68e28b47d5771f /plugins/genpass/genpass-xkcd | |
parent | 3aaa0bc62ece494dd2b6e47a191de79e562156f9 (diff) | |
parent | b28665aebb4c1b07a57890eb59551bc51d0acf37 (diff) | |
download | zsh-fb45741fc1dbd40dd2be72bc35a28c6ee8f3f7a5.tar.gz zsh-fb45741fc1dbd40dd2be72bc35a28c6ee8f3f7a5.tar.bz2 zsh-fb45741fc1dbd40dd2be72bc35a28c6ee8f3f7a5.zip |
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'plugins/genpass/genpass-xkcd')
-rwxr-xr-x | plugins/genpass/genpass-xkcd | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/plugins/genpass/genpass-xkcd b/plugins/genpass/genpass-xkcd new file mode 100755 index 000000000..a486ccb40 --- /dev/null +++ b/plugins/genpass/genpass-xkcd @@ -0,0 +1,68 @@ +#!/usr/bin/env zsh +# +# Usage: genpass-xkcd [NUM] +# +# Generate a password made of words from /usr/share/dict/words +# with the security margin of at least 128 bits. +# +# Example password: 9-mien-flood-Patti-buxom-dozes-ickier-pay-ailed-Foster +# +# If given a numerical argument, generate that many passwords. +# +# The name of this utility is a reference to https://xkcd.com/936/. + +emulate -L zsh -o no_unset -o warn_create_global -o warn_nested_var -o extended_glob + +if [[ ARGC -gt 1 || ${1-1} != ${~:-<1-$((16#7FFFFFFF))>} ]]; then + print -ru2 -- "usage: $0 [NUM]" + return 1 +fi + +zmodload zsh/system zsh/mathfunc || return + +local -r dict=/usr/share/dict/words + +if [[ ! -e $dict ]]; then + print -ru2 -- "$0: file not found: $dict" + return 1 +fi + +# Read all dictionary words and leave only those made of 1-6 characters. +local -a words +words=(${(M)${(f)"$(<$dict)"}:#[a-zA-Z](#c1,6)}) || return + +if (( $#words < 2 )); then + print -ru2 -- "$0: not enough suitable words in $dict" + return 1 +fi + +if (( $#words > 16#7FFFFFFF )); then + print -ru2 -- "$0: too many words in $dict" + return 1 +fi + +# Figure out how many words we need for 128 bits of security margin. +# Each word adds log2($#words) bits. +local -i n=$((ceil(128. / log2($#words)))) + +{ + local c + repeat ${1-1}; do + print -rn -- $n + repeat $n; do + while true; do + # Generate a random number in [0, 2**31). + local -i rnd=0 + repeat 4; do + sysread -s1 c || return + (( rnd = (~(1 << 23) & rnd) << 8 | #c )) + done + # Avoid bias towards words in the beginning of the list. + (( rnd < 16#7FFFFFFF / $#words * $#words )) || continue + print -rn -- -$words[rnd%$#words+1] + break + done + done + print + done +} </dev/urandom |