Documentation ¶
Overview ¶
Package dkim (DomainKeys Identified Mail signatures, RFC 6376) signs and verifies DKIM signatures.
Signatures are added to email messages in DKIM-Signature headers. By signing a message, a domain takes responsibility for the message. A message can have signatures for multiple domains, and the domain does not necessarily have to match a domain in a From header. Receiving mail servers can build a spaminess reputation based on domains that signed the message, along with other mechanisms.
Index ¶
- Variables
- func DefaultPolicy(sig *Sig) error
- func Lookup(ctx context.Context, elog *slog.Logger, resolver dns.Resolver, ...) (rstatus Status, rrecord *Record, rtxt string, authentic bool, rerr error)
- func Sign(ctx context.Context, elog *slog.Logger, localpart smtp.Localpart, ...) (headers string, rerr error)
- type Identity
- type Record
- type Result
- type Selector
- type Sig
- type Status
Constants ¶
This section is empty.
Variables ¶
var ( MetricSign stub.CounterVec = stub.CounterVecIgnore{} MetricVerify stub.HistogramVec = stub.HistogramVecIgnore{} )
var ( ErrNoRecord = errors.New("dkim: no dkim dns record for selector and domain") ErrMultipleRecords = errors.New("dkim: multiple dkim dns record for selector and domain") ErrDNS = errors.New("dkim: lookup of dkim dns record") ErrSyntax = errors.New("dkim: syntax error in dkim dns record") )
Lookup errors.
var ( ErrSigAlgMismatch = errors.New("dkim: signature algorithm mismatch with dns record") ErrHashAlgNotAllowed = errors.New("dkim: hash algorithm not allowed by dns record") ErrKeyNotForEmail = errors.New("dkim: dns record not allowed for use with email") ErrDomainIdentityMismatch = errors.New("dkim: dns record disallows mismatch of domain (d=) and identity (i=)") ErrSigExpired = errors.New("dkim: signature has expired") ErrHashAlgorithmUnknown = errors.New("dkim: unknown hash algorithm") ErrBodyhashMismatch = errors.New("dkim: body hash does not match") ErrSigVerify = errors.New("dkim: signature verification failed") ErrSigAlgorithmUnknown = errors.New("dkim: unknown signature algorithm") ErrCanonicalizationUnknown = errors.New("dkim: unknown canonicalization") ErrHeaderMalformed = errors.New("dkim: mail message header is malformed") ErrFrom = errors.New("dkim: bad from headers") ErrQueryMethod = errors.New("dkim: no recognized query method") ErrKeyRevoked = errors.New("dkim: key has been revoked") ErrTLD = errors.New("dkim: signed domain is top-level domain, above organizational domain") ErrPolicy = errors.New("dkim: signature rejected by policy") ErrWeakKey = errors.New("dkim: key is too weak, need at least 1024 bits for rsa") )
Signature verification errors.
var Pedantic bool
Pedantic enables stricter parsing.
Functions ¶
func DefaultPolicy ¶
DefaultPolicy is the default DKIM policy.
Signatures with a length restriction are rejected because it is hard to decide how many signed bytes should be required (none? at least half? all except max N bytes?). Also, it isn't likely email applications (MUAs) will be displaying the signed vs unsigned (partial) content differently, mostly because the encoded data is signed. E.g. half a base64 image could be signed, and the rest unsigned.
Signatures without Subject field are rejected. The From header field is always required and does not need to be checked in the policy. Other signatures are accepted.
func Lookup ¶
func Lookup(ctx context.Context, elog *slog.Logger, resolver dns.Resolver, selector, domain dns.Domain) (rstatus Status, rrecord *Record, rtxt string, authentic bool, rerr error)
Lookup looks up the DKIM TXT record and parses it.
A requested record is <selector>._domainkey.<domain>. Exactly one valid DKIM record should be present.
authentic indicates if DNS results were DNSSEC-verified.
Types ¶
type Identity ¶
Identity is used for the optional i= field in a DKIM-Signature header. It uses the syntax of an email address, but does not necessarily represent one.
type Record ¶
type Record struct { Version string // Version, fixed "DKIM1" (case sensitive). Field "v". Hashes []string // Acceptable hash algorithms, e.g. "sha1", "sha256". Optional, defaults to all algorithms. Field "h". Key string // Key type, "rsa" or "ed25519". Optional, default "rsa". Field "k". Notes string // Debug notes. Field "n". Pubkey []byte // Public key, as base64 in record. If empty, the key has been revoked. Field "p". Services []string // Service types. Optional, default "*" for all services. Other values: "email". Field "s". Flags []string // Flags, colon-separated. Optional, default is no flags. Other values: "y" for testing DKIM, "s" for "i=" must have same domain as "d" in signatures. Field "t". PublicKey any `json:"-"` // Parsed form of public key, an *rsa.PublicKey or ed25519.PublicKey. }
Record is a DKIM DNS record, served on <selector>._domainkey.<domain> for a given selector and domain (s= and d= in the DKIM-Signature).
The record is a semicolon-separated list of "="-separated field value pairs. Strings should be compared case-insensitively, e.g. k=ed25519 is equivalent to k=ED25519.
Example:
v=DKIM1;h=sha256;k=ed25519;p=ln5zd/JEX4Jy60WAhUOv33IYm2YZMyTQAdr9stML504=
func ParseRecord ¶
ParseRecord parses a DKIM DNS TXT record.
If the record is a dkim record, but an error occurred, isdkim will be true and err will be the error. Such errors must be treated differently from parse errors where the record does not appear to be DKIM, which can happen with misconfigured DNS (e.g. wildcard records).
func (*Record) Record ¶
Record returns a DNS TXT record that should be served at <selector>._domainkey.<domain>.
Only values that are not the default values are included.
func (*Record) ServiceAllowed ¶
ServiceAllowed returns whether service s is allowed by this key.
The optional field "s" can specify purposes for which the key can be used. If value was specified, both "*" and "email" are enough for use with DKIM.
type Result ¶
type Result struct { Status Status Sig *Sig // Parsed form of DKIM-Signature header. Can be nil for invalid DKIM-Signature header. Record *Record // Parsed form of DKIM DNS record for selector and domain in Sig. Optional. RecordAuthentic bool // Whether DKIM DNS record was DNSSEC-protected. Only valid if Sig is non-nil. Err error // If Status is not StatusPass, this error holds the details and can be checked using errors.Is. }
Result is the conclusion of verifying one DKIM-Signature header. An email can have multiple signatures, each with different parameters.
To decide what to do with a message, both the signature parameters and the DNS TXT record have to be consulted.
func Verify ¶
func Verify(ctx context.Context, elog *slog.Logger, resolver dns.Resolver, smtputf8 bool, policy func(*Sig) error, r io.ReaderAt, ignoreTestMode bool) (results []Result, rerr error)
Verify parses the DKIM-Signature headers in a message and verifies each of them.
If the headers of the message cannot be found, an error is returned. Otherwise, each DKIM-Signature header is reflected in the returned results.
NOTE: Verify does not check if the domain (d=) that signed the message is the domain of the sender. The caller, e.g. through DMARC, should do this.
If ignoreTestMode is true and the DKIM record is in test mode (t=y), a verification failure is treated as actual failure. With ignoreTestMode false, such verification failures are treated as if there is no signature by returning StatusNone.
type Selector ¶ added in v0.0.9
type Selector struct { Hash string // "sha256" or the older "sha1". HeaderRelaxed bool // If the header is canonicalized in relaxed instead of simple mode. BodyRelaxed bool // If the body is canonicalized in relaxed instead of simple mode. Headers []string // Headers to include in signature. // Whether to "oversign" headers, ensuring additional/new values of existing // headers cannot be added. SealHeaders bool // If > 0, period a signature is valid after signing, as duration, e.g. 72h. The // period should be enough for delivery at the final destination, potentially with // several hops/relays. In the order of days at least. Expiration time.Duration PrivateKey crypto.Signer // Either an *rsa.PrivateKey or ed25519.PrivateKey. Domain dns.Domain // Of selector only, not FQDN. }
Selector holds selectors and key material to generate DKIM signatures.
type Sig ¶
type Sig struct { // Required fields. Version int // Version, 1. Field "v". Always the first field. AlgorithmSign string // "rsa" or "ed25519". Field "a". AlgorithmHash string // "sha256" or the deprecated "sha1" (deprecated). Field "a". Signature []byte // Field "b". BodyHash []byte // Field "bh". Domain dns.Domain // Field "d". SignedHeaders []string // Duplicates are meaningful. Field "h". Selector dns.Domain // Selector, for looking DNS TXT record at <s>._domainkey.<domain>. Field "s". // Optional fields. // Canonicalization is the transformation of header and/or body before hashing. The // value is in original case, but must be compared case-insensitively. Normally two // slash-separated values: header canonicalization and body canonicalization. But // the "simple" means "simple/simple" and "relaxed" means "relaxed/simple". Field // "c". Canonicalization string Length int64 // Body length to verify, default -1 for whole body. Field "l". Identity *Identity // AUID (agent/user id). If nil and an identity is needed, should be treated as an Identity without localpart and Domain from d= field. Field "i". QueryMethods []string // For public key, currently known value is "dns/txt" (should be compared case-insensitively). If empty, dns/txt must be assumed. Field "q". SignTime int64 // Unix epoch. -1 if unset. Field "t". ExpireTime int64 // Unix epoch. -1 if unset. Field "x". CopiedHeaders []string // Copied header fields. Field "z". }
Sig is a DKIM-Signature header.
String values must be compared case insensitively.
type Status ¶
type Status string
Status is the result of verifying a DKIM-Signature as described by RFC 8601, "Message Header Field for Indicating Message Authentication Status".
const ( StatusNone Status = "none" // Message was not signed. StatusPass Status = "pass" // Message was signed and signature was verified. StatusFail Status = "fail" // Message was signed, but signature was invalid. StatusPolicy Status = "policy" // Message was signed, but signature is not accepted by policy. StatusNeutral Status = "neutral" // Message was signed, but the signature contains an error or could not be processed. This status is also used for errors not covered by other statuses. StatusTemperror Status = "temperror" // Message could not be verified. E.g. because of DNS resolve error. A later attempt may succeed. A missing DNS record is treated as temporary error, a new key may not have propagated through DNS shortly after it was taken into use. StatusPermerror Status = "permerror" // Message cannot be verified. E.g. when a required header field is absent or for invalid (combination of) parameters. Typically set if a DNS record does not allow the signature, e.g. due to algorithm mismatch or expiry. )