I use Mutt for most of my gmail needs for a number of reasons, including spam is usually HTML-only, while (actually important) emails written by humans always have a readable text-only format. My hubris also assures me that I’m reasonably insulated from email worms.
Configuring Mutt out of the box requires you to put your plaintext password in a
~/.muttrc file, which is asking for all sorts of problems. The following is how I use a hardware Yubikey to protect my Gmail password, and then sync that password across machines.
Yubikey setup with GnuPG
If you don’t already have one, get a Yubikey. Here are some options:
- Yubikey 5C Nano - USB-C recommended
- Yubikey 5C - USB-C
- Yubikey 5 Nano - USB-A, for older macbooks.
- Yubikey 5 NFC - USB-A + NFC
While waiting for this to arrive, you can continue with a GPG key on your hard drive; just make sure you protect it with a passphrase, which GPG will strongly encourage, because otherwise there’s no point.
Migrating your existing key to the card
If you’ve created your key already, migrate it to the card. Most people just have one master key, and one encryption subkey; if you run
gpg --list-secret-keys, you may see something like this:
/Users/philihp/.gnupg/pubring.kbx --------------------------------- sec rsa4096/0x5B640B9F9600F122 2016-02-29 [SC] [expires: 2021-02-07] 427E032939DB40F29D03D80F5B640B9F9600F122 uid [ultimate] Philihp Busby <email@example.com> ssb rsa4096/0x0D86EF2BF0DA842E 2016-02-29 [E] [expires: 2021-02-07]
- On the left the
secmeans “i have the secret key”
ssbmeans “i have the secret subkey”.
- If either of these says
sub, it means “i just have the public key”, and that’s a problem.
- The third column is the date the key was created, which is relevant for
In the brackets in the 4th column, you can see
[SC] for the master key meaning it is meant for the “Signing” usage and the “Certification” usage, and
[E] for the subkey meaning it is meant for “Encryption”. I think it’s not a bad idea to create another subkey for “Authentication” or add that usage to an existing key, but important: , but there are two important points:
- You can have any number of
Ssigning keys or
- You should only have one key with the
Eusage. When a message is encrypted, GPG uses the newest
Esubkey, i.e. the one with the last creation.
To move these to your Yubikey, run the command
gpg --edit-key 5B640B9F9600F122
5B640B9F9600F122 is your key… it’s actually my key; I don’t know your key. You could tell me it, though, I’d love to know if this helped you. So if you run that, you’ll be dropped into another console
gpg (GnuPG) 2.2.20; Copyright (C) 2020 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Secret key is available. ... ... gpg>
Useful commands here:
help, for common commands;
list to show your key,
key N, to select a subkey where N is the index number of the key starting with 1, and
keytocard to move the selected key to the card.
!> If you don’t have a key selected,
keytocard will move the master key.
keytocardwithout a key selected to move your master key into the Signing slot of your Yubikey.
gpg> keytocard Really move the primary key? (y/N) y Please select where to store the key: (1) Signature key (3) Authentication key Your selection? 1 Replace existing key? (y/N) y
keytocardto move your encryption subkey into the Encryption slot of your Yubikey
gpg> key 1 ... sec rsa4096/0x5B640B9F9600F122 ... ssb* rsa4096/0x0D86EF2BF0DA842E ... ssb rsa4096/0xFD8194C54A63DBD5 ... gpg> keytocard Please select where to store the key: (2) Encryption key Your selection? 2
- If you have another subkey with Authentication, or you added that usage to your first key using
change-usage, move this into the Authentication slot of your Yubikey.
saveto save your key.
Should display something like
sec> rsa4096/0x5B640B9F9600F122 2016-02-29 [SC] [expires: 2021-02-07] 427E032939DB40F29D03D80F5B640B9F9600F122 Card serial no. = 0006 09123456 uid [ultimate] Philihp Busby <firstname.lastname@example.org> ssb> rsa4096/0x0D86EF2BF0DA842E 2016-02-29 [E] [expires: 2021-02-07] ssb> rsa4096/0xFD8194C54A63DBD5 2020-06-13 [A] [expires: 2021-02-07]
Some things that changed here:
sec>, meaning “I know where to get the private key”
- There’s a line with
Card serial no. = 0006 09123456meaning “It’s on a smart card with this serial number.
- These are called Stub Keys.
- There’s a second subkey (which I added but did not document above, this is optional but useful if you want to use
gpg-agentas a replacement for
Similarly, if you run
You should see something like
❯ gpg --card-status Reader ...........: Yubico YubiKey FIDO CCID Application ID ...: D2760001240102010006091234560000 Application type .: OpenPGP Version ..........: 2.1 Manufacturer .....: Yubico Serial number ....: 09123456 Name of cardholder: Philihp Busby Language prefs ...: en Salutation .......: URL of public key : https://philihp.com/pgp.asc Login data .......: philihp Signature PIN ....: forced Key attributes ...: rsa4096 rsa4096 rsa4096 Max. PIN lengths .: 127 127 127 PIN retry counter : 3 0 3 Signature counter : 42 Signature key ....: 427E 0329 39DB 40F2 9D03 D80F 5B64 0B9F 9600 F122 created ....: 2016-02-29 07:34:57 Encryption key....: C54A 7C6F 8B38 A89F 3102 4BAB 0D86 EF2B F0DA 842E created ....: 2016-02-29 07:34:57 Authentication key: D5CB FB11 287E 0B3A 287D F591 FD81 94C5 4A63 DBD5 created ....: 2020-06-13 04:53:06 General key info..: pub rsa4096/0x5B640B9F9600F122 2016-02-29 Philihp Busby <email@example.com> sec> rsa4096/0x5B640B9F9600F122 created: 2016-02-29 expires: 2021-06-12 card-no: 0006 09123456 ssb> rsa4096/0x0D86EF2BF0DA842E created: 2016-02-29 expires: 2021-02-07 card-no: 0006 09123456 ssb> rsa4096/0xFD8194C54A63DBD5 created: 2016-11-08 expires: 2021-02-07 card-no: 0006 09123456
Alternatively, creating a new key on the card
If you don’t have a GPG key, or you don’t want to reuse it, this is simpler.
Should bring you into a console for tinkering with the card. The
help command will show you most of the common commands anyone should need. Most of the fun stuff is enabled with the
admin command, which you should do.
gpg/card> admin Admin commands are allowed
From there, you might want to use the
factory-reset command, or at least know it’s there.
passwd will let you change the PIN (default: 123456), or the Admin PIN (default: 12345678).
Run the following two commands:
gpg/card> admin Admin commands are allowed gpg/card> generate
It will ask you if you want an off-key backup, which it’s not a bad idea to do, and take the file in
~/.gnupg/sk_????????????????.gpg and the similar file in
~/.gnupg/openpgp-revocs.d/*, and copy those to a reliable and secure location. Treat these like you would treat your passport. You can get another, but you really don’t want to because you will lose all of your visas and history.
Test it out
You should now be able to sign something like a git commit… Configure it like this (with your own key, of course)
git config --global commit.gpgsign true git config --global user.signingkey 427E032939DB40F29D03D80F5B640B9F9600F122 git config --global log.showSignature true
Then go commit somewhere; it should ask you for a pin. Then
git log and you can see your signature.
commit a7558903018908258386c9cdabca70e47c6aed24 (HEAD -> master, origin/master) gpg: Signature made Fri Jun 12 03:26:18 2020 GMT gpg: using RSA key D5CBFB11287E0B3A287DF591FD8194C54A63DBD5 gpg: Good signature from "Philihp Busby <firstname.lastname@example.org>" [ultimate] Author: Philihp Busby <email@example.com> Date: Fri Jun 12 03:26:18 2020 +0000 Committing like a pro!
The UI you get for PIN entry is a ncurses-driven text-based PIN entry. This can cause issues with other command line programs, or weird behavior if you commit with a GUI.
I like to use a GUI-driven PIN entry as another layer of being sure I’m not being spoofed into unlocking my Yubikey; setting that up is as simple as
brew install pinentry-mac echo "pinentry-program /usr/local/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
and then kill any existing gpg-agent process.
Now that that’s sorted, it’s time to setup Passwordstore! Passwordstore is a 719 line shell script that fulfills a lot of the same functions as any commercial password manager. It’s a posterchild for the Unix philosophy by delegating encryption bits to GPG, and delegating the syncing of filesystems with nonlinear history to git. When decrypting a password, it gives it back to you in STDOUT, so you can pipe it as well. And it’s short, so if that’s not enough for you, or you’re wondering how it works (as I once did when I wanted to use it in a team setting, which it does very well), you can read it and get a pretty good grasp on it. I love it. Setting it up is pretty easy. On macOS, just run
brew install pass
pass init 427E032939DB40F29D03D80F5B640B9F9600F122
This will create a folder
~/.password-store, with a file
.gpg-id of with your ID in it. If you want other people (perhaps people on your team) to be able to decrypt the files here too, add them as a new line.
Now go to your Google Account and create an App Password. You should be given a 16 character lowercase password. Tell that to Pass with
pass insert firstname.lastname@example.org
Then if you go to
~/.password-store/gmail.com/ you should see a file
email@example.com. All this is is a text file with your password, encrypted. Test out decrypting it with:
gpg --decrypt ~/.firstname.lastname@example.org
Another way to get that is with
or if you were going to paste this somewhere and didn’t want it displayed to the screen,
pass -c email@example.com
Cool. Now you can programmatically request your own password from the command line. Now if we had a highly configurable email client, we could tell it a command that goes and gets your password so it can login and show you your email.
If you want to sync this with another machine, you can run
pass git init pass git remote add origin firstname.lastname@example.org:password-store.git pass git push
And on your other machines, instead of reconfiguring this, you can just say
git clone email@example.com:password-store.git ~/.password-store
Configuring Mutt to use Passwordstore
Install mutt (or neomutt) with
brew install mutt
Then configure it by creating a file
set realname = "Philihp Busby" set from = "firstname.lastname@example.org" set use_from = yes set envelope_from = yes set smtp_url = "smtps://email@example.com@smtp.gmail.com:465/" set smtp_pass = `pass show firstname.lastname@example.org` set imap_user = "email@example.com" set imap_pass = `pass show firstname.lastname@example.org` set folder = "imaps://imap.gmail.com:993" set spoolfile = "+INBOX" set ssl_force_tls = yes
!> Notice the back-ticks on the
imap_pass. That’s important, because it tells mutt that rather than “this is the password”, it says to execute that and it will give you back the password.
Now startup mutt with
If doesn’t manage to get your gmail password with this command, it will ask you for it.
I hope I didn’t expose myself by writing this, and admittedly this is a very unique snowflake for email; most people just use a browser on gmail.com. This setup has evolved over a couple years, and works very well for me.
There are some more things you can configure, which are not simple with other setups:
Automatically sign and verify signatures, or encrypt and decrypt emails (but perhaps don’t always sign)
gpg-agentand require your Yubikey to SSH or SCP.
Configure your Gmail to use your Yubikey to login to the web
Tell Github about your PGP key, so you’ll get a fancypants “Verified” badge next to your commits.