Sunday, September 23, 2012

Automatically pgp-sign outgoing email with mutt

There's very little I miss about Evolution. But, yeah, I have my weak moments. Occasionally I'll try to start it up, get frustrated and lose a few hours to the mental trap of "Aw, c'mon, it can't suck this much!". It does. When you're using it purely to interact with an Exchange server, in particular E2003 and E2010 (the two I've been compelled to interact with at work), it does. When you'd really rather be using an email client that positively revels in the simplicity of its interface, it does. On the other hand, everyone knows mutt sucks. It just happens to suck less. Except in two and a half ways (I'll get into that later) it sucks just a little bit more than Evolution.
  • Automatically signing outgoing email with your pgp key
  • Interacting with calendars
  • Interacting with calendars (yeah, this is two things, I'll post on it someday, too)
Okay, so the thing is, mutt can do what I want in terms of signing email, it just takes a little bit of doing. First, though, the problem statement.
I want to be able to have a single pgp key selected by default that will be used to sign all outgoing email from me and I only want to enter that key's passphrase when I start mutt.
Yes, yes, I know. That last part is a bad idea, I should have it time out or I should just use the built-in features in mutt to cache that information and be done with it. I'm aware of the risks I'm opening myself up to here, it's just the way I roll. Livin' on the edge, baybee. So we'll start with getting the preliminary bits out of the way. You need a key for starters. Odds are if you're using a modern Linux at all you've already got some of the required GNU Privacy Guard (gpg) tools installed. I'm not walking you through creating keys (maybe some other time), but you'll want to do something like this:

skynet ~ gpg --gen-key

And answer the questions then enter a reasonably good passphrase. Let's not even start down the "what makes a good passphrase" road. Firmly out of scope. If you've already got a key you want to use, then you'll do something like this:

skynet ~ gpg --list-keys

And find the one you're interested in using as your default from the output. In my case, I want to use my personal one: 5A20D17C. That's nothing private, you can get that info from here, for example. Now that you've got a key you want to use, time to the basic part of the mutt setup. A quick look here shows you already have options available. So turn them on. For reasons I may get into later, I keep them in a file named .user-mutt-config, but the go just fine in your .muttrc

# specify the uid to use when encrypting/signing
set pgp_sign_as=0x5A20D17C
# automatically sign all outcoming messages
set crypt_autosign


Phase one, complete. There's a lot more options available on the UseGPG page I linked above, but you don't need any of them if all you want to do is sign email and verify other signatures. Now every time you compose an email, mutt will have your selected key queued up and when you hit 'send' it'll attempt to use that key to sign your message. If you're well-behaved it'll now ask you for your pgp passphrase, which is the very thing I'm trying to avoid doing. There is one other option listed on the UseGPG page above, a timeout. You can use that to set the number of seconds mutt will consider your passphrase good, and really that's probably what you should use, but for me, it's just not what I want. I can't seem set it to value I like and I also have a few other places I use my gpg key (specifically when I build a new Ubuntu package) so I don't just need my email client to use my gpg key. So now what? GPG Agent. This is where things may get distro-specific. I use Xubuntu exclusively these days, but most of what I know about this comes from Debian pages. If you're in a non-Debian-ish world, I can't really help you except to suggest you should be able to find a package in your distro named somewhat close to gnupg-agent. Install gnupg-agent:

skynet ~sudo aptitude install gnupg-agent
[sudo] password for joe:
The following NEW packages will be installed:
gnupg-agent libassuan0{a} pinentry-gtk2{a}
0 packages upgraded, 3 newly installed, 11 to remove and 13 not upgraded.
Need to get 379 kB of archives. After unpacking 0 kB will be freed.
Do you want to continue? [Y/n/?] Y


And wait. Next we need to configure gnupg-agent. Not complicated, but it wasn't terribly obvious to me at first, despite the pretty straightforward pages I found describing it. The important things to remember here are that installing the package on Ubuntu will update the default X configuration to start up gpg-agent when X starts. This is important because gpg-agent works pretty much the same way ssh-agent works, to really be useful, you have to export an environment variable to any shell where intend to use it, telling that shell how to communicate with it. If you let it start when X starts, all shells in your X session will already know about it and you don't have to set up anything. This means, though, that it's probably best to just suck it up and log out and log back in again after you install gpg-agent. It sucks, but trust me, it's easier this way. And the next time you log in, you won't have to worry about it anymore anyway. Let's assume you ignore my advice and plan to start gpg-agent by hand (at least for this first run). Don't do that yet. We need to configure it first. The minimal useful gpg-agent configuration file (for me, at least) appears to be:

skynet ~cat ~/.gnupg/gpg-agent.conf
pinentry-program /usr/bin/pinentry-gtk-2 default-cache-ttl 86400
max-cache-ttl 86400


Which says, gpg-agent use pinentry-gtk-2 to prompt me whenever it need to prompt me for a passphrase, it should cache my passphrase for one day by default and it should never cache a passphrase for more than one day. Create yourself a ~/.gnupg/gpg-agent.conf file now. Put in whatever you like, you'll want at least a pinentry binary and select whatever cache times seem reasonable to you. Right, next, start up gpg-agent:

skynet ~eval $(gpg-agent --daemon)


You won't see any output due to the eval. It doesn't matter, all you would see would be something like this: GPG_AGENT_INFO=/tmp/gpg-7cigjL/S.gpg-agent:7425:1; export GPG_AGENT_INFO; And you'd have to paste that into any shell where you intended to use gpg-agent. If you want that information, something like this will do it for you:

skynet ~env | grep GPG_AGENT_INFO
GPG_AGENT_INFO=/tmp/gpg-7cigjL/S.gpg-agent:7425:1


Almost there. The last bit is to go back to mutt and tell it that it should use gpg-agent for passphrases if at all possible:

# consult a gpg-agent, if running, for pgp credentials set pgp_use_gpg_agent=yes


That's it. Start up mutt, compose an email and try it out.

Oh, one last caveat.  I have reliable reports that email sent out this way (presumably any RFC-compliant, PGP-signed email) breaks some versions of Good.  It certainly breaks the one being used at my work.  So, if you like explaining to folks how it's not your fault they cannot read your mail -- and not just because you have a proclivity for using ten-dollar-words -- this turns out to be a twofer.