The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Mail::GPG - Handling of GnuPG encrypted / signed mails

SYNOPSIS

  use Mail::GPG;

  my $mg = Mail::GPG->new;

  my %keys_id2mail = $mg->query_keyring (
    search => 'joern@zyn.de',
  );

  my $entity = MIME::Entity->build (
    From     => 'joern@zyn.de',
    Subject  => "Mail::GPG Testmail",
    Data     => [ "Hiho, a nice encrypted mail" ],
    Encoding => "quoted-printable",
    Charset  => "iso-8859-1",
  );

  my $encrypted_entity = $mg->mime_sign_encrypt (
    entity     => $entity,
    key_id     => $key_id,
    password   => 'topsecret',
    recipients => [ 'niceguy@zyn.de' ],
  );

  my $mail_text = $encrypted_entity->as_string;

  # and a lot more...

  $mg->mime_sign ( ... );
  $mg->mime_encrypt ( ... );
  $mg->mime_sign_encrypt ( ... );

  $mg->armor_sign ( ... );
  $mg->armor_encrypt ( ... );
  $mg->armor_sign_encrypt ( ... );

  $mg->parse ( ... );

  $mg->decrypt ( ... );
  $mg->verify ( ... );

  $mg->is_encrypted ( ... );
  $mg->is_signed ( ... );
  $mg->is_signed_quick ( ... );

  $mg->get_decrypt_key ( ... );
  $mg->get_key_trust ( ... );

DESCRIPTION

This Perl modules handles all the details of encrypting and signing Mails using GnuPG according to RFC 3156 and RFC 2440, that is OpenPGP MIME and traditional armor signed/encrypted mails.

This module also ships a patch to MIME-tools. Without this patch proper verification of MIME signed messages isn't guaranteed! Refer to the "MIME-tools PATCH" chapter in the documentation for details about this issue.

PREREQUISITES

  Perl              >= 5.00503
  GnuPG::Interface  >= 0.33  (optionally with shipped patch applied)
  MIME-tools        == 5.411 (with shipped patch applied)
  MIME::QuotedPrint >= 2.20  (part of MIME-Base64 distribution)

INSTALLATION

First get MIME-tools 5.411 and extract it, e.g. on the same level where you extracted the Mail::GPG tarball.

  % tar xvfz Mail-GPG-x.xx.tar.gz
  % tar xvfz MIME-tools-5.411.tar.gz

Apply the MIME-tools patch shipped with this module and build and install the MIME-tools package (Mail::GPG works without this patch, but it's strongly suggested, that you apply it. Refer to the next chapter for details):

  % cd MIME-tools-5.411
  % patch -p1 < ../Mail-GPG.x.xx/patches/MIME-tools-5.411.enc.preamble.txt
  % perl Makefile.PL
  % make test
  % make install

Make sure that the gpg program is installed and can be found using your standard PATH.

You may apply the shipped GnuPG::Interface patch as well. It just fixes a warning which is throwed on any keyring inspection. This is a known problem and reported to the author, hopefully it will be fixed upstream soon:

  % tar xvfz GnuPG-Interface-0.33.tar.gz
  % cd GnuPG-Interface-0.33
  % patch -p1 ../Mail-GPG.x.xx/patches/GnuPG-Interface-0.33.tru-record-type.txt
  % perl Makefile.PL
  % make test
  % make install

Then install Mail::GPG

  % cd ../Mail-GPG-x.xx
  % perl Makefile.PL
  % make test
  % make install

Mail::GPG has a bunch of tests which will create a temporary gpg keyring to be able to do real encryption and stuff. You need to have gpg in your path for the tests to succeed, otherwise all useful tests will be skipped.

Note that the test 04.big needs some time, on an Athlon 1800XP about 12 seconds, so be patient ;)

MIME-tools PATCH

Some words about MIME-tools: MIME::Entity internally stores all data in decoded form, that is without any content transfer encoding like quoted-printable or base64 applied. In particular if you parse with MIME::Parser, e.g. a MIME signed mail, the entity will always be stored that way.

But RFC 3156 requires the encoded version of the MIME entity, because the signature is calculated based on the encoded form. Some content transfer encodings are ambigious and you can't reverse the process and get back the correct encoded version without breaking the signature.

The shipped MIME-tools patch adds the ability of having encoded data in a MIME::Entity object and a method to advise MIME::Parser to use this ability and store the parsed data in encoded form.

Additionally MIME-tools does not reproduce preambles which consist only of empty lines. This also invalids signatures. E.g. mutt and sylpheed are known to add such empty preambles. The patch fixes this problem.

Mail::GPG generally works without this patch, but it's strongly suggested that you apply it. Otherwise you have no guarantee that MIME signed messages are verified correctly by Mail::GPG.

