45

What's a good one-liner to generate an easily memorable password, like xkcd's correct horse battery staple or a Bitcoin seed?

EDIT 1:

This is not the same as generating a random string since random strings are not at all memorable. Compare to the obligatory xkcd...

3

10 Answers 10

45

First of all, install a dictionary of a language you're familiar with, using:

sudo apt-get install <language-package>

To see all available packages:

apt-cache search wordlist | grep ^w

Note: all installation instructions assume you're on a debian-based OS.

After you've installed dictionary run:

WORDS=5; LC_ALL=C grep -x '[a-z]*' /usr/share/dict/words | shuf --random-source=/dev/urandom -n ${WORDS} | paste -sd "-"

Which will output ex:

blasphemous-commandos-vasts-suitability-arbor

To break it down:

  • WORDS=5; — choose how many words you want in your password.
  • LC_ALL=C grep -x '[a-z]*' /usr/share/dict/words — choose only words containing lowercase alphabet characters (it excludes words with ' in them or funky characters like in éclair). LC_ALL=C ensures that [a-z] in the regex won't match letter-like symbols other than lowercase letters without diacritics.
  • shuf --random-source=/dev/urandom -n ${WORDS} — chose as many WORDS as you've requested. --random-source=/dev/urandom ensures that shuf seeds its random generator securely; without it, shuf defaults to a secure seed, but may fall back to a non-secure seed on some systems such as some Unix emulation layers on Windows.
  • paste -sd "-" — join all words using - (feel free to change the symbol to something else).

Alternatively you can wrap it in a function:

#!/bin/bash

function memorable_password() {
  words="${1:-5}"
  sep="${2:--}"

  LC_ALL=C grep -x '[a-z]*' /usr/share/dict/words | shuf --random-source=/dev/urandom -n ${words} | paste -sd "$sep"
}

or

#!/bin/sh

memorable_password() {
  words="$1"
  if [ -z "${words}" ]; then
    words=5
  fi

  sep="$2"
  if [ -z "${sep}" ]; then
    sep="-"
  fi

  LC_ALL=C grep -x '[a-z]*' /usr/share/dict/words | shuf --random-source=/dev/urandom -n ${words} | paste -sd "$sep"
}

Both of which can be called as such:

memorable_password 7 _
memorable_password 4
memorable_password

Returning:

skipped_cavity_entertainments_gangway_seaports_spread_communique
evaporated-clashes-bold-presuming
excelling-thoughtless-pardonable-promulgated-forbearing

Bonus

For a nerdy and fun, but not very secure password, that doesn't require dictionary installation, you can use (courtesy of @jpa):

WORDS=5; man git | \
  tr ' ' '\n' | \
  egrep '^[a-z]{4,}$' | \
  sort | uniq | \
  shuf --random-source=/dev/urandom -n ${WORDS} | \
  paste -sd "-"
7
  • 6
    There is little sense on installing a word list in Cyrillic (for example) if all Cyrillic characters are going to be rejected by the [a-z] range (in the C locale).
    – user232326
    Sep 21, 2018 at 19:03
  • 4
    If wordlist is not available, you can use man git | tr ' ' '\n' | egrep '^[a-z.]+$' for suitably nerdy passwords. Such as "help path objects control another" or "index option foo.bar of force".
    – jpa
    Sep 23, 2018 at 19:05
  • Hahaha! That's brilliant! Although you might want to remove words shorter than 3 letters with man git | tr ' ' '\n' | egrep '^[a-z.]{4,}$'. And then add the usual suffix giving you a complete: WORDS=5; man git | tr ' ' '\n' | egrep '^[a-z.]{4,}$' | shuf --random-source=/dev/urandom -n ${WORDS} | paste -sd "-". Note: It's not very secure, but it's such a fun way of achieving it! (adding it to the answer)
    – meeDamian
    Sep 24, 2018 at 2:32
  • 7
    @Braiam A 4 character password, except each there could be 100000 different values for the character. log2(100000^4) is over 60 bits.
    – Hugoagogo
    Sep 24, 2018 at 7:42
  • 1
    @Björn Basically WORDS get scoped to the whole line, while LC_ALL only to the initial grep. See this as an example: A=dog; B=cat echo "cat\ndog" | grep "^\($A\|$B\)$"
    – meeDamian
    Sep 24, 2018 at 10:45
37

I don't do this with standard utilities that are not designed with cryptographic use in mind. There is no reason to believe they're using a csPRNG or that they're seeding it properly, and someone who knows your method will be able to reproduce your passphrases. Likewise, behave of multi-purpose utilities if you aren't sure how to use them properly.

pwqgen from passwdqc.

3
  • 4
    +1. I think this should be the accepted answer, and would add a couple of links, "Strong Passwords" or "Strength of a password generation process" and Project description xkcdpass. -- xkcdpass uses crytographically strong random number generators where possible (provided by random.SystemRandom() on most modern operating systems). From version 1.7.0 falling back to an insecure RNG must be explicitly enabled
    – sudodus
    Sep 21, 2018 at 7:02
  • 1
    "Dont leave Randomness to Chance" apg or AdvancedPasswordGenerator generates pronounceable passwords not in the dictionary. yum install apg or apt-get install apg apg -t Yes, it is in the bash on windows shell.
    – rjt
    Sep 21, 2018 at 22:10
  • 11
    Thankfully /dev/urandom is designed with cryptographic use in mind.
    – Joshua
    Sep 22, 2018 at 2:37
13

You need a wordlist dictionary, since you mention bitcoin, most likely you want this one:

https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt

If your native language is not English, there are wordlists for other languages available in the same repository.

Given this english.txt file, you can do a random selection with shuf:

$ shuf -n 4 english.txt 
anchor
neck
trumpet
response

Note you'll need way more than just 4 words for a truly secure passphrase. 4 words is for an online service where the number of attempts an attacker might do is very limited. I think the bitcoin recommendation is 16 words, not sure.

Also in this example, each word may only appear once. If you wish to allow repeated words, add the --repeat option:

$ shuf -n 4 -r english.txt

That would allow each word to appear more than once.

If you want the output in one line, you can just add xargs echo:

$ shuf -n 4 english.txt | xargs echo
math easily cube daughter

Or if you prefer command substitution:

$ echo $(shuf -n 4 -r english.txt)
photo milk roast ozone

On a sidenote, I don't find this style of password to be easily memorable.

Well, actually I got very lucky with math easily cube daughter since that just happens to make it easy to think of a story where your daughter can easily do math with cubes or whatever. It's something humans can relate to, as is the horse in XKCD's example.

But what the heck is a anchor neck trumpet response? I'm not a comic book author with creativity to spare to come up with a mnemonic for that. So it will be forgotten in no time.

Even if you can remember the words, it's hard to remember their correct order. Was it math easily cube daughter or daugher easily math cube or something else?

And the password is supposed to be random, you're not allowed to pick and modify it.


As for bitcoin seeds, you're not really supposed to remember them. This is just a way to be able to write it down easily. Anyone can write down 16 words on a piece of paper and read them back correctly; with random letters it's much more likely to make mistakes.


If you have concerns about the randomness of shuf, add the --random-source=/dev/urandom parameter to all shuf commands.

See also https://www.gnu.org/software/coreutils/manual/html_node/Random-sources.html#Random-sources

12
  • To allow repeated words, an alternative would be repeated invocation of shuf -n 1. That being said it might not be the worst idea to avoid suggesting a method that would allow a passphrase with only one distinct word.
    – David Z
    Sep 20, 2018 at 20:00
  • @DavidZ you're right, of course. But then you have a loop and while you can write them in one line, I really prefer not to. But I just found out that shuf also has a --repeat option. Solves the issue nicely, as it allows words to be repeated any number of times. Sep 20, 2018 at 20:28
  • Ah, of course, that is better.
    – David Z
    Sep 20, 2018 at 20:32
  • 3
    Is the shuf command using a cryptographic random number source? Otherwise this is highly flawed. Sep 21, 2018 at 2:38
  • 1
    @R.. shuf uses /dev/urandom or anything you like with --random-source. Can be useful if you need to shuffle related files the same way: unix.stackexchange.com/a/220394/30851 Sep 21, 2018 at 8:18
9

I have this:

xkcd_password () { 
    command grep "^[[:alpha:]]\{5,8\}$" "${1-/usr/share/dict/words}" | sort --random-source=/dev/urandom -R | head -4
}
9
  • Why {5,8}? It cuts down the number of usable words which lowers entropy. Word length isn't really important, only the number of words. Sep 20, 2018 at 20:50
  • 3
    I know, but shorter words are easier (for me) to remember. The EFF link I posted above recommends more shorter words as an alternative technique Sep 20, 2018 at 20:52
  • 2
    What sort of seed and prng does sort -R use? Probably not something suitable for this use. Sep 21, 2018 at 2:38
  • 2
    @R.. sort -R, like shuf, uses ISAAC, which is generally accepted as a good CSPRNG even if it isn't very popular. Since coreutils 8.6, which was released in October 2010, the CSPRNG is seeded from /dev/urandom if available. Sep 21, 2018 at 11:46
  • 1
    @Gilles: Is there only one implementationf of sort providing -R? Otherwise there's a risk of inadvertently using one where it's not a csprng. Sep 21, 2018 at 12:47
8

Try this,

shuf --random-source=/dev/urandom -n5 /usr/share/dict/words | sed 's/[^[:alnum:]]//g' | paste -sd_

Output:

Quayles_knockwursts_scrotums_Barrie_hauler

Explanation:

  • shuf --random-source=/dev/urandom -n5 /usr/share/dict/words Get 5 random words from dict file
  • sed 's/[^[:alnum:]]//g' Remove non-alphanumeric characters
  • paste -sd_, join the words with underscore (_)

You can use any wordlist file from the Internet instead of /usr/share/dict/words.

Install the wordlist if you don't have it. It might be be in your distros repository, e.g. https://packages.debian.org/sid/wordlist:

sudo apt install wamerican
4
  • It's very likely /usr/share/dict/words doesn't exist on your system and some dictionary needs to be installed first.
    – meeDamian
    Sep 20, 2018 at 18:23
  • @meeDamian words is a standard file on all Unix and Unix-like operating systems. I work on an embedded system that doesn't include it, but otherwise it seems pretty ubiquitous. Sep 20, 2018 at 22:23
  • It doesn't exist on Raspbian nor any VPS I have
    – meeDamian
    Sep 21, 2018 at 5:37
  • 1
    I added a hint to installation of the wordlist or to you use a different.
    – pLumo
    Sep 21, 2018 at 6:59
4

As suggested by @R.. but for RHEL/CentOS machines (available in the EPEL repo):

pwgen

Note this excerpt from pwgen's manpage (emphasis mine):

The pwgen program generates passwords which are designed to be easily memorized by humans, while being as secure as possible. Human-memorable passwords are never going to be as secure as completely completely random passwords.

4
  • 1
    Sure, you can cram more bits of randomness into a password by using all printable characters. But importantly passwords don't need to be as complex as you can possibly make them - they need to be complex enough that they are not the weakest link in the security scheme. And 44 bits is not bad. If you want more, words from a bigger dictionary (>~2,000 words) should be memorable for native speakers.
    – l0b0
    Sep 22, 2018 at 8:08
  • 1
    This is incorrect: Human-memorable passwords are never going to be as secure as completely completely random passwords. Increasing the length will match any finite random requirement.
    – user232326
    Sep 23, 2018 at 4:36
  • I'm assuming the manpage refers about passwords of equal length.
    – dr_
    Sep 24, 2018 at 6:11
  • @Isaac that's only true with very long passphrases, which given that they need to be random, would be difficult to memorize. For the adversary, splicing strings is cheap, doesn't matter whenever is the alphabet or wikipedia data dump.
    – Braiam
    Sep 24, 2018 at 6:28
1

If you want it to be even easier to memorize, you can also use the password hashing approach (remember a single passphrase to generate many other passphrases from). Instead of memorizing hundreds of passphrases, you just memorize the method to generate them.

Using shuf with openssl to provide a seeded random source, you can re-generate passphrases as needed. Please note that the security of this approach depends on the strength of your passphrase alone, so make it count.

In the following example, the random seed depends on

  • your passphrase (obviously)
  • a designated purpose (user@site, wallet#number, whatever)
  • the wordlist used (hash thereof)
  • number of words requested

Change any of these and you get a different result.

get_seeded_random() {
    seed=$(printf "%s:" "$@")
    openssl enc -aes-256-ctr -pass pass:"$seed" -nosalt \
    < /dev/zero 2>/dev/null
}

get_random_words() {
    dictionary=$1
    number=$2
    passphrase=$3
    purpose=$4

    dictionary_hash=$(sha1sum < "$dictionary")

    shuf -n "$number" \
         --random-source=<(get_seeded_random
            "$passphrase" "$purpose" "$dictionary_hash" "$number") \
         "$dictionary" \
    | xargs echo
}

So, if my passphrase was WienerSchnitzel (not a good choice for obvious reasons), and if I used XKCD style passwords everywhere:

$ get_random_words english.txt 4 WienerSchnitzel wallet:1
robust lottery ugly stone
$ get_random_words english.txt 4 WienerSchnitzel wallet:2
vapor comfort various bitter
$ get_random_words english.txt 4 WienerSchnitzel [email protected]
any actor tobacco tattoo

And you can execute it multiple times, always gives the same result:

$ get_random_words english.txt 4 WienerSchnitzel wallet:1
robust lottery ugly stone
$ get_random_words english.txt 4 WienerSchnitzel wallet:1
robust lottery ugly stone

But requesting one more word is a completely different result (as would be using a different dictionary, etc.):

$ get_random_words english.txt 5 WienerSchnitzel wallet:1
crash category extra hollow cloud

The downside of this approach is that, if anyone knows you're using this method, they can try to brute-force your secret passphrase and then proceed to generate all your other passphrases.

In this example, it doesn't take long to guess WienerSchnitzel. To improve this, you'd need to apply an expensive (but repeatable) hash to the passphrase itself.

# poor man's expensive hash replacement
seed=$( (echo "$seed" ; head -c 1G /dev/zero) | sha1sum)

And just use a much, much better passphrase in the first place.

You could also hard-code a truly random high-entropy password, but that completely defeats the "easily memorable" aspect of things.

Also, this implementation depends on shuf always selecting words the same way, which may not be the case for future versions in the long term.

0

For completeness:

git clone [email protected]:victor-engmark/xkcd-passphrase-generator.git
cd xkcd-passphrase-generator
./generate.sh
0

In a nutshell:

random.sample(open("/usr/share/dict/words").readlines(), 4)

Produces e.g.:

['controverts\n', "Queensland's\n", 'plaids\n', 'aback\n']

Full code would be a bit longer, smth like,

import random; print("".join(random.sample(open("/usr/share/dict/words").readlines(), 4)))
Weyden
Geronimo's
Jidda
enumerate

On the upside, you occasionally get perls like this:

polyamory
replicates
unmarried
diseases

On the downside, you might get words that are hard to spell or even type:

fezzes
Lumière's
tercentenary's
Liliuokalani

And of course, if you were to pick one output out of the several options, you narrow down the search space dramatically, so you have to stick to first (i.e. truly random) set of words.

1
  • As @meeDamian pointed out, PRNG source must be secure, and also PRNG algorithm must be secure, thus random is a bit of a misfit.
    – user141424
    Sep 24, 2018 at 1:57
0

xkcdpass is a simple way to get this without writing one-liners.

$ xkcdpass 
optimal mycology Kumamoto thorny chrism unsavoury

There are options to set number of words, etc.

To install:

  • Linux/Unix/macOS: pip install xkcdpass (you may need to sudo).
  • If you don't have Python / pip, look for packages like python and python-pip, for Python 2 or 3.

Not the answer you're looking for? Browse other questions tagged .