Just using the shell, either with built-in tools, or 3rd party generators, for building passwords with at least 70-bits of entropy (1 in at least 1,180,591,620,717,411,303,424 possibilities).
Each provide their own advantages and disadvantages.
All possible 94 graphical characters (not the <Space>
or <Tab>
) are
randomly chosen.
- Length: 11 characters
- Complexity: Very frustrating to type
- Memorable: Difficult
$ tr -cd '[:graph:]' < /dev/urandom | fold -w 11 | head -n 1
3R=%MGPos}0
Lowercase, uppercase, and digits only. No special characters.
- Length: 12 characters
- Complexity: Frustrating to type on mobile
- Memorable: Moderate
$ tr -cd 'a-zA-Z0-9' < /dev/urandom | fold -w 12 | head -n 1
mhD8TMCBFldd
Based on Crockford's base-32, which uses all 10 digits, then removes ambiguity
by removing i
, l
, o
, and u
.
- Length: 14 characters
- Complexity: Easy to type
- Memorable: Moderate, easy to read for vision difficulties
$ tr -cd '0-9a-hjkmnp-tv-z' < /dev/urandom | fold -w 14 | head -n 1
f2hp95rjxzd4az
Based on the default /usr/share/dict/american-english
word list, which on
Debian and Ubuntu systems is ~100k words. If you have a word list of at least
262,144 words, you only need 4 words to build your passphrase.
- Length: 5 words
- Complexity: Easy to type
- Memorable: Easy to recall
$ shuf --random-source=/dev/urandom -r /usr/share/dict/words | head -n 5 | paste -sd '-'
McQueen's-pithily-biases-push-propelling
These three functions can be added to any Bourne-compatible shell rc-config file (Bash, KSH, ZSH, dash, etc.) on many UNIX-like systems (GNU/Linux, *BSD, macOS, Solaris, etc.).
These functions are not POSIX, per the comments:
shuff() {
# Tries to use a CSPRNG for shuffling
# /dev/urandom and /dev/stdin are not POSIX
if [ "$(command -v shuf)" ]; then
shuf -r -n "$1" --random-source=/dev/urandom
elif [ "$(command -v shuffle)" ]; then
# NetBSD uses arc4random_uniform() in src/usr.bin/shuffle/shuffle.c
shuffle -f /dev/stdin -p "$1"
else
awk 'BEGIN{
"od -tu4 -N4 -A n /dev/urandom" | getline
srand(0+$0)
}
{print rand()"\t"$0}' | sort -n | cut -f 2 | head -n "$1"
fi
}
gen_monkey_pass() {
# Generates an unambiguous password with at least 70 bits entropy
# Uses Crockford's base32
# /dev/urandom is not POSIX
i=0
[ $(printf "$1" | grep -E '[0-9]+') ] && num="$1" || num="1"
until [ "$i" -eq "$num" ]; do
i=$((i+1))
LC_ALL=C tr -cd '0-9a-hjkmnp-tv-z' < /dev/urandom | dd bs=1 count=14 2> /dev/null
echo # add newline
done | column
}
gen_xkcd_pass() {
# Generates a passphrase with at least 70 bits entropy
# /dev/stdin is not POSIX
i=0
[ $(printf "$1" | grep -E '[0-9]+') ] && num="$1" || num="1"
# Solaris, Illumos, FreeBSD, OpenBSD, NetBSD, GNU/Linux, OS X:
[ $(uname) = "SunOS" ] && file="/usr/dict/words" || file="/usr/share/dict/words"
dict=$(LC_ALL=C grep -E '^[a-zA-Z]{3,6}$' "$file")
size=$(printf "$dict" | wc -l | sed -e 's/ //g')
entropy=$(printf "l(${size})/l(2)\n" | bc -l)
words=$(printf "(70+${entropy}-1)/${entropy}\n" | bc)
until [ "$i" -eq "$num" ]; do
i=$((i+1))
printf "$dict" | shuff "$words" | paste -s -d '.' /dev/stdin
done | column
}
After saving, execute a new shell. Now execute the functions, with an argument to the number of passwords you want:
$ gen_monkey_pass
qb8s2f5rwqmtyy
$ gen_monkey_pass 10
6x54198cg3p4rf e5vxgndv498k5t 6paxks5r95feyt k51mr2bxx0021r 98ytcybrjvf3xv
37ny17bchgyb52 n5cdn42pgnecf4 k9x6w5tmymjayb sekfnj362vy027 x7z51d8hr5kesy
$ gen_xkcd_pass
Royce.mart.ached.Oslo.Sharpe
$ gen_xkcd_pass 10
gutsy.sump.parody.loofah.tout clap.Serbia.poked.snaked.blithe
boldly.mower.boot.heists.realty tortes.jam.yogi.list.cynic
free.nay.chides.Tanya.Crimea Lucia.minty.Seljuk.havens.fiddle
Bret.retry.teen.untold.logons poplar.clingy.flays.Vulcan.Styron
gig.runway.abut.Viking.Tucker meted.catnip.recopy.madman.ulcer
Personal utility written by me in Nodejs. Initially, I wrote a web-based password generator at https://ae7.st/g/, and open-sourced it at https://github.com/atoponce/webpassgen. Someone asked for a command-line version, so as they did not have to trust the browser. Because the bulk of the code was already written in JavaScript, I migrated the code to Nodejs, and https://github.com/atoponce/nodepassgen was born.
Clone the Git repository first, then create a symlink in /usr/local/bin
:
$ git clone https://github.com/atoponce/nodepassgen.git
$ sudo ln -s /path/to/nodepassgen/nodepassgen /usr/local/bin
Now execute it, hyphenating passphrases:
$ nodepassgen -H
Alternate(Trump): potential-1-Unified-she-Luis-Marco
Bitcoin(English): rail-scout-mammal-sniff-heavy-crowd-velvet
Diceware(English): coma-bonn-byers-lab-hairy-tapa
EFF(Short): usher-dawn-twine-aide-greet-most-sepia
Pseudowords(Bubble_Babble): xolac-rebix-vacam-fitus-rizox
Random(Base94): v7hiP5]|Hy=
For parsing, JSON formatting is supported which is useful for scripts:
$ nodepassgen -H --json
[
{
"Generator": "Alternate",
"Wordlist": "Trump",
"Password": ".@VP-seven-RNC-@HighonHillcrest-Waterbury-fifty",
"Characters": 42,
"Entropy": 77
},
{
"Generator": "Bitcoin",
"Wordlist": "English",
"Password": "potato-search-device-grid-bullet-disorder-festival",
"Characters": 44,
"Entropy": 77
},
{
"Generator": "Diceware",
"Wordlist": "English",
"Password": "sir-ordain-fawn-timid-crux-shu",
"Characters": 25,
"Entropy": 77
},
{
"Generator": "EFF",
"Wordlist": "Short",
"Password": "city-item-bulk-hunk-fable-volt-foe",
"Characters": 28,
"Entropy": 72
},
{
"Generator": "Pseudowords",
"Wordlist": "Bubble_Babble",
"Password": "xaruf-lakuz-haguv-rizis-xavax",
"Characters": 25,
"Entropy": 78
},
{
"Generator": "Random",
"Wordlist": "Base94",
"Password": "><8Ek8=s-1u",
"Characters": 11,
"Entropy": 72
}
]
Check nodepassgen --help
for options, such as changing the minimum entropy
level, hyphenating the passphrases, changing the dictionaries, and other
options.
The pwgen(1)
utility is nice, even if its defaults are lacking. Even though
it defaults to 160 passwords with no options passed, if placed in a shell
script, where STDOUT is not a tty, only 1 password is generated.
First make sure you have it installed:
$ sudo apt install pwgen
Uppercase, lowercase, and digits only. No special characters.
- Length: 12 characters
- Complexity: Frustrating to type on mobile
- Memorable: Moderate
$ pwgen -s 12 1
Oi6V0O9gwY38
This is where pwgen(1)
really shines. If you have a list of users, this can
be useful:
$ cat users.txt
user1
user2
user3
user4
user5
$ while read line; do pwgen -s 12 | sed "s/^/$line /"; done < users.txt
user1 Sk6yU47VsLl5
user2 wGef9E1QWWrm
user3 du9ucj11YPjY
user4 23PVw1ruiwhn
user5 lI2L0y09L33G
I'm not really a fan of apg(1)
. I find it's options overly complex and the
source code difficult to audit, especially with the "pronounceable" passwords.
To keep things simple, meeting our minimum 70-bits entropy requirement, just
copy/paste what's below.
First make sure you have it installed:
$ sudo apt install apg
All possible 94 graphical characters (not the <Space>
or <Tab>
) are
randomly chosen.
- Length: 11 characters
- Complexity: Very frustratirg to type
- Memorable: Difficult
$ apg -a 1 -m 11 -n 1
RXqX[f280:.
Lowercase, uppercase, and digits only. No special characters.
- Length: 12 characters
- Complexity: Frustating to type on mobile
- Memorable: Moderate
$ apg -a 1 -M NCL -m 12 -n 1
qOHZk2rJb7xI
Based on Crockford's base-32, which uses all 10 digits, then removes ambiguity
by removing i
, l
, o
, and u
.
- Length: 14 characters
- Complexity: Easy to type
- Memorable: Moderate, easy to read for vision difficulties
$ apg -a 1 -M LN -E iolu -m 14 -n 1
cdjvxh3x5sqppy