Revision history for Mail::DKIM2
0.12 2026-10-04
- undo() rebuilt a base64 or quoted-printable body encoded twice:
Recipes work on wire lines, so the rebuilt body is already in its
transfer encoding, and Email::MIME->body_set encoded it again.
Every such message a list re-encoded (footer appended, body
re-wrapped) failed "m=1 does not match content" and the milter
refused to sign it -- 17 of 88 charset-corpus samples through
Mailman, while the Python undo rebuilt them byte for byte. The
body is now set as raw octets. (t/undo-encoded-body.t)
- DKIM2_DATE is 2026-10-04: the Message-Instance headers this
library emits changed shape in 0.11 ("b" literals, integer copy
ranges), and the X-DKIM2-Info date stamp follows emitted-header
changes.
0.11 2026-10-04
Fixes found by replaying public-archive mail in assorted charsets
(ISO-2022-JP, GB2312/GB18030, Big5, EUC-KR, Latin-1, raw 8-bit
headers) through the signers, verifiers and list managers
(interop util/charset-corpus.sh).
- Recipe literals carrying any octet >= 0x80 are emitted as a new
{"b": [base64, ...]} step instead of {"d": [...]}. A literal is
the raw octets of a header value or body line; JSON text is
UTF-8, so the old encoder wrote ISO-2022-JP, GB18030, Big5 and
Latin-1 octets into the JSON as they were, which no strict JSON
parser reads back. The decoder rejects a "b" item that is not
RFC 4648 base64 or decodes to something containing CR or LF.
Agreed extension to spec-06 §5, proposed to the WG.
(t/recipe-base64.t)
- Recipe copy ranges must ascend (spec-06 §5.1): each "c" step
starts after the one before it ends. undo() used to sort the
ranges and reject only overlap; it now rejects an out-of-order
range too, for body and header Recipes alike. The header Recipe
builder in calculate() no longer emits one: a header instance a
hop moved above one it left alone is recorded literally.
(t/recipe-order.t, t/undo-bounds.t)
- Two more Recipe schema rules the other verifiers already hold, so
every implementation gives the same verdict: a "c" bound must be
a JSON integer (a string such as "2" is malformed; told apart by
the scalar's flags, not its text), an empty "d" or "b" array is
malformed (minItems 1), and so is a "d" string containing CR or
LF (§5.1/§5.2 MUST NOT). (t/recipe-order.t, t/recipe-base64.t)
- Recipe copy ranges are emitted as JSON integers. An index used as
a hash key while de-duplicating header copies was stringified in
place, so every Sympa Message-Instance carried {"c":["2","2"]},
which the spec-06 schema forbids and strict verifiers reject.
(t/recipe-integers.t)
- A broken Content-Type (`text/plain; Windows-1252`) no longer makes
every verification print Email::MIME's "Illegal parameter" warning:
the library parses with parameter checking relaxed, for the parse
only, since DKIM2 never reads a MIME parameter.
(t/malformed-content-type.t)
- The test suite is self-contained: the test keys and dns.json ship
under t/data/ (t/data-in-sync.t keeps them equal to the interop
repository's shared copies), bin/validate.pl takes --dns-json and
defaults to that copy, and the tests that cross-check the other
implementations or the deployment templates skip outside the
repository. 0.10's tests could not run from the tarball.
0.10 2026-10-02
API cleanup ahead of a CPAN release. Incompatible changes are marked *.
- New top-level Mail::DKIM2 module documenting the conventions every
module follows; every module now carries the distribution $VERSION.
- Constructor options are CamelCase and validated: an unknown option
croaks. Verifier options (SkipTimestampCheck, AllowUnsignedMI,
MidProcess, HeadersOnly, PubkeyCallback, Resolver, IgnorePrefixes)
can be given to new() and are no longer silently discarded.
- load($input): one-shot PRINT+CLOSE taking a string, scalar ref,
filehandle or Email::MIME, normalising LF to CRLF. TIEHANDLE lets a
Signer or Verifier be tied to a filehandle.
- Verifier->signatures and ->top_signature for Authentication-Results
writers.
* Ignore prefixes are per instance: Common::ignore_header_prefixes is
gone; pass IgnorePrefixes to Verifier->new and to
MessageInstance->calculate/verify/chain_verifies instead.
should_skip takes the prefixes as a second argument.
* Key fetching moves to the Verifier: Signature->fetch_public_key is
replaced by Verifier->fetch_public_key($signature, $idx), driven by
the Resolver option. Only NXDOMAIN/NOERROR/NODATA are permanent; any
other resolver error is temperror. The pubkey callback receives the
verifier as a third argument.
* Signer no longer dies from inside PRINT on a chain it cannot
extend: result() is 'fail' and details() says why. result() is
undef (not '?') before CLOSE. details() and result_detail() added.
* Signature: mail_from(), rcpt_to() and flags() are get/set like the
other tag accessors; set_rcpt_to is removed.
* Mail::DKIM2::DSN methods take CamelCase named arguments (Message,
Signer, To, ReportingMTA, Status, Reason, PubkeyCallback,
ForwarderDomain, SkipAuthentication, SkipTimestampCheck) instead of
a hashref. Validate::report takes PubkeyCallback, DnsPath,
SkipTimestampCheck.
* Command-line tools: dkim2sign (was dkim2sign.pl) and the new
dkim2verify are installed; verify-sig.pl, calculate-dkim2.pl and
the *-mailversion tools are removed.
- POD rewritten for spec-06 (the previous text described
draft-clayton-08 tags); Net::DNS declared as a prerequisite.
- dkim2-milter and dkim2-split-lmtp are installed programs (were
bin/*.pl, run from the checkout). X-DKIM2-Info sw= says
dkim2-milter.
0.01 2026-03-08
- Initial release
- Implements draft-clayton-dkim2-spec-08
- Signer: streaming DKIM2-Signature generation with SMTP param recording
- Verifier: full chain verification (all signatures, not just outermost)
- MessageInstance: calculate, verify, and undo with header/body diff recipes
- Signature: tag-value parser with base64-encoded JSON tags
- HeaderParser: thin streaming base class replacing Mail::DKIM::Common
- Common: shared canonicalization, hashing, domain matching utilities
Keyboard Shortcuts
Global
s
Focus search bar
?
Bring up this help dialog
GitHub
gp
Go to pull requests
gi
Go to GitHub issues (only if GitHub is preferred repository)