#!/usr/bin/env zsh # # Usage: genpass-apple [NUM] # # Generate a password made of 6 pseudowords of 6 characters each # with the security margin of at least 128 bits. # # Example password: xudmec-4ambyj-tavric-mumpub-mydVop-bypjyp # # 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=bcdfghjklmnpqrstvwxz 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 repeat ${1-1}; do # Generate 6 pseudowords of the form cvccvc where c and v # denote random consonants and vowels respectively. local words=() repeat 6; do words+=('') repeat 2; do for chars in $consonants $vowels $consonants; do -$0-rand $#chars || return words[-1]+=$chars[REPLY] done done done local pwd=${(j:-:)words} # Replace either the first or the last character in one of # the words with a random digit. -$0-rand $#digits || return local digit=$digits[REPLY] -$0-rand $((2 * $#words)) || return pwd[REPLY/2*7+2*(REPLY%2)-1]=$digit # Convert one lower-case character to upper case. while true; do -$0-rand $#pwd || return [[ $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}-*" }