Last weekend, I finished up a project that had been in the back of my mind for a while. The project was not an original idea; it's a password manager. At its most basic, a password manager is an encrypted volume in which you store your myriad unique passwords for all the various services you use. Many find that a text file and GPG is sufficient, but most prefer the usability improvements that utilities such as LastPass, KeePass(X), and 1Password provide. However, these all have problems that I could not easily ignore. LastPass is centralized and closed source, leaving you trusting an unauditable service that holds all your data. 1Password is also closed source. KeePass is a monstrously complex application, complete with support for plugins, browser integration, and dozens of other features, all of which are incidental to the main function of a password manager and are potentially exploitable. All of the above store the entirety of your credentials in memory for long periods of time.
After feeling disillusioned with the current offerings of password managers, a colleague introduced me to pass, a password manager inspired by the UNIX philosophy.
pass is clearly a step closer to what I wanted out of a password manager. Unfortunately, pass also failed a cursory security audit. An attacker with access to your
.password-store data directory learns:
- How many passwords you have
- The names for each password (usually these are the sites associated with the passwords)
- The usernames associated with each name
Exasperated, I decided to write masterkey. The goals were now well defined:
- Provide a simple, convenient interface for securely storing passphrases and other secrets
- Use existing, well-audited, hard to mess up cryptography.
- Write clean, well tested, readable and easily auditable code.
- Assume a more aggressive threat model than most other password managers. Specifically, avoid storing decrypted credentials in memory, and don't leak metadata.
Completing these goals represented a quantifiable improvement in terms of security over the popular password managers, and a quantifiable improvement in usability over the bare bones GPG-textfile method.
masterkey is operated via a REPL, a type of interactive command line interface. Like other password managers, the user only has to enter their master passphrase once at the beginning of the session. I took some inspiration from Cobra, and built a small package that creates an interactive prompt and can register arbitrary commands and pass arguments to those commands. The code is available here (handy godoc link), and may serve to be useful for others looking to build interactive CLI applications using Go. The secure stateful nature of
masterkey's vault means it is also amendable to a GUI, and will likely be endowed with a web interface at some point.
The Core Algorithm
masterkey uses NACL for authenticated symmetrical encryption and Scrypt for key derivation. A random nonce is generated every time a masterkey vault is created. This nonce is used to encrypt the data and to salt the user's passphrase. The resulting derived key, encrypted data, and nonce are then stored in memory.
masterkey uses a REPL to provide an interface to interact with the vault, and each time the vault is accessed it is decrypted and re-encrypted. The result of this algorithm is that an attacker who somehow gains access to the memory of
masterkey only knows how to decrypt the vault for the current session, since the nonce changes each time the vault is used and only the derived secret key is stored in memory, not the passphrase. The attacker is also faced with the challenge of determining exactly where in memory the secret key and the encrypted data are, which is a nontrivial task since both are cryptographically random. In comparison, an attacker may simply string dump 1Password or KeePassX and use simple regular expressions to extract all credentials.