Mutt bietet von sich aus keine Möglichkeit Nachrichten an Empfänger, für die ein öffentlicher GPG-Schlüssel vorhanden ist, automatisch zu verschlüsseln. Man kann zwar die Option pgp_autoencrypt aktivieren, dies führt aber dazu, dass mutt versucht jede E-Mail zu verschlüsseln, was man dann regelmäßig vor dem Absenden deaktivieren muss, wenn der Empfänger keinen public key hat. Die Alternative (pgp_autoencrypt deaktivieren) ist auch nicht sehr benutzerfreundlich, weil man hier viel zu leicht vergessen kann, die Verschlüsselung im Einzelfall zu aktivieren.

Abhilfe

Abhilfe schafft die Möglichkeit die automatische Verschlüsselung zunächst global zu deaktivieren und mit so genannten send-hooks für einzelne Empfängeradressen (eben diejenigen, für die ein Schlüssel vorhanden ist) wieder zu aktivieren. In der ~/.muttrc sieht das Ganze dann so aus:

send-hook . 'unset pgp_autoencrypt'
send-hook '~t my-friend@example.com' 'set pgp_autoencrypt'

Die erste Zeile deaktiviert (unset) automatische Verschlüsselung für alle Empfänger (der ‘.’ trifft immer zu). In der zweiten Zeile wird nun die Verschlüsselung wieder aktiviert und zwar immer dann, wenn eine Mail an my-friend@example.com adressiert wird (der Schalter ‘~t‘ bezieht das Suchmuster auf das Empfänger-Feld (To:). Den passenden Schlüssel kann mutt dann selbst ermitteln, sofern pgp_list_pubring_command korrekt gesetzt ist.

Nun ist es alles andere als praktikabel für jeden einzelnen Empfänger, der einen GPG-Schlüssel besitzt, eine separate Zeile in der Konfigurationsdatei hinzuzufügen, zumal diese Information ja bereits existiert: im public keyring. Netterweise bietet GPG eine komfortable Möglichkeit, die Liste der vorhandenen Schlüssel automatisiert zu verarbeiten und zwar mit Hilfe der Schalter »--list-keys --with-colons». Das Ergebnis ist eine Liste mit 15 durch Doppelpunkte getrennten Feldern. Für unsere Zwecke benötigen wir nur drei davon:

  • Feld 1 enthält den Typ der Zeile. Uns interessieren nur diejenigen vom Typ pub und uid.
  • Feld 2 enthält den berechneten trust level. Ich habe mich dafür entschieden, alle Schlüssel zu berücksichtigen, denen ich mindestens »marginally« (’m') vertraue. Des Weiteren steht ‘f’ für »fully« und ‘u’ für »ultimately« trusted.
  • Feld 10 enthält die eigentliche UID, also den Namen und die E-Mail-Adresse.

Als regulärer Ausdruck ergibt sich damit

/^(uid|pub):[mfu]:(.*:){7}.* <(.+@.+)>:/

Die vollständige Dokumentation dieses Formats findet sich übrigens in der Datei DETAILS.gz im Dokumentationsverzeichnis von GnuPG (z.B. bei Debian /usr/share/doc/gnupg/).

Alles zusammen

Das folgende Script prüft zunächst, ob eine Cache-Datei existiert und diese neuer als der public keyring ist. Wenn das der Fall ist, wird einfach diese Datei ausgegeben. Andernfalls die oben beschriebene Liste aller Schlüssel mit gpg --list-keys --with-colons und daraus entsprechende Konfigurationszeilen für mutt erzeugt. Eingebunden wird das Script mit der Zeile

source ~/.muttrc.gpg.pl|

in der ~/.muttrc (das Pipe-Symbol (|) am Ende nicht vergessen!).

Falls die Cache-Datei nicht existiert und der Keyring einigermaßen groß ist (bei mir sind es derzeit etwas über 1800 Zeilen), verzögert sich der Start von mutt um wenige Sekunden, was durchaus im Rahmen des Erträglichen liegt.

#!/usr/bin/perl

# file:    ~/.muttrc.gpg.pl
# author:  Martin Grandrath <mail@martin-grandrath.de>
# date:    2008-04-19
#
# description:
#   Creates .muttrc lines in the form
#   ,----
#   | send-hook '~t trusted@example.com' 'set pgp_autoencrypt'
#   `----
#   for each trusted uid in your gpg public keyring.  The output is
#   stored in a cache file that will be refreshed whenever the keyring
#   is newer than the cache file (mtime).
#
#   This script can be included in ~/.muttrc with the following line
#   (don't forget the trailing pipe (|))
#   ,----
#   | source ~/.muttrc.gpg.pl|
#   `----

use strict;
use warnings;
use File::stat;

my $cachefile="$ENV{HOME}/.cache/muttrc.gpg";
my $keyring="$ENV{HOME}/.gnupg/pubring.gpg";

if (-s $cachefile && -s $keyring &&
    stat($cachefile)->mtime > stat($keyring)->mtime)
{
    # Just dump cachefile to stdout if it exists and is not older than
    # keyring
    open FILE, $cachefile or die "ERROR: $!\n";
    print while (<FILE>);
    close FILE;
}
else
{
    my %count;   # count adresses (we want no duplicates)
    my $address; # email addresses from keyring

    # Create new muttrc config and "tee" it to stdout and the
    # cachefile
    open STDOUT, "| tee $cachefile" or die "ERROR: $!\n";
    open GPG, '/usr/bin/gpg --list-keys --with-colons |' or die "Error: $!\n";

    # let mutt reset all autoencrypt settings
    print "send-hook . 'unset pgp_autoencrypt'\n";

    while (<GPG>)
    {
        # Get all keys which we at least "marginally" trust
        if (/^(uid|pub):[mfu]:(.*:){7}.* <(.+@.+)>:/)
        {
            $address = lc($3);
            $count{$address} += 1;

            # Output new send-hook line if address is no duplicate
            print "send-hook '~t $address' 'set pgp_autoencrypt'\n"
                if ($count{$address} < 2);
        }
    }
    close GPG;
    close STDOUT;
}

Ich setze hier voraus, dass alle E-Mails verschlüsselt werden sollen, sobald die Möglichkeit dazu besteht. Möchte man einzelne Empfänger von dieser Regel ausnehmen, kann man für diese natürlich nach der Einbindung des Scriptes weitere send-hooks anlegen, die die automatische Verschlüsselung wieder deaktivieren.