Unfortunately the maintainer of MIME-tools currently seeks for a new maintainer and stopped development, so there is no chance to get the patch into an official CPAN version of MIME-tools. That's why you have to apply the patch manually.

WHY ANOTHER GnuPG MAIL MODULE?

I know the Mail::GnuPG module. I worked a long time with it and submitted a few patches adding features and fixing bugs. The problems with MIME signed messages mentioned above led me to my own implementation. In the meantime I know, that regarding the implemented RFC's Mail::GnuPG works as correct as Mail::GPG does. Only that Mail::GnuPG's documentation is not aware of these MIME signature problems resp. encoded vs decoded data storage.

I like clean OO interfaces and well documented source code. With Mail::GnuPG you need to access internal data structures from outside (e.g. things like gpg's last output). Also Mail::GnuPG modifies the MIME::Entity objects you pass to it, which is bad in some situations. Mail::GPG has some more features, e.g. multiplexed I/O with the gpg program, which makes it work even with huge amounts of data.

Last but not least it was simply more fun for me to fix my own bugs in my own code and to learn all the details by making my own faults. And fun is important for an Open Source programmer, in particular for me ;)

So it's up to you: you have the choice, not too bad at all, not? ;)

KNOWN BUGS

Currently none. Please report any bugs to the author: Joern Reder <joern AT zyn.de>.

EXAMPLES

The Mail::GPG distribution contains the program mgpg-test:

  Usage: mgpg-test file ...

It takes one or more filenames of mails as arguments, analyzes them, prints information about signatures and decrypts encrypteded mails (after asking for the correspondent passphrases). The script is rather small and a good example of Mail::GPG usage.

The regression tests in the t/ directory of the distribution show exemplary usage of all Mail::GPG features.

CONSTRUCTOR AND ATTRIBUTES

new

  $mg = Mail::GPG->new (
    attribute => value,
    ...
  );

The new class method returns a new instance of the Mail::GPG class, initialized with the attributes passed as hash parameters.

Attributes

This is the list of attributes you can pass to the new method and access using the methods set_attribute and get_attribute:

default_key_id

The default_key_id takes a GnuPG key id. It is used for all methods which expect a key id, if you don't pass a specific key id to them.

default_passphrase

You can store the passphrase of the default_key_id using this attribute. All methods expecting the passphrase will take it from here by default.

WARNING

Aware that storing the secret key password in many variables in your program increases the risk of being attacked by memory inspection. So you probably don't want to use the default_passphrase attribute.

debug

Setting the debug attribute to a true value will cause Mail::GPG to dump files into the debug_dir (see beyond). This way you can track entities, if signature validation or decryption fails for some reason.

debug_dir

This defaults to File::Spec->tmpdir. The directory is used to store debug files (see debug above).

gnupg_hash_init

This attribute corresponds to the GnuPG::Interface hash_init attribute. Please refer to the GnuPG::Interface manpage for details. E.g. you can set gpg's --homedir option this way and much more.

digest

This is the digest used by GnuPG to calculate hash values for signatures. By default Mail::GPG sets it to "RIPEMD160", which is needed to handle DSA keys (which are very common). You can check the supported digests of your gpg installation by executing 'gpg --version'.

default_key_encrypt

Set this attribute to a true value if you whish to have the default_key_id always added as a recipient for encrypted mails.

no_strict_7bit_encoding

By default this attribute is false, that means that all data which should be signed or encrypted is firstly checked for a RFC 3156 conform 7bit encoding. Until you set no_strict_7bit_encoding to true, an exception will be raised for non 7bit transparent encodings.

gpg_call

This defaults to 'gpg' and is the path of the gpg program executed through GnuPG::Interface. Change this attribute if the 'gpg' program is not in your PATH.

METHODS TO CREATE MIME OpenPGP MESSAGES (RFC 3156)

mime_sign

  $signed_entity = $mg->mime_sign (
      entity     => $entity,
    [ key_id     => $key_id,
      passphrase => $passphrase ]
  );

This method returns the MIME signed version of an entity.

entity

The MIME::Entity object to be signed. By default it must not contain any parts with non 7bit content transfer encodings, because RFC 3156 forbids that. If you want to be able to pass 8bit also (and thus create non RFC conform data), you have to set the no_strict_7bit_encoding attribute.

key_id

The id of the key used to sign the entity. This defaults to default_key_id if omitted here.

passphrase

The corresponding passphrase of the key. This defaults to default_passphrase if omitted here.

mime_encrypt

  $encrypted_entity = $mg->mime_encrypt (
      entity     => $entity,
      recipients => \@recipients,
  );

Returns the MIME encrypted version of an entity.

entity

