#!/bin/bash
# Generate a purely whitespace password with 128 bits of symmetric security.
#
# Characters are strictly non-control, non-graphical spaces/blanks. Both
# nonzero- and zero-width characters are used. Two characters are technically
# vertical characters, but aren't interpreted as such in the shell. They are
# "\u2028" and "\u2029". You might need a font with good Unicode support to
# prevent some of these characters creating tofu.
rng() {
# Cryptographically secure RNG
local min=$((2 ** 32 % 30)) # 30 = size of $s below
local r=$SRANDOM
while [ "$r" -lt "$min" ]; do r=$SRANDOM; done # Modulo with rejection
echo "$(($r % 30))"
}
s=(
# Non-zero width characters
"\u0009" # Character tabulation
"\u0020" # Space
"\u00A0" # Non-breaking space
"\u2000" # En quad
"\u2001" # Em quad
"\u2002" # En space
"\u2003" # Em space
"\u2004" # Three-per-em space
"\u2005" # Four-per-em space
"\u2006" # Six-per-em space
"\u2007" # Figure space
"\u2008" # Punctuation space
"\u2009" # Thin space
"\u200A" # Hair space
"\u2028" # Line separator
"\u2029" # Paragraph separator
"\u202F" # Narrow no-break space
"\u205F" # Medium mathematical space
"\u2800" # Braille pattern blank
"\u3000" # Ideographic space
"\u3164" # Hangul filler
"\uFFA0" # Halfwidth hangul filler
# Zero width characters
"\u115F" # Hangul choseong filler
"\u1160" # Hangul jungseong filler
"\u180E" # Mongolian vowel separator
"\u200B" # Zero width space
"\u200C" # Zero width non-joiner
"\u200D" # Zero width joiner
"\u2060" # Word joiner
"\uFEFF" # Zero width non-breaking space
)
p=""
# Generate 27 characters for at least 128 bits security
for i in {1..27}; do
r=$(rng)
c=${s[$r]}
p="${p}${c}"
done
tabs -1 # Tab width of 1 space
# Wrap the password in braille pattern blanks for correctly handling zero-width
# characters and to prevent whitespace stripping by the auth form.
echo -e "\"\u2800${p}\u2800\""
Example:
$ bash whitespace.bash
"⠀ ᅠ ⠀ ᅟ ᅠ⠀"