Synchronized two-factor authenticated password manager
There are a couple of issues with standard password managers.
- Typically, the security on the mobile end (iPhone, Android) is not as secure as the desktop end.
- One-factor authentication — usually a password or passphrase, sometimes known as the "master password".
This means that someone who gains access to your computer or phone now has a decent chance of gaining access to all of your passwords (they can run offline dictionary and brute-force attacks). True, with a good master password, this isn't much of a threat — at least at present. But it's always good to be a little paranoid 😉 So let's get started, shall we?
What you'll need
I first got this idea when I bought a Yubikey. You will need a hardware token, preferrably one with NFC (for mobile) — I linked to Yubikey because that's what I have, but other hardware keys should work as well. You will also need to make sure your token can store GPG keys (if you're buying a Yubikey, make sure it supports GPG — check the description).
The solution I have come up with utilizes pass — the only version I know this works with is linux, since that is the platform I use. I have generally found that having a good command-line interface to the programs used here is helpful for debugging. Note that pass requires both gpg and git.
Setting up the software
Follow Yubico's instructions to get a key generated and onto the Yubikey. If you are using another hardware token, follow their instructions. Additionally, you will want to generate another key which will serve as a backup encryption key. After all, you want a way to access your passwords if you lose your hardware token, don't you? Also, note that you can generate a subkey (which is what I did) instead of a new primary key if you already have a primary GPG key. Generally speaking, you only want to have one GPG ID (identity, usually consisting of a name and email address) and generate subkeys (keys which share the primary key's ID) of that key for different purposes. Normally, when you generate your first key, you will have a primary key which is used for signing things and certifying other keys and a subkey for encryption. In my case, I have my primary key which is used for signing things and certifying other keys, a subkey for encryption, a subkey for signing things with my Yubikey, a subkey for encrypting things with my Yubikey, a subkey for authenticating things with my Yubikey, and a subkey for the backup encryption key. In the case that you do this, make sure you don't move your primary key to your hardware token (i.e. modify the instructions accordingly to select the right subkey).
Run pass init to generate a new password store. For the GPG IDs, you'll want to run gpg --list-secret-keys --with-subkey-fingerprints and copy the right IDs. If you're using subkeys, append an exclamation point to the end so that it uses that key (otherwise, it will default to the last generated key, which is no good!). You should now be all set with respect to the local end.
One of the nice things provided by a lot of password managers is synchronization between devices. But we can do that too! Create an empty git repository (Github or Gitlab will do) and make sure to mark it private — while it's true that everything is encrypted locally, there's absolutely no reason to make this a public git repo! Now, run pass git init to initialize the local git repository and pass git remote add origin <url> to set up the local git repo, where <url> is the URL for the remote git repo you set up in the first part of this paragraph. Now, you can do a pass git push -u --all to synchronize the local and remote repositories. If this step fails, you may need to resolve some conflicts manually (this can happen when Gitlab or Github decides to create files for you which aren't on the local end…).
There are multiple ways to do this. Ideally, there is a pass-compatible program for your platform (exists for both Android and iOS). Otherwise, you can download a git client and a PGP/GPG client and manually perform the two steps (sync and decryption). On Android, I recommend MGit and OpenKeychain if you want to take the more manual route. Either way, the crucial step is to ensure that the key used for encryption is never stored on the phone and only resides on the hardware token.
Using this setup
Phew! Now that the setup is all complete, we can actually start using this thing. First, a couple of notes:
- Always add new items to the password store from the computer. If you have followed the steps above, you will notice that pass will encrypt every GPG file with two keys. Attempting to do this on the phone will fail as the second key should not be available (it will only ever reside on the computer). In other words, not all devices are equal in this setup — one computer is the "master" device and all other devices receive propagated changes from it.
- As a corollary, this means that you should push from the master computer and pull to your phone and other devices. It should be possible to recover from pushing from your phone (especially if you don't pull to your master computer), but I have never tried it.
That out of the way, a typical workflow looks something like this:
- Insert a new password into the password store. Note that you can create categories, which are represented as directories in the password store hierarchy. So for example, running pass insert Financial/MyBank would create a file called MyBank.gpg inside the Financial directory under the ~/.password-store directory (where everything is stored). You can use the -m option to insert more than one line.
- GPG should prompt you for the PIN for your hardware token when you end the insertion. Once all of the passwords are entered, do a pass git push to sync with the remote repo.
- On the phone, open the git client and pull from the remote repo. This should update the passwords.
- When the password needs to be accessed on Android, open the GPG file in OpenKeychain and decrypt it using the hardware token. When the password needs to be accessed on the computer, just run pass show Financial/MyBank and enter the PIN when prompted. Using -c (at least on linux) will copy the password to the clipboard and auto-erase it after 45 seconds.
In general, this is not too cumbersome. The most annoying part is how finicky the NFC reader can sometimes be, which can sometimes slow down accessing the password. In order for pass and its cousins to be most useful, I would recommend using the first line to put just the password and use the rest of the file for additional metadata (username, email address, website, security questions, etc). I had initially used various other formats, and while pass itself isn't too picky, the Android version (at least) assumes that the first line is the password. Various pass extensions assume this as well, which is why it might be most useful to just adopt that convention.