The MIME::Entity object to be encrypted. By default it must not contain any parts with non 7bit content transfer encodings, because RFC 3156 forbids that. If you want to be able to pass 8bit also (and thus create non RFC conform data), you have to set the no_strict_7bit_encoding attribute.

recipients

This is a reference to an array of recipients, which may be email adresses or key id's. If default_key_encrypt is set, the default_key_id will be added as a recipient automatically.

mime_sign_encrypt

  $encrypted_signed_entity = $mg->mime_sign_encrypt (
      entity     => $entity,
      recipients => \@recipients,
    [ key_id     => $key_id,
      passphrase => $passphrase ]
  );

Returns the encrypted and signed version of an entity.

entity

The MIME::Entity object to be signed and encrypted. By default it must not contain any parts with non 7bit content transfer encodings, because RFC 3156 forbids that. If you want to be able to pass 8bit also (and thus create non RFC conform data), you have to set the no_strict_7bit_encoding attribute.

recipients

This is a reference to an array of recipients, which may be email adresses or key id's. If default_key_encrypt is set, the default_key_id will be added as an recipient automatically.

key_id

The id of the key used to sign the entity. This defaults to default_key_id if omitted here.

passphrase

The corresponding passphrase of the key. This defaults to default_passphrase if omitted here.

METHODS TO CREATE ARMOR OpenPGP MESSAGES (RFC 2440)

armor_sign

  $signed_entity = $mg->armor_sign (
      entity     => $entity,
    [ key_id     => $key_id,
      passphrase => $passphrase ]
  );

This method returns the armor signed version of a MIME::Entity.

entity

The MIME::Entity object to be signed. It must not have any parts and a 7bit clean content transfer encoding.

key_id

The id of the key used to sign the entity. This defaults to default_key_id if omitted here.

passphrase

The corresponding passphrase of the key. This defaults to default_passphrase if omitted here.

armor_encrypt

  $signed_entity = $mg->armor_encrypt (
      entity     => $entity,
      recipients => \@recipients,
  );

Returns the armor encrypted version of an entity.

entity

The MIME::Entity object to be encrypted. It must not have any parts and a 7bit clean content transfer encoding.

recipients

This is a reference to an array of recipients, which may be email adresses or key id's. If default_key_encrypt is set, the default_key_id will be added as an recipient automatically.

armor_sign_encrypt

  $signed_entity = $mg->mime_sign_encrypt (
      entity     => $entity,
      recipients => \@recipients,
    [ key_id     => $key_id,
      passphrase => $passphrase ]
  );

Returns the encrypted and signed version of an entity.

entity

The MIME::Entity object to be encrypted. It must not have any parts and a 7bit clean content transfer encoding.

recipients

This is a reference to an array of recipients, which may be email adresses or key id's. If default_key_encrypt is set, the default_key_id will be added as an recipient automatically.

key_id

The id of the key used to sign the entity. This defaults to default_key_id if omitted here.

passphrase

The corresponding passphrase of the key. This defaults to default_passphrase if omitted here.

METHODS FOR PARSING, DECRYPTION AND VERIFICATION

parse

  $entity = Mail::GPG->parse (
      mail_fh   => $filehandle,
    | mail_sref => \$mail_data
  );

This is a convenience method for parsing a mail message. It uses MIME::Parser and distinguish between MIME and non-MIME messages, doing the right thing regarding reading decoded or encoded bodies.

mail_fh

An opened filehandle of the mail message in question.

mail_sref

A reference to a scalar holding the mail message to be parsed.

Details about parsing with MIME::Parser for Mail::GPG

Parsing is not trivial, because we have a basic problem with MIME::Parser and MIME::Entity. If the mail in question is text/plain and contains an ASCII armor PGP message, Mail::GPG must see the decoded data.

But if it's a MIME PGP message, Mail::GPG needs the encoded data.

With the shipped MIME-tools patch you can advice MIME::Parser to create an encoded entity (be default it creates decoded entities and encodes them on demand). You can activate this transparent encoding mode with the decode_bodies attribute of MIME::Parser, which defaults to 1:

  $parser = MIME::Parser->new;
  $parser->decode_bodies(0);

So you need to set decode_bodies(0) for MIME messages and keep the default of decode_bodies(1) for armor messages. But how can you know in advance which is right without having the entity parsed already? You can't!

One possible solution is to parse the entity twice if it's MIME, and keep the decoded version from the first parse run otherwise, or you do some quick analysis on the data in question, without really parsing it.

  $parser = MIME::Parser->new;
  $parser->decode_bodies(0);
  $entity = $parser->parse_data($mail_data);
  if ( $entity->effective_type ne 'multipart/signed' and
       $entity->effective_type ne 'multipart/encrypted' ) {
    $parser->decode_bodies(1);
    $entity = $parser->parse_data($mail_data);
  }

That's exactly what the parse() method does for you, so it's a good idea to use it instead of fiddling with all the details yourself ;)

