Clicky

Harry R. Schwartz

Software engineer, nominal scientist, gentleman of the internet. Member, ←Hotline Webring→.


Encrypting Email By Default with Mutt

Published 17 Nov 2015. Tags: beards, email, unix.

I’d like my email to be encrypted by default when I address an email to a contact with a key in my keyring. Conversely, I don’t want to be prompted for a key ID if I address an email to someone without a key in my keyring. Here’s how to do that.

I use mutt for my email, and I like to encrypt my email with GPG. It’s relatively easy to encrypt on a per-email basis, but I’d like to encrypt my emails by default.

I can ensure that my replies to encrypted emails are always encrypted with:

set crypt_replyencrypt = yes

This is useful because it helps me avoid inadvertently responding to an encrypted email in plaintext.

However, I’d like to ensure that every email thread I start will also be encrypted. The recommended solution seems to be:

set crypt_autoencrypt=yes

This technically works, but I don’t like it. Mutt prompts me for the recipient’s key ID every time I send an email to a user that isn’t in my keyring. That might be fine if I only corresponded with GPG users, but I’m mostly communicating with regular folks without keys. That prompt is almost always an inconvenience.

Ideally, I’d like mutt to check my GPG keyring every time I draft an email and encrypt it only if I have the recipient’s key.

Mutt has send-hooks which can support that behavior on a per-recipient basis. For example, suppose I always want to always encrypt messages to foo@example.com:

send-hook "!~l ~t foo@example.org" "set crypt_autoencrypt"

(The !~l ensures that known mailing lists are excluded.)

We’d like to have such a hook in place for every email address in our keyring. We can write a simple Ruby script that will print a send-hook for each of those addresses to STDOUT:

#!/usr/bin/env ruby

def raw_key_dump
  `gpg --list-keys --with-colons --fixed-list-mode --no-secmem-warning`
end

def email_matcher
  /.*\<(.*)\>/
end

def gpg_email_addresses
  raw_key_dump.scan(email_matcher).flatten.uniq
end

gpg_email_addresses.each do |address|
  puts "send-hook \"!~l ~t #{address}\" \"set pgp_autoencrypt\""
end

This doesn’t do anything too complex; it just finds and formats all the email addresses in gpg --list-keys. We can download this script and run it to verify that it prints out a bunch of formatted send-hooks.

Let’s save it as ~/.mutt/gpg_keyring_send_hooks.rb and make it executable. Then we can add a couple lines in our muttrc to load it when we start mutt:

send-hook . 'reset pgp_autoencrypt'
source "~/.mutt/gpg_keyring_send_hooks.rb|"

That first hook will reset encryption for every email address, and the second will execute the script we wrote and evaluate the results as part of the mutt configuration (that’s what the trailing “|” indicates).

That’s it! Our crypto-capable contacts will now get encrypted emails by default. Our plaintext contacts won’t.

I’ve done this in my dotfiles and it seems to work just fine; you can check out the commit to verify the configuration.