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.