feat(plugins): add genpass plugin with 3 distinct password generators (#9502)
parent
9181954255
commit
9d6b3984f9
3 changed files with 161 additions and 0 deletions
@ -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/ |
||||||
@ -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 |
||||||
|
} |
||||||
Loading…
Reference in new issue