paint-brush
How I Manage My Passwords (Technical Version)by@0x0ece
3,118 reads
3,118 reads

How I Manage My Passwords (Technical Version)

by Emanuele CesenaSeptember 24th, 2017
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

<strong>Update: </strong>we released <a href="https://medium.com/@0x0ece/mempa-a-modern-deterministic-password-manager-2c0f28fa108b" target="_blank">MemPa, a modern deterministic password manager</a> that implements the ideas in this article.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - How I Manage My Passwords (Technical Version)
Emanuele Cesena HackerNoon profile picture

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.

Q: Do you really hash a password?!?!


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”.

Q: Why don’t you use a Key Derivation Function?!?!

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.

Algorithm

This is the concrete algorithm:

secret = format(hash( username, passphrase, counter, service ))

Where:

  • username, username or email used to log in
  • passphrase is the group passphrase, i.e. it’s the same for a set of services
  • counter is a counter to be able to generate new secrets if needed, and it’s almost always 0
  • service is the service, by convention the hostname stripped of “www”, e.g. pinterest.com — note that there is no strict enforcing, we can use “pinterest.com” even to log in into “pinterest.co.uk”
  • hash is sha256, chosen because it’s available in many environments — we’ll replace it with sha3 asap
  • format is a function that converts the output of hash into a “properly formatted” string. We decided to use base64, strip all non-alphanumeric characters, truncate to 12 chars (60-bit), and add a dash every 3 chars, e.g. “uBP-8Pe-5xM-mBe” (same format used by Safari)

Implementations

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.