mirror of
https://github.com/ohmyzsh/ohmyzsh.git
synced 2026-01-23 02:35:38 +01:00
100 lines
3.1 KiB
Bash
Executable file
100 lines
3.1 KiB
Bash
Executable file
#!/usr/bin/env zsh
|
|
#
|
|
# Usage: genpass-apple [NUM]
|
|
#
|
|
# Generate a password made of 6 syllables of 3 characters each
|
|
# with the security margin of at least 71 bits.
|
|
#
|
|
# Example password: nukci1-zochob-Werfip
|
|
#
|
|
# If given a numerical argument, generate that many passwords.
|
|
|
|
emulate -L zsh -o no_unset -o warn_create_global -o warn_nested_var
|
|
|
|
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 vowels=aeiouy
|
|
local -r consonants=bcdfghjkmnpqrstvwxz
|
|
local -r digits=0123456789
|
|
|
|
# Sets REPLY to a uniformly distributed random number in [1, $1].
|
|
# Requires: $1 <= 256.
|
|
function -$0-rand() {
|
|
local c
|
|
while true; do
|
|
sysread -s1 c || return
|
|
# Avoid bias towards smaller numbers.
|
|
(( #c < 256 / $1 * $1 )) && break
|
|
done
|
|
typeset -g REPLY=$((#c % $1 + 1))
|
|
}
|
|
|
|
local REPLY chars i
|
|
|
|
repeat ${1-1}; do
|
|
# Generate 6 syllables of the form cvc where c and v
|
|
# denote random consonants and vowels respectively.
|
|
local syllables=()
|
|
repeat 6; do
|
|
syllables+=('')
|
|
for chars in $consonants $vowels $consonants; do
|
|
-$0-rand $#chars || return
|
|
syllables[-1]+=$chars[REPLY]
|
|
done
|
|
done
|
|
|
|
# First concatenate all syllables without hyphens
|
|
local pwd_chars=${(j::)syllables}
|
|
|
|
# Valid positions for digit in the 18-char string: 5, 6, 11, 12, 17 (0-indexed)
|
|
# 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
|
|
local digit=$digits[REPLY]
|
|
|
|
# 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 (excluding digit position)
|
|
# Calculate digit position in final string (with hyphens)
|
|
local final_digit_pos=$((digit_pos + (digit_pos - 1) / 6))
|
|
|
|
while true; do
|
|
-$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
|
|
done
|
|
# NOTE: We aren't using ${(U)c} here because its results are
|
|
# locale-dependent. For example, when upper-casing 'i' in Turkish
|
|
# locale we would get 'İ', a.k.a. latin capital letter i with dot
|
|
# above. We could set LC_CTYPE=C locally but then we would run afoul
|
|
# of this zsh bug: https://www.zsh.org/mla/workers/2020/msg00588.html.
|
|
local c=$pwd[REPLY]
|
|
printf -v c '%o' $((#c - 32))
|
|
printf "%s\\$c%s\\n" "$pwd[1,REPLY-1]" "$pwd[REPLY+1,-1]" || return
|
|
done
|
|
} always {
|
|
unfunction -m -- "-${(b)0}-*"
|
|
} </dev/urandom
|