summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/CODEOWNERS1
-rw-r--r--plugins/genpass/README.md65
-rw-r--r--plugins/genpass/genpass.plugin.zsh95
3 files changed, 161 insertions, 0 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 8e175d549..b091f5d89 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,5 +1,6 @@
# Plugin owners
plugins/aws/ @maksyms
+plugins/genpass/ @atoponce
plugins/git-lfs/ @vietduc01100001
plugins/gitfast/ @felipec
plugins/sdk/ @rgoldberg
diff --git a/plugins/genpass/README.md b/plugins/genpass/README.md
new file mode 100644
index 000000000..e6e7a5138
--- /dev/null
+++ b/plugins/genpass/README.md
@@ -0,0 +1,65 @@
+# genpass
+
+This plugin provides three unique password generators for ZSH. Each generator
+has at least a 128-bit security margin and generates passwords from the
+cryptographically secure `/dev/urandom`. Each generator can also take an
+optional numeric argument to generate multiple passwords.
+
+Requirements:
+
+* `grep(1)`
+* GNU coreutils (or appropriate for your system)
+* Word list providing `/usr/share/dict/words`
+
+To use it, add `genpass` to the plugins array in your zshrc file:
+
+ plugins=(... genpass)
+
+## genpass-apple
+
+Generates a pronounceable pseudoword passphrase of the "cvccvc" consonant/vowel
+syntax, inspired by [Apple's iCloud Keychain password generator][1]. Each
+pseudoword has exactly 1 digit placed at the edge of a "word" and exactly 1
+capital letter to satisfy most password security requirements.
+
+ % genpass-apple
+ gelcyv-foqtam-fotqoh-viMleb-lexduv-6ixfuk
+
+ % genpass-apple 3
+ japvyz-qyjti4-kajrod-nubxaW-hukkan-dijcaf
+ vydpig-fucnul-3ukpog-voggom-zygNad-jepgad
+ zocmez-byznis-hegTaj-jecdyq-qiqmiq-5enwom
+
+[1]: https://developer.apple.com/password-rules/
+
+## genpass-monkey
+
+Generates visually unambiguous random meaningless strings using [Crockford's
+base32][2].
+
+ % genpass-monkey
+ xt7gn976e7jj3fstgpy27330x3
+
+ % genpass-monkey 3
+ n1qqwtzgejwgqve9yzf2gxvx4m
+ r2n3f5s6vbqs2yx7xjnmahqewy
+ 296w9y9rts3p5r9yay0raek8e5
+
+[2]: https://www.crockford.com/base32.html
+
+## genpass-xkcd
+
+Generates passphrases from `/usr/share/dict/words` inspired by the [famous (and
+slightly misleading) XKCD comic][3]. Each passphrase is prepended with a digit
+showing the number of words in the passphrase to adhere to password security
+requirements that require digits. Each word is 6 characters or less.
+
+ % genpass-xkcd
+ 9-eaten-Slav-rife-aired-hill-cordon-splits-welsh-napes
+
+ % genpass-xkcd 3
+ 9-worker-Vlad-horde-shrubs-smite-thwart-paw-alters-prawns
+ 9-tutors-stink-rhythm-junk-snappy-hooray-barbs-mewl-clomp
+ 9-vital-escape-Angkor-Huff-wet-Mayra-abbés-putts-guzzle
+
+[3]: https://xkcd.com/936/
diff --git a/plugins/genpass/genpass.plugin.zsh b/plugins/genpass/genpass.plugin.zsh
new file mode 100644
index 000000000..15bfebda8
--- /dev/null
+++ b/plugins/genpass/genpass.plugin.zsh
@@ -0,0 +1,95 @@
+autoload -U regexp-replace
+zmodload zsh/mathfunc
+
+genpass-apple() {
+ # Generates a 128-bit password of 6 pseudowords of 6 characters each
+ # EG, xudmec-4ambyj-tavric-mumpub-mydVop-bypjyp
+ # Can take a numerical argument for generating extra passwords
+ local -i i j num
+
+ [[ $1 =~ '^[0-9]+$' ]] && num=$1 || num=1
+
+ local consonants="$(LC_ALL=C tr -cd b-df-hj-np-tv-xz < /dev/urandom \
+ | head -c $((24*$num)))"
+ local vowels="$(LC_ALL=C tr -cd aeiouy < /dev/urandom | head -c $((12*$num)))"
+ local digits="$(LC_ALL=C tr -cd 0-9 < /dev/urandom | head -c $num)"
+
+ # The digit is placed on a pseudoword edge using $base36. IE, Dvccvc or cvccvD
+ local position="$(LC_ALL=C tr -cd 056bchinotuz < /dev/urandom | head -c $num)"
+ local -A base36=(0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 a 10 b 11 c 12 d 13 \
+ e 14 f 15 g 16 h 17 i 18 j 19 k 20 l 21 m 22 n 23 o 24 p 25 q 26 r 27 s 28 \
+ t 29 u 30 v 31 w 32 x 33 y 34 z 35)
+
+ for i in {1..$num}; do
+ local pseudo=""
+
+ for j in {1..12}; do
+ # Uniformly iterate through $consonants and $vowels for each $i and $j
+ # Creates cvccvccvccvccvccvccvccvccvccvccvccvc for each $num
+ pseudo="${pseudo}${consonants:$((24*$i+2*${j}-26)):1}"
+ pseudo="${pseudo}${vowels:$((12*$i+${j}-13)):1}"
+ pseudo="${pseudo}${consonants:$((24*$i+2*${j}-25)):1}"
+ done
+
+ local -i digit_pos=${base36[${position[$i]}]}
+ local -i char_pos=$digit_pos
+
+ # The digit and uppercase character must be in different locations
+ while [[ $digit_pos == $char_pos ]]; do
+ char_pos=$base36[$(LC_ALL=C tr -cd 0-9a-z < /dev/urandom | head -c 1)]
+ done
+
+ # Places the digit on a pseudoword edge
+ regexp-replace pseudo "^(.{$digit_pos}).(.*)$" \
+ '${match[1]}${digits[$i]}${match[2]}'
+
+ # Uppercase a random character (that is not a digit)
+ regexp-replace pseudo "^(.{$char_pos})(.)(.*)$" \
+ '${match[1]}${(U)match[2]}${match[3]}'
+
+ # Hyphenate each 6-character pseudoword
+ regexp-replace pseudo '^(.{6})(.{6})(.{6})(.{6})(.{6})(.{6})$' \
+ '${match[1]}-${match[2]}-${match[3]}-${match[4]}-${match[5]}-${match[6]}'
+
+ printf "${pseudo}\n"
+ done
+}
+
+genpass-monkey() {
+ # Generates a 128-bit base32 password as if monkeys banged the keyboard
+ # EG, nz5ej2kypkvcw0rn5cvhs6qxtm
+ # Can take a numerical argument for generating extra passwords
+ local -i i num
+
+ [[ $1 =~ '^[0-9]+$' ]] && num=$1 || num=1
+
+ local pass=$(LC_ALL=C tr -cd '0-9a-hjkmnp-tv-z' < /dev/urandom \
+ | head -c $((26*$num)))
+
+ for i in {1..$num}; do
+ printf "${pass:$((26*($i-1))):26}\n"
+ done
+}
+
+genpass-xkcd() {
+ # Generates a 128-bit XKCD-style passphrase
+ # EG, 9-mien-flood-Patti-buxom-dozes-ickier-pay-ailed-Foster
+ # Can take a numerical argument for generating extra passwords
+ local -i i num
+
+ [[ $1 =~ '^[0-9]+$' ]] && num=$1 || num=1
+
+ # Get all alphabetic words of at most 6 characters in length
+ local dict=$(grep -E '^[a-zA-Z]{,6}$' /usr/share/dict/words)
+
+ # Calculate the base-2 entropy of each word in $dict
+ # Entropy is e = L * log2(C), where L is the length of the password (here,
+ # in words) and C the size of the character set (here, words in $dict).
+ # Solve for e = 128 bits of entropy. Recall: log2(n) = log(n)/log(2).
+ local -i n=$((int(ceil(128*log(2)/log(${(w)#dict})))))
+
+ for i in {1..$num}; do
+ printf "$n-"
+ printf "$dict" | shuf -n "$n" | paste -sd '-'
+ done
+}