NAME
Mail::DKIM2::Common - Canonicalization, hashing, folding and key handling for DKIM2
SYNOPSIS
use Mail::DKIM2::Common qw(
should_skip dkim2_canonicalize_header fold_header
load_private_key parse_dkim_pubkey
extract_domain relaxed_domain_match
DKIM2_DRAFT MAX_CHAIN_LENGTH
);
my $key = load_private_key('/etc/dkim2/sel1.pem');
my $pub = parse_dkim_pubkey('v=DKIM1; k=rsa; p=MIIB...');
my $folded = fold_header("Message-Instance: " . $mi->as_string);
DESCRIPTION
The functions the other modules share. Nothing is exported by default. Those under "CANONICALIZATION" and "SIGNING INPUT" define bytes the spec defines and are of interest to anyone checking this implementation against another; the rest are conveniences.
This module implements draft-ietf-dkim-dkim2-spec-06; see "STATUS" in Mail::DKIM2 for what that means for the wire format and the API, and "CONVENTIONS" in Mail::DKIM2 for the option, input and error conventions every module here follows.
CONSTANTS
DKIM2_DRAFT, DKIM2_REPO, DKIM2_DATE
The spec revision implemented (ietf-dkim-dkim2-spec-06), where the code lives, and the date this implementation's DKIM2 behaviour last changed. Emitted in X-DKIM2-Info debug headers (draft-gondwana-dkim2-debug-header).
MAX_CHAIN_LENGTH
32: a message carrying more Message-Instance or DKIM2-Signature fields than this is a PERMERROR, found before any key is fetched. Local policy, not spec.
CANONICALIZATION
should_skip($header_name, [\@prefixes])
True if the field is excluded from the header hash: the spec-06 section 4 list (Received, Return-Path, Message-Instance, DKIM2-Signature, DKIM-Signature, Authentication-Results, the ARC fields and others), any X-* or Received-* field, or a field whose name starts with one of the caller's prefixes, case-insensitively. The prefixes are an operator's local policy; pass them as IgnorePrefixes to Mail::DKIM2::Verifier and the Mail::DKIM2::MessageInstance class methods.
check_ignore_prefixes($value)
Croaks unless $value is undef or an array reference; returns it. Every entry point that takes IgnorePrefixes calls this.
dkim2_canonicalize_header($line)
Header-hash canonicalization (section 5.2) of one complete header line including its CRLF: unfold, lowercase the name, collapse whitespace runs to one space, trim whitespace around the colon and at the end. Returns name:value\r\n.
dkim2_canonicalize_sig_header($line)
Signing-input canonicalization (section 8.5): as above, but all whitespace in the value is removed rather than collapsed.
extract_mi_version($header_value)
The m= number of a Message-Instance value, or undef. Accepts a string, a scalar ref, or an arrayref (first element).
strip_mi_versions($message, @m)
Removes the Message-Instance fields with the given m= numbers from a CRLF message string and returns the result.
SIGNING INPUT
build_signing_input(%args)
The bytes a DKIM2-Signature signs (section 8.5): the canonicalized Message-Instance headers in ascending m= order, the DKIM2-Signature headers below the one being signed in ascending i= order, and that one with empty s= values. Arguments: mi_headers, an arrayref of { v => N, raw => $line } sorted by v; dk2_headers, an arrayref of { i => N, raw => $line, sig => $signature } sorted by i; signing_i, the i= being signed or verified; signature, its Mail::DKIM2::Signature; and optionally signing_header, the exact folded text to use for it, which the Signer passes so that the folds it chose are signed.
chain_length_error($msg_or_counts)
The PERMERROR string for a message over "MAX_CHAIN_LENGTH", or undef. Takes an Email::MIME or a hashref of field counts keyed by lowercased name.
duplicate_number_error($field, $tag, @numbers)
The PERMERROR string for the first number that appears twice, or undef.
ADDRESSES
extract_domain($address)
The domain of user@domain or <user@domain>, or undef.
to_rfc5321_path($address)
Wraps an address in angle brackets if it has none; undef or empty becomes <>. The form mf= and rt= carry.
relaxed_domain_match($domain, $parent)
True if $domain is $parent or a subdomain of it, case-insensitively.
FOLDING
Only for a header this code is creating. A header read from anywhere else is never refolded: a fold where there was no whitespace changes its canonical form and breaks every signature over it.
parse_mime($raw)
Parse $raw into an Email::MIME for the library's own use (raw header fields, raw body), with Email::MIME::ContentType's parameter check relaxed for the duration of the parse. DKIM2 never reads a MIME parameter, so a sender's broken Content-Type must not stop a message from being verified or make every verification warn. The package variable is restored afterwards.
fold_header($line, [$margin], %opts)
Folds a complete header line at $margin characters (default 72) with CRLF-tab continuations, breaking at ; first, then at a space, then after a ,, then anywhere. With delimiters_only => 1 it breaks only after ; or ,, never inside a token, leaving a value that fits nowhere whole on an over-long line (the rule for X-DKIM2-Info).
fold_value($string, [$margin])
Folds a string at arbitrary positions, $margin content characters per line (default 64).
KEYS
load_private_key($pem_file)
A Crypt::PK::RSA or Crypt::PK::Ed25519 from a PEM file, whichever it holds. Dies if the file is neither.
load_private_key_data($data)
The same from key material in memory: PEM, or bare base64 DER as some key stores keep it. Returns undef rather than dying, so a signer of live mail can log and carry on.
parse_dkim_pubkey($txt_record)
The public key object from a DKIM TXT record (k= and p=; h= is ignored per section 10.3). Ed25519 keys are accepted as the raw 32 bytes of RFC 8463 or as DER. Returns undef for a record it cannot parse.
TAG ENCODING
encode_tag_json($data), decode_tag_json($base64)
Canonical JSON in base64, the encoding of the r= Recipe tag.
digest64($digest_object)
The base64 of a CryptX digest object's result.
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.