]> src.twobees.de Git - dotfiles.git/blob - stow/oh-my-zsh/.oh-my-zsh/plugins/genpass/genpass-xkcd
...
[dotfiles.git] / stow / oh-my-zsh / .oh-my-zsh / plugins / genpass / genpass-xkcd
1 #!/usr/bin/env zsh
2 #
3 # Usage: genpass-xkcd [NUM]
4 #
5 # Generate a password made of words from /usr/share/dict/words
6 # with the security margin of at least 128 bits.
7 #
8 # Example password: 9-mien-flood-Patti-buxom-dozes-ickier-pay-ailed-Foster
9 #
10 # If given a numerical argument, generate that many passwords.
11 #
12 # The name of this utility is a reference to https://xkcd.com/936/.
13
14 emulate -L zsh -o no_unset -o warn_create_global -o warn_nested_var -o extended_glob
15
16 if [[ ARGC -gt 1 || ${1-1} != ${~:-<1-$((16#7FFFFFFF))>} ]]; then
17   print -ru2 -- "usage: $0 [NUM]"
18   return 1
19 fi
20
21 zmodload zsh/system zsh/mathfunc || return
22
23 local -r dict=/usr/share/dict/words
24
25 if [[ ! -e $dict ]]; then
26   print -ru2 -- "$0: file not found: $dict"
27   return 1
28 fi
29
30 # Read all dictionary words and leave only those made of 1-6 characters.
31 local -a words
32 words=(${(M)${(f)"$(<$dict)"}:#[a-zA-Z](#c1,6)}) || return
33
34 if (( $#words < 2 )); then
35   print -ru2 -- "$0: not enough suitable words in $dict"
36   return 1
37 fi
38
39 if (( $#words > 16#7FFFFFFF )); then
40   print -ru2 -- "$0: too many words in $dict"
41   return 1
42 fi
43
44 # Figure out how many words we need for 128 bits of security margin.
45 # Each word adds log2($#words) bits.
46 local -i n=$((ceil(128. / (log($#words) / log(2)))))
47
48 {
49   local c
50   repeat ${1-1}; do
51     print -rn -- $n
52     repeat $n; do
53       while true; do
54         # Generate a random number in [0, 2**31).
55         local -i rnd=0
56         repeat 4; do
57           sysread -s1 c || return
58           (( rnd = (~(1 << 23) & rnd) << 8 | #c ))
59         done
60         # Avoid bias towards words in the beginning of the list.
61         (( rnd < 16#7FFFFFFF / $#words * $#words )) || continue
62         print -rn -- -$words[rnd%$#words+1]
63         break
64       done
65     done
66     print
67   done
68 } </dev/urandom