Update: we released MemPa, a modern deterministic password manager that implements the ideas in this article.
In the past year, I refined a method to handle my passwords as I had a major issue with existing managers: there are some passwords — Google, banks — that I never want to store, yet I always want to access.
I started to share my idea in a close group, onboarded a handful of friends, and now I think it’s time to open it and see what you think.
It’s a deterministic password manager designed for usability. For every service (site or app), we generate a unique secret (the password stored in the service) from a passphrase (something to know). We only have to remember a single passphrase, or a very small number of them— personally I have a default one, one for important sites (Google, Facebook, …), and one for banks. This is the high-level algorithm:
secret = ...hash( ...passphrase, service... )
And these are the FAQ.
No. We’re not “hashing a password to securely store it”, which would be wrong. Secret is the password, and: 1. we never store it, 2. the service will store it password-encrypted. (hopefully!)We are “hashing to generate a random bit string”.
A KDF requires a salt, which is something that can’t be remembered, and this is a deal-breaker. The main goal is to be able to reconstruct any secret with only information that we can remember.
This is the concrete algorithm:
secret = format(hash( username, passphrase, counter, service ))
Where:
In Javascript (if you’re on Chrome, open the dev console and paste it):
document.head.appendChild(document.createElement('script')).src='https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.min.js';
CryptoJS.SHA256('user:passphrase:0:example.com').toString(CryptoJS.enc.Base64).replace(/[^0-9A-Za-z]/g, '').substring(0, 12).match(/.{3}/g).join('-');
In Python 2/3:
import base64, hashlib, re
h = hashlib.sha256(); h.update(b'user:passphrase:0:example.com'); '-'.join([re.sub(r'[^0-9A-Za-z]', '', base64.b64encode(h.digest()) .decode())[i:i+3] for i in range(0, 12, 3)])
Edit: my friend Paolo built his own Python app!
In bash:
echo -n 'user:passphrase:0:example.com' | openssl dgst -sha256 -binary | openssl base64 | sed -e 's/[^0-9A-Za-z]//g' | egrep -o '.{3}' | head -4 | paste -sd'-' -
This is the kind of usability we want. Parameters and core algorithm are easy enough to remember, and the cryptographic functions (sha256, base64) are widely available.
The one-liners above are just fancy add-ons, I personally don’t remember them exactly, but once I’ve run sha256 and base64 (in the example, getting: “uBP8Pe5xMmBeeOpLEAsG+PpkPBkBBmVoRBaaVofwis0=”), I can easily derive the formatted string “uBP-8Pe-5xM-mBe” manually.
I’ve started this as an experiment with only a few passwords, and now I’m using this method for most of my websites and apps.
It clearly doesn’t work with existing secrets, nor with passwords that you have to type very frequently, such as the one to unlock my laptop. In practice, I didn’t experience other “apparent” limitations, for example the format of the secret is easy to adapt — and remember — for sites with special requirements.
Next, I’m looking into building an app for managing secrets, to make it easier to copy & paste on mobile. All preserving the core idea, that some passwords should never be stored, yet always accessible. (even without the app!)
Time to hear from you: what do you think? Do you like this algorithm and do you see yourself using it? If you have critiques, are these on deterministic password managers in general, or specific to this one? If you like it “enough”, do you feel anything is missing? Any feedback is more than welcome, below in the comments, on Hacker News, or on Twitter @0x0ece.
Update: check out MemPa, a modern deterministic password manager that implements the ideas in this article.