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
