fix(genpass): follow Apple password specification requirements

This commit is contained in:
Ryan Vo 2025-06-08 10:51:05 -05:00
commit 98af06ad66
2 changed files with 49 additions and 27 deletions

View file

@ -19,19 +19,20 @@ passwords in a script:
## genpass-apple ## genpass-apple
Generates a pronounceable pseudoword passphrase of the "cvccvc" consonant/vowel Generates a pronounceable pseudoword passphrase of the "cvccvc" consonant/vowel
syntax, inspired by [Apple's iCloud Keychain password generator][1]. Each syntax based on [Apple Passwords Generated Strong Password Format][1]. Each
password has exactly 1 digit placed at the edge of a "word" and exactly 1 password has exactly 1 digit placed on other either side of the hyphen or at the
capital letter to satisfy most password security requirements. end of the password and exactly 1 capital letter to satisfy most password security
requirements.
% genpass-apple % genpass-apple
gelcyv-foqtam-fotqoh-viMleb-lexduv-6ixfuk nyvtyv-mYhmob-xyqby4
% genpass-apple 3 % genpass-apple 3
japvyz-qyjti4-kajrod-nubxaW-hukkan-dijcaf kedfu4-nowryn-gezjeR
vydpig-fucnul-3ukpog-voggom-zygNad-jepgad qoJsoh-fikgon-nugfa8
zocmez-byznis-hegTaj-jecdyq-qiqmiq-5enwom fecvos-kiCliv-byvhi1
[1]: https://developer.apple.com/password-rules/ [1]: https://rmondello.com/2024/10/07/apple-passwords-generated-strong-password-format/
## genpass-monkey ## genpass-monkey

View file

@ -2,10 +2,10 @@
# #
# Usage: genpass-apple [NUM] # Usage: genpass-apple [NUM]
# #
# Generate a password made of 6 pseudowords of 6 characters each # Generate a password made of 6 syllables of 3 characters each
# with the security margin of at least 128 bits. # with the security margin of at least 71 bits.
# #
# Example password: xudmec-4ambyj-tavric-mumpub-mydVop-bypjyp # Example password: nukci1-zochob-Werfip
# #
# If given a numerical argument, generate that many passwords. # If given a numerical argument, generate that many passwords.
@ -20,7 +20,7 @@ zmodload zsh/system zsh/mathfunc || return
{ {
local -r vowels=aeiouy local -r vowels=aeiouy
local -r consonants=bcdfghjklmnpqrstvwxz local -r consonants=bcdfghjkmnpqrstvwxz
local -r digits=0123456789 local -r digits=0123456789
# Sets REPLY to a uniformly distributed random number in [1, $1]. # Sets REPLY to a uniformly distributed random number in [1, $1].
@ -35,34 +35,55 @@ zmodload zsh/system zsh/mathfunc || return
typeset -g REPLY=$((#c % $1 + 1)) typeset -g REPLY=$((#c % $1 + 1))
} }
local REPLY chars local REPLY chars i
repeat ${1-1}; do repeat ${1-1}; do
# Generate 6 pseudowords of the form cvccvc where c and v # Generate 6 syllables of the form cvc where c and v
# denote random consonants and vowels respectively. # denote random consonants and vowels respectively.
local words=() local syllables=()
repeat 6; do repeat 6; do
words+=('') syllables+=('')
repeat 2; do for chars in $consonants $vowels $consonants; do
for chars in $consonants $vowels $consonants; do -$0-rand $#chars || return
-$0-rand $#chars || return syllables[-1]+=$chars[REPLY]
words[-1]+=$chars[REPLY]
done
done done
done done
local pwd=${(j:-:)words} # First concatenate all syllables without hyphens
local pwd_chars=${(j::)syllables}
# Replace either the first or the last character in one of # Valid positions for digit in the 18-char string: 5, 6, 11, 12, 17 (0-indexed)
# the words with a random digit. # In 1-indexed: 6, 7, 12, 13, 18
local -a digit_positions=(6 7 12 13 18)
-$0-rand $#digit_positions || return
local digit_pos=$digit_positions[REPLY]
# Generate random digit
-$0-rand $#digits || return -$0-rand $#digits || return
local digit=$digits[REPLY] local digit=$digits[REPLY]
-$0-rand $((2 * $#words)) || return
pwd[REPLY/2*7+2*(REPLY%2)-1]=$digit # Special handling for positions 7 and 13 (start of syllable pair)
if [[ $digit_pos == 7 || $digit_pos == 13 ]]; then
# Shift characters backwards to maintain cvcv pattern
for (( i = digit_pos + 5; i > digit_pos; i-- )); do
pwd_chars[i]=$pwd_chars[i-1]
done
fi
# Place the digit
pwd_chars[digit_pos]=$digit
# Now add hyphens to create the final password
local pwd="${pwd_chars[1,6]}-${pwd_chars[7,12]}-${pwd_chars[13,18]}"
# Convert one lower-case character to upper case. # Convert one lower-case character to upper case (excluding digit position)
# Calculate digit position in final string (with hyphens)
local final_digit_pos=$((digit_pos + (digit_pos - 1) / 6))
while true; do while true; do
-$0-rand $#pwd || return -$0-rand $#pwd || return
# Skip if it's the digit position or a hyphen
[[ REPLY == $final_digit_pos || $pwd[REPLY] == '-' ]] && continue
[[ $vowels$consonants == *$pwd[REPLY]* ]] && break [[ $vowels$consonants == *$pwd[REPLY]* ]] && break
done done
# NOTE: We aren't using ${(U)c} here because its results are # NOTE: We aren't using ${(U)c} here because its results are