NAME
Mail::DKIM2 - DKIM2 signing and verification for email
SYNOPSIS
use Mail::DKIM2;
# Sign: record the message in a Message-Instance (an originating hop
# adds m=1; a hop that changed a message adds the next m= with a Recipe,
# see Mail::DKIM2::MessageInstance), then add a DKIM2-Signature over it.
my $mi = Mail::DKIM2::MessageInstance->calculate($message);
$message = Mail::DKIM2::Common::fold_header('Message-Instance: ' . $mi->as_string)
. "\r\n" . $message;
my $signer = Mail::DKIM2::Signer->new(
Domain => 'example.com',
Selector => 'sel1',
KeyFile => '/etc/dkim2/sel1.pem',
MailFrom => '<sender@example.com>',
RcptTo => ['<recipient@example.net>'],
)->load($message);
die $signer->result_detail unless $signer->result eq 'signed';
my $header = $signer->as_string; # "DKIM2-Signature: i=1; ..."
# Verify: check every signature in the chain and the Message-Instance
# chain beneath it.
my $verifier = Mail::DKIM2::Verifier->new->load($message);
print $verifier->result_detail, "\n"; # pass (i=1..2 verified)
# Streaming, for a milter or other filter that sees the message in
# pieces (CRLF line endings); one object per message:
my $v = Mail::DKIM2::Verifier->new;
$v->PRINT($chunk) for @chunks;
$v->CLOSE;
DESCRIPTION
DKIM2 is a successor to DKIM in which every hop that handles a message signs it, each signature covers all the signatures before it, and a Message-Instance header records hashes of the message at each hop together with a Recipe for undoing that hop's changes. A recipient can therefore tell who handled a message, in what order, and what each of them changed.
This distribution implements signing and verification for draft-ietf-dkim-dkim2-spec-06 (see "STATUS"). The modules:
- Mail::DKIM2::Signer
-
Adds a DKIM2-Signature header for this hop.
- Mail::DKIM2::Verifier
-
Verifies the chain of DKIM2-Signature headers and the Message-Instance chain, reporting
pass,fail,none,permerrorortemperror. - Mail::DKIM2::MessageInstance
-
Computes, verifies and undoes Message-Instance headers, including the Recipes that describe a hop's changes.
- Mail::DKIM2::Signature
-
Parses and builds one DKIM2-Signature header.
- Mail::DKIM2::DSN
-
Generates, authenticates and propagates DKIM2-signed Delivery Status Notifications (spec-06 section 12).
- Mail::DKIM2::Common
-
Canonicalization, hashing, folding and key-loading functions shared by the above.
- Mail::DKIM2::HeaderParser, Mail::DKIM2::TagValueList
-
The streaming message parser the Signer and Verifier are built on, and the tag=value list a DKIM2-Signature is built on.
Mail::DKIM2::MessageStore, Mail::DKIM2::Reflector, Mail::DKIM2::Validate and Mail::DKIM2::Split support the authentication_milter handlers (Mail::Milter::Authentication::Handler::DKIM2Sign, Mail::Milter::Authentication::Handler::DKIM2Verify) and the dkim2.com demonstration server, and are not needed to sign or verify mail.
The command-line tools dkim2sign and dkim2verify sign and verify a message from a file or standard input.
CONVENTIONS
The whole distribution follows these rules; each module's documentation assumes them.
Options and methods
Constructor options and the options of class methods are CamelCase (SkipTimestampCheck, IgnorePrefixes). Methods are snake_case (skip_timestamp_check). Where an option can also be set after construction, the method has the same name as the option in snake_case and acts as a getter with an optional setter argument (a code-reference option has a set_ method instead). A constructor refuses an option it does not know, so a misspelling is an error rather than a silently ignored setting.
Feeding a message
The Signer and Verifier are streaming parsers. PRINT($bytes) feeds any amount of the message, in chunks of any size, and CLOSE() finishes it. Line endings must be CRLF, as they are on the wire; every hash in DKIM2 is defined over CRLF text. load($input) is the one-shot form: it takes the message as a string, a reference to one, a filehandle or an Email::MIME, normalises bare LF to CRLF, and calls PRINT then CLOSE. A Signer or Verifier can also be tied to a filehandle, which routes print and close to PRINT and CLOSE.
A Signer or Verifier object handles one message. Make a new one for the next.
Results and errors
A mistake in how the library is called, such as a missing required option or an unknown one, is reported by croak from the call that made it.
The outcome of processing a message is never an exception. The Verifier reports it through result() (one of pass, fail, none, permerror, temperror) and details() (the reason); the Signer through result() (signed or fail) and details(). Both have a result_detail() combining the two for display. PRINT and CLOSE return normally whatever the verdict. The Mail::DKIM2::MessageInstance class methods follow the same split: verify and chain_verifies return a status, while calculate and undo die when the message they are handed cannot be processed, which a caller treats as a configuration error.
The library only ever dies with a plain string. Any exception that is a reference, such as an object a milter framework throws to signal a timeout, passes through every eval in the library untouched and reaches the host.
Public keys
The Verifier fetches public keys from DNS through a Net::DNS::Resolver (the Resolver option, created on demand if not given). A PubkeyCallback replaces the lookup entirely, and is handed the verifier so it can fall back to the standard fetch for keys it does not know. A DNS failure that is not a definite "no such record" is a temperror.
Operator-local header fields
Fields an operator's own systems add after a message is signed and strip before it leaves are named by prefix with IgnorePrefixes, on a Verifier and on each Mail::DKIM2::MessageInstance call. They are local policy and are never process-wide state.
STATUS
This implements draft-ietf-dkim-dkim2-spec-06, an Internet-Draft that is still changing. The wire format follows the draft, so a message signed by this version may not verify with a version tracking a later draft, and the other way round. It is in production use: Fastmail signs outbound mail with it, and the dkim2.com interoperability server runs it. The API is at 0.x and may still change in incompatible ways between releases; the Changes file records every such change.
SEE ALSO
https://github.com/dkim2wg/interop/blob/master/docs/dkim2-postfix-list-host-guide.md, the guide to running a Postfix mailing-list host with this distribution, Mailman 3 or Sympa.
https://datatracker.ietf.org/doc/draft-ietf-dkim-dkim2-spec/, https://github.com/dkim2wg/interop, https://dkim2.com/, Mail::DKIM.
AUTHOR
Bron Gondwana <brong@fastmailteam.com>
COPYRIGHT AND LICENSE
Copyright (c) 2025-2026 Fastmail Pty Ltd. This is free software; you can redistribute it and/or modify it under the same terms as Perl itself.