Parent directory

genpass-apple

2327 bytes
 1#!/usr/bin/env zsh
 2#
 3# Usage: genpass-apple [NUM]
 4#
 5# Generate a password made of 6 pseudowords of 6 characters each
 6# with the security margin of at least 128 bits.
 7#
 8# Example password: xudmec-4ambyj-tavric-mumpub-mydVop-bypjyp
 9#
10# If given a numerical argument, generate that many passwords.
11
12emulate -L zsh -o no_unset -o warn_create_global -o warn_nested_var
13
14if [[ ARGC -gt 1 || ${1-1} != ${~:-<1-$((16#7FFFFFFF))>} ]]; then
15  print -ru2 -- "usage: $0 [NUM]"
16  return 1
17fi
18
19zmodload zsh/system zsh/mathfunc || return
20
21{
22  local -r vowels=aeiouy
23  local -r consonants=bcdfghjklmnpqrstvwxz
24  local -r digits=0123456789
25
26  # Sets REPLY to a uniformly distributed random number in [1, $1].
27  # Requires: $1 <= 256.
28  function -$0-rand() {
29    local c
30    while true; do
31      sysread -s1 c || return
32      # Avoid bias towards smaller numbers.
33      (( #c < 256 / $1 * $1 )) && break
34    done
35    typeset -g REPLY=$((#c % $1 + 1))
36  }
37
38  local REPLY chars
39
40  repeat ${1-1}; do
41    # Generate 6 pseudowords of the form cvccvc where c and v
42    # denote random consonants and vowels respectively.
43    local words=()
44    repeat 6; do
45      words+=('')
46      repeat 2; do
47        for chars in $consonants $vowels $consonants; do
48          -$0-rand $#chars || return
49          words[-1]+=$chars[REPLY]
50        done
51      done
52    done
53
54    local pwd=${(j:-:)words}
55
56    # Replace either the first or the last character in one of
57    # the words with a random digit.
58    -$0-rand $#digits || return
59    local digit=$digits[REPLY]
60    -$0-rand $((2 * $#words)) || return
61    pwd[REPLY/2*7+2*(REPLY%2)-1]=$digit
62
63    # Convert one lower-case character to upper case.
64    while true; do
65      -$0-rand $#pwd || return
66      [[ $vowels$consonants == *$pwd[REPLY]* ]] && break
67    done
68    # NOTE: We aren't using ${(U)c} here because its results are
69    # locale-dependent. For example, when upper-casing 'i' in Turkish
70    # locale we would get 'İ', a.k.a. latin capital letter i with dot
71    # above. We could set LC_CTYPE=C locally but then we would run afoul
72    # of this zsh bug: https://www.zsh.org/mla/workers/2020/msg00588.html.
73    local c=$pwd[REPLY]
74    printf -v c '%o' $((#c - 32))
75    printf "%s\\$c%s\\n" "$pwd[1,REPLY-1]" "$pwd[REPLY+1,-1]" || return
76  done
77} always {
78  unfunction -m -- "-${(b)0}-*"
79} </dev/urandom