decrypt

  ($decrypted_entity, $result) = $mg->decrypt (
      entity     => $entity,
    [ passphrase => $passphrase ]
  );

Returns the decrypted version of an entity and a Mail::GPG::Result object with detailed information about the entities encryption (refer to the manpage of Mail::GPG::Result).

entity

The MIME::Entity to be decrypted. Please read the chapter about the parse() method of details about this entity.

passphrase

The corresponding passphrase of the secret key which is needed to decrypt the message. Use get_decrypt_key to determine the corresponding key. This defaults to default_passphrase if omitted here.

verify

  $result = $mg->verify (
      entity => $entity,
  );

Returns a Mail::GPG::Result object with detailed information about the signature of an entity. Refer to the manpage of Mail::GPG::Result.

entity

The signed MIME::Entity to be verified. Please read the chapter about the parse() method of details about this entity.

METHODS FOR ENTITY INSPECTION

is_signed

  $signed = $mg->is_signed (
      entity => $entity,
  );

Returns whether an entity is signed or not.

entity

The entity to be checked for a signature.

is_signed_quick

  $signed = $mg->is_signed_quick (
      mail_fh   => $filehandle,
    | mail_sref => \$mail_data
  );

Does some very quick and rough detection whether a message is signed or not. Note: the special about this method is it doesn't require a MIME::Entity. Creating a MIME::Entity is the opposite of being "quick" ;)

Major drawback is, you can't really rely on the result of this method. It can't detect base64 encoded armor signed messages (it reports always false on them).

Also it may report a signature although it's not signed at all. E.g. the message is a reply to a armor signed message and the quoted parts contain the -----BEGIN PGP SIGNATURE----- string or something like that. To be really sure you should call is_signed() afterwards.

Just use is_signed_quick() to decide whether you want to do deeper inspection or not, but don't rely only on its result.

mail_fh

An opened filehandle of the mail message to be analyzed. Note: the filehandle is rewinded by the method using seek($mail_fh, 0, 0).

mail_sref

A reference to a scalar holding the mail message to be analyzed.

is_encrypted

  $encrypted = $mg->is_encrypted (
      entity     => $entity,
  );

Return whether an entity is encrypted or not.

entity

The entity to be checked for a encryption.

get_decrypt_key

  ($key_id, $key_mail) = $mg->get_decrypt_key (
      entity => $entity,
  );

Returns secret key id and mail address which is needed to decrypt an encrypted entity.

entity

The entity to inspect.

METHODS FOR KEY RING INSPECTION

query_keyring

  %result              = $mg->query_keyring ( search => $search );
  $key_id              = $mg->query_keyring ( search => $search );
  ($key_id, $key_mail) = $mg->query_keyring ( search => $search );

Searches the keyring for a key id or email address. In list context a subsequent list of key id and mail address pairs (suitable for a hash variable) is returned. In scalar context the key id of the first entry is returned. If nothing was found undef is returned.

If you need more detailed control about the query result, use GnuPG::Interface->get_public_keys and GnuPG::Interface->get_secret_keys instead. For details refer to the GnuPG::PrimaryKey manpage.

If you use Perl 5.8.0 or better email addresses will be returned as an utf8 enabled scalar, because gpg always lists email adresses in utf8. Since Perl > 5.8.0 handles utf8 very nice and transparently, you mostly don't need to care about this ;)

If you use the module with older Perl versions you need to handle utf8 encoded data yourself.

Key id or email address to query for.

get_key_trust

  $trust = $mg->get_key_trust (
    key_id => $key_id
  );
  

Reports the trust level of the given key. The known levels are listed in the DETAILS file of the gnupg distribution, but qouted here for convenience (gnupg 1.2.5):

  o = Unknown (this key is new to the system)
  i = The key is invalid (e.g. due to a missing self-signature)
  d = The key has been disabled
      (deprecated - use the 'D' in field 12 instead)
  r = The key has been revoked
  e = The key has expired
  - = Unknown trust (i.e. no value assigned)
  q = Undefined trust
      '-' and 'q' may safely be treated as the same
      value for most purposes
  n = Don't trust this key at all
  m = There is marginal trust in this key
  f = The key is fully trusted
  u = The key is ultimately trusted.  This often means
      that the secret key is available, but any key may
      be marked as ultimately trusted.
key_id

Key id to query for.

AUTHOR

Joern Reder <joern AT zyn.de>

CONTACT

You can contact me by email. Please place the module name "Mail::GPG" somewhere in the subject, because I filter my mails that way. I'm a native German speaker, but you can contact me in english as well.

COPYRIGHT

Copyright (C) 2004 by Joern Reder, All Rights Reserved.

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

SEE ALSO

Mail::GPG::Result, perl(1).