RFC 4871

DomainKeys Identified Mail (DKIM) Signatures

๐Ÿ“… published May 2007, from ietf

RFC 4871: DomainKeys Identified Mail (DKIM) Signatures ๐Ÿฆ‹ (part 1 of 15, more to come!)

Okay bestie, this one's from the Network Working Group and it's dated May 2007 ๐Ÿ“…. Six authors wrote it: E. Allman (Sendmail, Inc.), J. Callas (PGP Corporation), M. Delany and M. Libbey (both Yahoo! Inc), and J. Fenton and M. Thomas (both Cisco Systems, Inc.) ๐Ÿ‘‘. It's on the Standards Track, and it obsoletes RFC 4870 ๐Ÿ’…. The header doesn't list anything it updates.


Status of This Memo ๐Ÿ“œ

Bestie, this document is an Internet standards track protocol for the whole Internet community, and it's literally asking for discussion and suggestions for improvements ๐Ÿ—ฃ๏ธ. To see where this protocol stands in standardization, check the current edition of the "Internet Official Protocol Standards" (STD 1) ๐Ÿ“š. Distribution of this memo is unlimited ๐ŸŒˆ.

Copyright (C) The IETF Trust (2007).

Abstract โœจ

DomainKeys Identified Mail (DKIM) is a domain-level authentication framework for email ๐Ÿ’Œ. It uses public-key cryptography and key server technology so that either Mail Transfer Agents (MTAs) or Mail User Agents (MUAs) can verify the source and contents of messages ๐Ÿ”. The big goal is to let a signing domain take responsibility for a message. That protects the identity of the signer and the integrity of the messages they send, all while keeping Internet email working the way it does today ๐Ÿซถ. Protecting email identity may help with the global control of "spam" and "phishing" ๐Ÿ™…โ€โ™€๏ธ.

Table of Contents ๐Ÿ—‚๏ธ

   1.  Introduction . . . . . . . . . . . . . . . . . . . . . . . . .  4
     1.1.  Signing Identity . . . . . . . . . . . . . . . . . . . . .  5
     1.2.  Scalability  . . . . . . . . . . . . . . . . . . . . . . .  5
     1.3.  Simple Key Management  . . . . . . . . . . . . . . . . . .  5
   2.  Terminology and Definitions  . . . . . . . . . . . . . . . . .  5
     2.1.  Signers  . . . . . . . . . . . . . . . . . . . . . . . . .  6
     2.2.  Verifiers  . . . . . . . . . . . . . . . . . . . . . . . .  6
     2.3.  Whitespace . . . . . . . . . . . . . . . . . . . . . . . .  6
     2.4.  Common ABNF Tokens . . . . . . . . . . . . . . . . . . . .  6
     2.5.  Imported ABNF Tokens . . . . . . . . . . . . . . . . . . .  7
     2.6.  DKIM-Quoted-Printable  . . . . . . . . . . . . . . . . . .  7
   3.  Protocol Elements  . . . . . . . . . . . . . . . . . . . . . .  8
     3.1.  Selectors  . . . . . . . . . . . . . . . . . . . . . . . .  8
     3.2.  Tag=Value Lists  . . . . . . . . . . . . . . . . . . . . . 10
     3.3.  Signing and Verification Algorithms  . . . . . . . . . . . 11
     3.4.  Canonicalization . . . . . . . . . . . . . . . . . . . . . 13
     3.5.  The DKIM-Signature Header Field  . . . . . . . . . . . . . 17
     3.6.  Key Management and Representation  . . . . . . . . . . . . 25
     3.7.  Computing the Message Hashes . . . . . . . . . . . . . . . 29
     3.8.  Signing by Parent Domains  . . . . . . . . . . . . . . . . 31
   4.  Semantics of Multiple Signatures . . . . . . . . . . . . . . . 32
     4.1.  Example Scenarios  . . . . . . . . . . . . . . . . . . . . 32
     4.2.  Interpretation . . . . . . . . . . . . . . . . . . . . . . 33
   5.  Signer Actions . . . . . . . . . . . . . . . . . . . . . . . . 34
     5.1.  Determine Whether the Email Should Be Signed and by
           Whom . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
     5.2.  Select a Private Key and Corresponding Selector
           Information  . . . . . . . . . . . . . . . . . . . . . . . 35
     5.3.  Normalize the Message to Prevent Transport Conversions . . 35
     5.4.  Determine the Header Fields to Sign  . . . . . . . . . . . 36
     5.5.  Recommended Signature Content  . . . . . . . . . . . . . . 38
     5.6.  Compute the Message Hash and Signature . . . . . . . . . . 39
     5.7.  Insert the DKIM-Signature Header Field . . . . . . . . . . 40
   6.  Verifier Actions . . . . . . . . . . . . . . . . . . . . . . . 40
     6.1.  Extract Signatures from the Message  . . . . . . . . . . . 41
     6.2.  Communicate Verification Results . . . . . . . . . . . . . 46
     6.3.  Interpret Results/Apply Local Policy . . . . . . . . . . . 47
   7.  IANA Considerations  . . . . . . . . . . . . . . . . . . . . . 48
     7.1.  DKIM-Signature Tag Specifications  . . . . . . . . . . . . 48
     7.2.  DKIM-Signature Query Method Registry . . . . . . . . . . . 49
     7.3.  DKIM-Signature Canonicalization Registry . . . . . . . . . 49
     7.4.  _domainkey DNS TXT Record Tag Specifications . . . . . . . 50
     7.5.  DKIM Key Type Registry . . . . . . . . . . . . . . . . . . 50
     7.6.  DKIM Hash Algorithms Registry  . . . . . . . . . . . . . . 51
     7.7.  DKIM Service Types Registry  . . . . . . . . . . . . . . . 51
     7.8.  DKIM Selector Flags Registry . . . . . . . . . . . . . . . 52
     7.9.  DKIM-Signature Header Field  . . . . . . . . . . . . . . . 52
   8.  Security Considerations  . . . . . . . . . . . . . . . . . . . 52
     8.1.  Misuse of Body Length Limits ("l=" Tag)  . . . . . . . . . 52
     8.2.  Misappropriated Private Key  . . . . . . . . . . . . . . . 53
     8.3.  Key Server Denial-of-Service Attacks . . . . . . . . . . . 54
     8.4.  Attacks Against the DNS  . . . . . . . . . . . . . . . . . 54
     8.5.  Replay Attacks . . . . . . . . . . . . . . . . . . . . . . 55
     8.6.  Limits on Revoking Keys  . . . . . . . . . . . . . . . . . 55
     8.7.  Intentionally Malformed Key Records  . . . . . . . . . . . 56
     8.8.  Intentionally Malformed DKIM-Signature Header Fields . . . 56
     8.9.  Information Leakage  . . . . . . . . . . . . . . . . . . . 56
     8.10. Remote Timing Attacks  . . . . . . . . . . . . . . . . . . 56
     8.11. Reordered Header Fields  . . . . . . . . . . . . . . . . . 56
     8.12. RSA Attacks  . . . . . . . . . . . . . . . . . . . . . . . 56
     8.13. Inappropriate Signing by Parent Domains  . . . . . . . . . 57
   9.  References . . . . . . . . . . . . . . . . . . . . . . . . . . 57
     9.1.  Normative References . . . . . . . . . . . . . . . . . . . 57
     9.2.  Informative References . . . . . . . . . . . . . . . . . . 58
   Appendix A.  Example of Use (INFORMATIVE)  . . . . . . . . . . . . 60
     A.1.  The user composes an email . . . . . . . . . . . . . . . . 60
     A.2.  The email is signed  . . . . . . . . . . . . . . . . . . . 61
     A.3.  The email signature is verified  . . . . . . . . . . . . . 61
   Appendix B.  Usage Examples (INFORMATIVE)  . . . . . . . . . . . . 62
     B.1.  Alternate Submission Scenarios . . . . . . . . . . . . . . 63
     B.2.  Alternate Delivery Scenarios . . . . . . . . . . . . . . . 65
   Appendix C.  Creating a Public Key (INFORMATIVE) . . . . . . . . . 67
   Appendix D.  MUA Considerations  . . . . . . . . . . . . . . . . . 68
   Appendix E.  Acknowledgements  . . . . . . . . . . . . . . . . . . 69

1. Introduction โœจ

DKIM defines a mechanism where email messages can be cryptographically signed ๐Ÿ–Š๏ธ. That lets a signing domain claim responsibility for putting a message into the mail stream ๐ŸŒŠ. Recipients can verify the signature by asking the signer's domain directly for the right public key, and that confirms the message was vouched for by someone holding the private key for the signing domain ๐Ÿ”‘.

The DKIM approach is different from earlier message signing systems, like Secure/Multipurpose Internet Mail Extensions (S/MIME) [RFC1847] and OpenPGP [RFC2440], and here's how ๐Ÿ’…:

  • the signature lives in a message header field, so neither human readers nor existing MUA (Mail User Agent) software gets confused by signature stuff showing up in the body;
  • it doesn't depend on public and private key pairs being issued by well-known, trusted certificate authorities;
  • it doesn't depend on deploying any new Internet protocols or services for public key distribution or revocation;
  • a failed signature verification doesn't force rejection of the message;
  • encryption isn't part of the mechanism, no attempt is made there;
  • archiving messages isn't a design goal.

And DKIM is also:

  • compatible with the existing email infrastructure and as transparent as it possibly can be ๐Ÿซง;
  • needs minimal new infrastructure;
  • can be implemented independently of clients, so deployment doesn't take forever โฉ;
  • can be deployed bit by bit, incrementally;
  • allows signing to be delegated to third parties ๐Ÿค.

1.1. Signing Identity ๐Ÿชช

DKIM keeps two questions apart: who the signer of the message is, and who the purported author of the message is. A signature includes the identity of the signer, and verifiers can use that signing info to decide how they want to process the message ๐Ÿง. The signing identity goes in the signature header field.

INFORMATIVE RATIONALE: The signing identity in a DKIM signature isn't required to match an address in any particular header field, because recipient mail systems (MUAs included) interpret things in lots of broad ways.

1.2. Scalability ๐Ÿ“ˆ

DKIM is built for the extreme scalability needs of the email identification problem ๐ŸŒ. There are currently over 70 million domains, and a much larger number of individual addresses. DKIM wants to keep the good stuff about today's email infrastructure, like anyone being able to talk to anyone else without an introduction ๐Ÿ’Œ.

1.3. Simple Key Management ๐Ÿ—๏ธ

DKIM is different from traditional hierarchical public-key systems, because no Certificate Authority infrastructure is needed. The verifier asks for the public key from a repository in the claimed signer's domain directly, not from some third party ๐Ÿ™…โ€โ™€๏ธ.

The DNS is proposed as the first mechanism for the public keys, so DKIM depends on DNS administration and the security of the DNS system ๐ŸŒ. It's designed to be extensible to other key fetching services as they show up ๐Ÿงš.

2. Terminology and Definitions ๐Ÿ“–

This section defines the terms used in the rest of the document. Syntax descriptions use the form described in Augmented BNF for Syntax Specifications [RFC4234] ๐Ÿงฉ.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be read the way [RFC2119] describes ๐ŸŽ€.

2.1. Signers โœ๏ธ

Parts of the mail system that sign messages on behalf of a domain are called signers ๐Ÿ’œ. These can be MUAs (Mail User Agents), MSAs (Mail Submission Agents), MTAs (Mail Transfer Agents), or other agents like mailing list exploders. Generally, any signer gets involved in injecting a message into the message system somehow. The key issue is that a message must be signed before it leaves the administrative domain of the signer ๐Ÿšช.

2.2. Verifiers ๐Ÿ”

Parts of the mail system that verify signatures are called verifiers ๐Ÿฆ‹. These can be MTAs, Mail Delivery Agents (MDAs), or MUAs. Usually, verifiers are expected to sit close to an end user (reader) of the message, or to some consuming agent like a mailing list exploder ๐ŸŒธ.

2.3. Whitespace ๐Ÿซถ

okay so whitespace comes in three flavors, bestie ๐Ÿ“

  • WSP is the simple one: a space or a tab character (formal definition in [RFC4234]).
  • LWSP is linear whitespace, which is WSP plus CRLF (formal definition in [RFC4234]).
  • FWS is folding whitespace. It lets multiple lines separated by CRLF, followed by at least one whitespace, get joined together โœจ

the formal ABNF for these is below (WSP and LWSP are just for information, ngl):

    WSP =   SP / HTAB
    LWSP =  *(WSP / CRLF WSP)
    FWS =   [*WSP CRLF] 1*WSP

the definition of FWS is basically identical to the one in [RFC2822], except it leaves out obs-FWS. ๐Ÿ’…

2.4. Common ABNF Tokens ๐ŸŒธ

these ABNF tokens get reused all over the doc, so they're the main characters here ๐Ÿ‘‘

  hyphenated-word =  ALPHA [ *(ALPHA / DIGIT / "-") (ALPHA / DIGIT) ]
  base64string =     1*(ALPHA / DIGIT / "+" / "/" / [FWS])
                     [ "=" [FWS] [ "=" [FWS] ] ]

2.5. Imported ABNF Tokens ๐Ÿฆ‹

some tokens get borrowed from other RFCs. those RFCs are the source of truth, so they win if there's any drama ๐Ÿ™ˆ

the following tokens are imported from [RFC2821]:

  • "Local-part" (implementation warning: this permits quoted strings)
  • "sub-domain"

the following tokens are imported from [RFC2822]:

  • "field-name" (name of a header field)
  • "dot-atom-text" (in the Local-part of an email address)

the following tokens are imported from [RFC2045]:

  • "qp-section" (a single line of quoted-printable-encoded text)
  • "hex-octet" (a quoted-printable encoded octet)
INFORMATIVE NOTE: heads up, the ABNF in RFC 2045 doesn't follow the rules of RFC 4234, so you have to read it accordingly, especially when it comes to case folding. ๐Ÿ™ƒ

everything else that isn't defined here gets imported from [RFC4234]. those are the intuitive basics like SP, HTAB, WSP, ALPHA, DIGIT, CRLF, etc. ๐Ÿงš

2.6. DKIM-Quoted-Printable ๐Ÿ’œ

the DKIM-Quoted-Printable encoding is giving Quoted-Printable [RFC2045], Section 6.7 vibes, but with its own twist. any character MAY be encoded as an "=" followed by two hexadecimal digits from the alphabet "0123456789ABCDEF" (no lowercase characters permitted), representing the hexadecimal-encoded integer value of that character. all control characters (those with values < %x20), 8-bit characters (values > %x7F), and the characters DEL (%x7F), SPACE (%x20), and semicolon (";", %x3B) MUST be encoded. note that all whitespace, including SPACE, CR, and LF characters, MUST be encoded. after encoding, FWS MAY be added at arbitrary locations to avoid super long lines, but that whitespace is NOT part of the value, and it MUST be removed before decoding. ๐Ÿซ 

ABNF:

    dkim-quoted-printable =
                      *(FWS / hex-octet / dkim-safe-char)
                 ; hex-octet is from RFC 2045
    dkim-safe-char =   %x21-3A / %x3C / %x3E-7E
                 ; '!' - ':', '<', '>' - '~'
                 ; Characters not listed as "mail-safe" in
                 ; RFC 2049 are also not recommended.
INFORMATIVE NOTE: DKIM-Quoted-Printable is its own thing and differs from the RFC 2045 Quoted-Printable in a few important ways ๐Ÿ‘€ 1. whitespace in the input text, including CR and LF, must be encoded. RFC 2045 doesn't require that, and it doesn't allow encoding CR or LF characters that are part of a CRLF line break. 2. whitespace in the encoded text gets ignored. this is so tags encoded with DKIM-Quoted-Printable can be wrapped wherever needed. RFC 2045 says line breaks in the input have to stay as physical line breaks, and that's not the case here. 3. the "soft line break" syntax ("=" as the last non-whitespace character on the line) doesn't apply. 4. DKIM-Quoted-Printable doesn't require encoded lines to be no more than 76 characters long (although other requirements may apply depending on the context the encoded text is used in).

3. Protocol Elements ๐Ÿ’…

protocol elements are the conceptual bits of the protocol that aren't specific to signers or verifiers. the protocol descriptions for signers and verifiers live in later sections (Signer Actions (Section 5) and Verifier Actions (Section 6)). NOTE: this section must be read in the context of those sections. ๐ŸŽ€

3.1. Selectors ๐ŸŽ€

to support multiple concurrent public keys per signing domain, the key namespace gets split up using "selectors". for example, selectors might be the names of office locations (e.g., "sanfrancisco", "coolumbeach", and "reykjavik"), the signing date (e.g., "january2005", "february2005", etc.), or even the individual user. ๐Ÿ—บ๏ธ

selectors are needed for some important use cases, like:

  • domains that want to delegate signing capability for a specific address, for a given duration, to a partner such as an advertising provider or other outsourced function.
  • domains that want to let frequent travelers send messages locally without needing to connect to a particular MSA.
  • "affinity" domains (e.g., college alumni associations) that forward incoming mail but don't operate a mail submission agent for outgoing mail.

periods are allowed in selectors and act as component separators. when keys get retrieved from the DNS, the periods in selectors define DNS label boundaries, similar to how they work in domain names. selector components might combine dates with locations, for example "march2005.reykjavik". in a DNS implementation, this can allow delegation of a portion of the selector namespace. ๐Ÿงฉ

ABNF:

   selector =   sub-domain *( "." sub-domain )

the number of public keys and corresponding selectors for each domain is decided by the domain owner. lots of owners will be happy with just one selector, while administratively distributed organizations may want to manage different selectors and key pairs in different regions or on different email servers. ๐ŸŒ

beyond admin convenience, selectors make it possible to seamlessly swap out public keys on a routine basis. if a domain wants to switch from the public key tied to selector "january2005" to the one tied to selector "february2005", it just makes sure both public keys are advertised in the public-key repository at the same time for the transition period, during which email may still be in transit before verification. at the start of the transition period, the outbound email servers get configured to sign with the "february2005" private key. at the end of the transition period, the "january2005" public key gets removed from the public-key repository. ๐Ÿ”„

INFORMATIVE NOTE: a key may also be revoked, as described below. the difference between revoking a key selector record and removing it is subtle. when phasing out keys like described above, a signing domain would probably just remove the key record after the transition period. however, a signing domain could choose to revoke the key (but keep the key record) for a further period. there's no defined semantic difference between a revoked key and a removed key. ๐Ÿคทโ€โ™€๏ธ

some domains might want to make selector values well known, but others will want to be careful not to allocate selector names in a way that lets outside parties harvest data. for example, if per-user keys get issued, the domain owner has to decide whether to tie the selector directly to the user name, or make it some unassociated random value, like a fingerprint of the public key. ๐Ÿ•ต๏ธโ€โ™€๏ธ

INFORMATIVE OPERATIONS NOTE: reusing a selector with a new key (for example, changing the key associated with a user's name) makes it impossible to tell the difference between a message that didn't verify because the key is no longer valid and a message that's actually forged. for this reason, signers are ill-advised to reuse selectors for new keys. a better strategy is to assign new keys to new selectors. ๐Ÿšซ

3.2. Tag=Value Lists ๐Ÿฆ‹

DKIM uses a simple "tag=value" syntax in several contexts, including messages and domain signature records. ๐Ÿ“

values are a series of strings containing plain text, "base64" text (as defined in [RFC2045], Section 6.8), "qp-section" (ibid, Section 6.7), or "dkim-quoted-printable" (as defined in Section 2.6). the name of the tag decides the encoding of each value. unencoded semicolon (";") characters MUST NOT occur in the tag value, since that separates tag-specs. ๐Ÿช„

INFORMATIVE IMPLEMENTATION NOTE: even though the "plain text" defined below (as "tag-value") only includes 7-bit characters, an implementation that wants to anticipate future standards would be advised not to rule out UTF8-encoded text in tag=value lists. ๐ŸŒ

formally, the syntax rules look like this:

     tag-list  =  tag-spec 0*( ";" tag-spec ) [ ";" ]
     tag-spec  =  [FWS] tag-name [FWS] "=" [FWS] tag-value [FWS]
     tag-name  =  ALPHA 0*ALNUMPUNC
     tag-value =  [ tval 0*( 1*(WSP / FWS) tval ) ]
                       ; WSP and FWS prohibited at beginning and end
     tval      =  1*VALCHAR
     VALCHAR   =  %x21-3A / %x3C-7E
                       ; EXCLAMATION to TILDE except SEMICOLON
     ALNUMPUNC =  ALPHA / DIGIT / "_"

note that WSP is allowed anywhere around tags. in particular, any WSP after the "=" and any WSP before the terminating ";" is not part of the value. WSP inside the value, though, is significant. ๐Ÿ‘€

tags MUST be interpreted in a case-sensitive manner. values MUST be processed as case sensitive, unless the specific tag description of semantics says they're case insensitive. โœจ

tags with duplicate names MUST NOT occur within a single tag-list. if a tag name occurs more than once, the entire tag-list is invalid. ๐Ÿ’€

whitespace within a value MUST be retained, unless the specific tag description explicitly excludes it. ๐Ÿซง

tag=value pairs that represent the default value MAY be included to aid legibility. ๐ŸŒท

unrecognized tags MUST be ignored. ๐Ÿ™ˆ

tags that have an empty value are not the same as omitted tags. an omitted tag is treated as having the default value, while a tag with an empty value explicitly designates the empty string as the value. for example, "g=" does not mean "g=", even though "g=" is the default for that tag. ๐Ÿง 

3.3. Signing and Verification Algorithms ๐ŸŒˆ

DKIM supports multiple digital signature algorithms. two algorithms are defined by this specification right now: rsa-sha1 and rsa-sha256. rsa-sha256 is the default if no algorithm is specified. verifiers MUST implement both rsa-sha1 and rsa-sha256. signers MUST implement and SHOULD sign using rsa-sha256. ๐Ÿ”

INFORMATIVE NOTE: even though sha256 is strongly encouraged, some senders of low-security messages (such as routine newsletters) may prefer sha1 because it needs less CPU to compute a sha1 hash. in general, sha256 should always be used whenever possible. ๐Ÿ“ฐโœจ

3.3.1. The rsa-sha1 Signing Algorithm โœจ

okay so the rsa-sha1 signing algorithm first makes a message hash (see Section 3.7 below) using SHA-1 [FIPS.180-2.2002] as the hash-alg ๐Ÿ”. then the signer signs that hash with RSA (from PKCS#1 version 1.5 [RFC3447]) as the crypt-alg, using their private key ๐Ÿ”‘. the hash MUST NOT be truncated or converted into any form other than the native binary form before it gets signed, no cap ๐Ÿ™…. also the signing algorithm SHOULD use a public exponent of 65537 ๐Ÿ’….

3.3.2. The rsa-sha256 Signing Algorithm ๐ŸŒธ

this one is basically the same vibe, except it uses SHA-256 [FIPS.180-2.2002] as the hash-alg ๐Ÿ’œ. the message hash gets signed by the signer with RSA (PKCS#1 version 1.5 [RFC3447]) as the crypt-alg and the signer's private key ๐Ÿ—๏ธ. same rule as before: the hash MUST NOT be truncated or converted into any form other than the native binary form before being signed ๐Ÿซถ.

3.3.3. Key Sizes ๐ŸŽ€

picking a key size is literally a balancing act between cost, performance, and risk ๐Ÿคน. short RSA keys are easier to crack with off-line attacks, so signers MUST use RSA keys of at least 1024 bits for long-lived keys ๐Ÿ”’. verifiers MUST be able to validate signatures with keys from 512 bits up to 2048 bits, and they MAY be able to validate bigger ones too ๐Ÿฆ‹. verifier policies may use the length of the signing key as one metric for deciding whether a signature is acceptable ๐Ÿ‘€.

factors that should influence the key size choice include:

  • The practical constraint that large (e.g., 4096 bit) keys may not fit within a 512-byte DNS UDP response packet ๐Ÿ’€
  • The security constraint that keys smaller than 1024 bits are subject to off-line attacks ๐Ÿ™ˆ
  • Larger keys impose higher CPU costs to verify and sign email
  • Keys can be replaced on a regular basis, thus their lifetime can be relatively short ๐Ÿ”„
  • The security goals of this specification are modest compared to typical goals of other systems that employ digital signatures ๐Ÿ“

see [RFC3766] for more on picking key sizes โœจ

3.3.4. Other Algorithms ๐Ÿงš

other algorithms MAY be defined in the future ๐Ÿ”ฎ. verifiers MUST ignore any signatures using algorithms they don't implement, so no drama ๐Ÿ’–.

3.4. Canonicalization ๐Ÿงผ

okay this part is giving "who gets to decide what counts as a change" ๐Ÿ˜ญ. empirical evidence shows that some mail servers and relay systems modify email in transit, and that can totally break a signature ๐Ÿ’”. there are two competing perspectives here. for most signers, mild modification of email is immaterial to the authentication status of the email, so for them a canonicalization algorithm that survives modest in-transit modification is preferred ๐ŸŒˆ.

other signers are super strict. any modification of the email, however minor, should result in a signature verification failure, and these signers prefer a canonicalization algorithm that does not tolerate in-transit modification of the signed email ๐Ÿ™…โ€โ™€๏ธ.

some signers may be willing to accept modifications to header fields that are within the bounds of email standards such as [RFC2822], but are unwilling to accept any modification to the body of messages ๐Ÿซ .

so to make everyone happy, two canonicalization algorithms are defined for each of the header and the body โœจ. there's a "simple" algorithm that tolerates almost no modification, and a "relaxed" algorithm that tolerates common modifications such as whitespace replacement and header field line rewrapping ๐Ÿฆ‹. a signer MAY specify either algorithm for header or body when signing an email. if no canonicalization algorithm is specified by the signer, the "simple" algorithm defaults for both header and body. verifiers MUST implement both canonicalization algorithms. the header and body may use different canonicalization algorithms, which is very on-brand for them ๐Ÿ’…. further canonicalization algorithms MAY be defined in the future, and verifiers MUST ignore any signatures that use unrecognized canonicalization algorithms ๐Ÿšซ.

canonicalization just preps the email for presentation to the signing or verification algorithm ๐ŸŽ€. it MUST NOT change the transmitted data in any way ๐Ÿซถ. the header and body canonicalization are described below.

NOTE: This section assumes that the message is already in "network normal" format (text is ASCII encoded, lines are separated with CRLF characters, etc.). See also Section 5.3 for information about normalizing the message.

3.4.1. The "simple" Header Canonicalization Algorithm ๐Ÿค

the "simple" header canonicalization algorithm does not change header fields in any way, zero edits, full stop ๐ŸงŠ. header fields MUST be presented to the signing or verification algorithm exactly as they are in the message being signed or verified. in particular, header field names MUST NOT be case folded and whitespace MUST NOT be changed ๐Ÿ™Œ.

3.4.2. The "relaxed" Header Canonicalization Algorithm ๐ŸŒท

the "relaxed" header canonicalization algorithm is the glow-up version ๐Ÿ’–. it MUST apply the following steps in order:

  • Convert all header field names (not the header field values) to lowercase. For example, convert "SUBJect: AbC" to "subject: AbC".
  • Unfold all header field continuation lines as described in [RFC2822]. in particular, lines with terminators embedded in continued header field values (that is, CRLF sequences followed by WSP) MUST be interpreted without the CRLF. Implementations MUST NOT remove the CRLF at the end of the header field value.
  • Convert all sequences of one or more WSP characters to a single SP character. WSP characters here include those before and after a line folding boundary.
  • Delete all WSP characters at the end of each unfolded header field value.
  • Delete any WSP characters remaining before and after the colon separating the header field name from the header field value. The colon separator MUST be retained.

3.4.3. The "simple" Body Canonicalization Algorithm ๐Ÿซง

the "simple" body canonicalization algorithm ignores all empty lines at the end of the message body. an empty line is a line of zero length after removing the line terminator ๐Ÿ™‚. if there's no body or no trailing CRLF on the message body, a CRLF gets added. it makes no other changes to the message body. in more formal terms, it converts 0*CRLF at the end of the body to a single CRLF ๐Ÿงฎ.

note that a completely empty or missing body gets canonicalized as a single CRLF, so the canonicalized length will be 2 octets ๐Ÿผ.

3.4.4. The "relaxed" Body Canonicalization Algorithm ๐ŸŒŸ

the "relaxed" body canonicalization algorithm:

  • Ignores all whitespace at the end of lines. Implementations MUST NOT remove the CRLF at the end of the line.
  • Reduces all sequences of WSP within a line to a single SP character.
  • Ignores all empty lines at the end of the message body. "Empty line" is defined in Section 3.4.3.
INFORMATIVE NOTE: It should be noted that the relaxed body canonicalization algorithm may enable certain types of extremely crude "ASCII Art" attacks where a message may be conveyed by adjusting the spacing between words. If this is a concern, the "simple" body canonicalization algorithm should be used instead. ๐ŸŽจ

3.4.5. Body Length Limits ๐Ÿ“

a body length count MAY be specified to limit the signature calculation to an initial prefix of the body text, measured in octets. if the body length count isn't specified, the entire message body is signed ๐Ÿ”.

INFORMATIVE RATIONALE: This capability is provided because it is very common for mailing lists to add trailers to messages (e.g., instructions how to get off the list). Until those messages are also signed, the body length count is a useful tool for the verifier since it may as a matter of policy accept messages having valid signatures with extraneous data. ๐Ÿ“ฌ
INFORMATIVE IMPLEMENTATION NOTE: Using body length limits enables an attack in which an attacker modifies a message to include content that solely benefits the attacker. It is possible for the appended content to completely replace the original content in the end recipient's eyes and to defeat duplicate message detection algorithms. To avoid this attack, signers should be wary of using this tag, and verifiers might wish to ignore the tag or remove text that appears after the specified content length, perhaps based on other criteria. ๐Ÿšจ

the body length count lets the signer of a message allow data to be appended to the end of the body of a signed message ๐Ÿง. the body length count MUST be calculated following the canonicalization algorithm. for example, any whitespace ignored by a canonicalization algorithm is not included as part of the body length count. signers of MIME messages that include a body length count SHOULD be sure that the length extends to the closing MIME boundary string ๐Ÿงท.

INFORMATIVE IMPLEMENTATION NOTE: A signer wishing to ensure that the only acceptable modifications are to add to the MIME postlude would use a body length count encompassing the entire final MIME boundary string, including the final "--CRLF". A signer wishing to allow additional MIME parts but not modification of existing parts would use a body length count extending through the final MIME boundary string, omitting the final "--CRLF". Note that this only works for some MIME types, e.g., multipart/mixed but not multipart/signed. ๐Ÿง 

a body length count of zero means the body is completely unsigned ๐Ÿซฅ.

signers who want to make sure no modification of any sort can happen should specify the "simple" canonicalization algorithm for both header and body, and omit the body length count ๐Ÿ”๐Ÿ’….

3.4.6. Canonicalization Examples (INFORMATIVE) ๐Ÿฐ

in the following examples, actual whitespace is only used for clarity. the actual input and output text is marked with bracketed descriptors: "<SP>" for a space character, "<HTAB>" for a tab character, and "<CRLF>" for a carriage-return/line-feed sequence. for example, "X <SP> Y" and "X<SP>Y" represent the same three characters ๐Ÿ”.

Example 1: A message reading:

    A: <SP> X <CRLF>
    B <SP> : <SP> Y <HTAB><CRLF>
    <HTAB> Z <SP><SP><CRLF>
    <CRLF>
    <SP> C <SP><CRLF>
    D <SP><HTAB><SP> E <CRLF>
    <CRLF>
    <CRLF>

when canonicalized using relaxed canonicalization for both header and body, the header reads:

    a:X <CRLF>
    b:Y <SP> Z <CRLF>

and the body reads:

    <SP> C <CRLF>
    D <SP> E <CRLF>

Example 2: The same message canonicalized using simple canonicalization for both header and body results in a header reading:

    A: <SP> X <CRLF>
    B <SP> : <SP> Y <HTAB><CRLF>
    <HTAB> Z <SP><SP><CRLF>

and a body reading:

    <SP> C <SP><CRLF>
    D <SP><HTAB><SP> E <CRLF>

Example 3: When processed using relaxed header canonicalization and simple body canonicalization, the canonicalized version has a header of:

    a:X <CRLF>
    b:Y <SP> Z <CRLF>

and a body reading:

    <SP> C <SP><CRLF>
    D <SP><HTAB><SP> E <CRLF>

3.5. The DKIM-Signature Header Field ๐Ÿ–‹๏ธ

the signature of the email is stored in the DKIM-Signature header field ๐Ÿ’Œ. this header field holds all of the signature and key-fetching data, and its value is a tag-list as described in Section 3.2 ๐Ÿ’œ.

the DKIM-Signature header field SHOULD be treated as though it were a trace header field as defined in Section 3.6 of [RFC2822]. that means it SHOULD NOT be reordered, and it SHOULD be prepended to the message ๐Ÿงšโ€โ™€๏ธโœจ.

The DKIM-Signature header field being made or checked is always part of the signature math ๐ŸŽ€, and it goes after all the other header fields being signed โœจ. But here's the twist ๐Ÿ™ˆ: while the math is happening, the value of the b= tag (the signature value) in that very DKIM-Signature field MUST be treated as though it were an empty string. Unknown tags in the DKIM-Signature header field MUST be included in the signature calculation but MUST be otherwise ignored by verifiers ๐Ÿซถ. Other DKIM-Signature header fields that are included in the signature should be treated as normal header fields ๐Ÿ’–, so the b= tag isn't treated specially over there ๐Ÿฆ‹.

The encodings for each field type are listed below ๐ŸŒธ. Tags described as qp-section are encoded as described in Section 6.7 of MIME Part One [RFC2045], with the additional conversion of semicolon characters to "=3B". Intuitively, that's one line of quoted-printable encoded text ๐Ÿ’œ. The dkim-quoted-printable syntax is defined in Section 2.6.

Here are the tags on the DKIM-Signature header field, along with their type and requirement status ๐Ÿ‘‘. Unrecognized tags MUST be ignored ๐Ÿงš.

v= Version (MUST be included) ๐Ÿ“. This tag defines the version of this specification that applies to the signature record. It MUST have the value "1". Verifiers must do a string comparison on this value, so "1" is not the same as "1.0" ๐Ÿ™ˆ.

sig-v-tag   = %x76 [FWS] "=" [FWS] "1"

INFORMATIVE NOTE: DKIM-Signature version numbers are expected to increase arithmetically as new versions of this specification are released ๐ŸŒˆ.

a= The algorithm used to generate the signature (plain-text; REQUIRED) ๐Ÿ”ฅ. Verifiers MUST support "rsa-sha1" and "rsa-sha256". Signers SHOULD sign using "rsa-sha256" โœจ. See Section 3.3 for a description of algorithms.

sig-a-tag       = %x61 [FWS] "=" [FWS] sig-a-tag-alg
sig-a-tag-alg   = sig-a-tag-k "-" sig-a-tag-h
sig-a-tag-k     = "rsa" / x-sig-a-tag-k
sig-a-tag-h     = "sha1" / "sha256" / x-sig-a-tag-h
x-sig-a-tag-k   = ALPHA *(ALPHA / DIGIT)   ; for later extension
x-sig-a-tag-h   = ALPHA *(ALPHA / DIGIT)   ; for later extension

b= The signature data (base64; REQUIRED) ๐Ÿฆ‹. Whitespace is ignored in this value and MUST be ignored when reassembling the original signature. In particular, the signing process can safely insert FWS in this value in arbitrary places to conform to line-length limits. See Signer Actions (Section 5) for how the signature is computed ๐Ÿ’….

sig-b-tag       = %x62 [FWS] "=" [FWS] sig-b-tag-data
sig-b-tag-data  = base64string

bh= The hash of the canonicalized body part of the message as limited by the "l=" tag (base64; REQUIRED) ๐ŸŒธ. Whitespace is ignored in this value and MUST be ignored when reassembling the original signature. In particular, the signing process can safely insert FWS in this value in arbitrary places to conform to line-length limits. See Section 3.7 for how the body hash is computed ๐Ÿซถ.

sig-bh-tag      = %x62 %x68 [FWS] "=" [FWS] sig-bh-tag-data
sig-bh-tag-data = base64string

c= Message canonicalization (plain-text; OPTIONAL, default is "simple/simple") ๐ŸŽ€. This tag tells the verifier which type of canonicalization was used to prepare the message for signing. It's two names separated by a "slash" (%d47) character, matching the header and body canonicalization algorithms respectively. These algorithms are described in Section 3.4. If only one algorithm is named, that algorithm is used for the header and "simple" is used for the body. For example, "c=relaxed" is treated the same as "c=relaxed/simple" ๐Ÿ’œ.

sig-c-tag       = %x63 [FWS] "=" [FWS] sig-c-tag-alg
                ["/" sig-c-tag-alg]
sig-c-tag-alg   = "simple" / "relaxed" / x-sig-c-tag-alg
x-sig-c-tag-alg = hyphenated-word    ; for later extension

d= The domain of the signing entity (plain-text; REQUIRED) ๐Ÿ‘‘. This is the domain that will be queried for the public key. This domain MUST be the same as or a parent domain of the "i=" tag (the signing identity, as described below), or it MUST meet the requirements for parent domain signing described in Section 3.8. When presented with a signature that does not meet these requirement, verifiers MUST consider the signature invalid ๐Ÿšซ.

Internationalized domain names MUST be encoded as described in [RFC3490] ๐ŸŒ.

sig-d-tag       = %x64 [FWS] "=" [FWS] domain-name
domain-name     = sub-domain 1*("." sub-domain)
         ; from RFC 2821 Domain, but excluding address-literal

h= Signed header fields (plain-text, but see description; REQUIRED) ๐Ÿ“. This is a colon-separated list of header field names that identify the header fields presented to the signing algorithm. The field MUST contain the complete list of header fields in the order presented to the signing algorithm. The field MAY contain names of header fields that do not exist when signed. Nonexistent header fields do not contribute to the signature computation (that is, they are treated as the null input, including the header field name, the separating colon, the header field value, and any CRLF terminator) ๐Ÿ™ˆ. The field MUST NOT include the DKIM-Signature header field that is being created or verified, but may include others. Folding whitespace (FWS) MAY be included on either side of the colon separator. Header field names MUST be compared against actual header field names in a case-insensitive manner. This list MUST NOT be empty. See Section 5.4 for a discussion of choosing header fields to sign โœจ.

sig-h-tag       = %x68 [FWS] "=" [FWS] hdr-name
                0*( *FWS ":" *FWS hdr-name )
hdr-name        = field-name

INFORMATIVE EXPLANATION: By "signing" header fields that do not actually exist, a signer can prevent insertion of those header fields before verification. However, since a signer cannot possibly know what header fields might be created in the future, and since some MUAs might present header fields that are embedded inside a message (e.g., as a message/rfc822 content type), the security of this solution is not total ๐Ÿ˜ญ.

INFORMATIVE EXPLANATION: The exclusion of the header field name and colon as well as the header field value for non-existent header fields prevents an attacker from inserting an actual header field with a null value ๐Ÿ”.

i= Identity of the user or agent (e.g., a mailing list manager) on behalf of which this message is signed (dkim-quoted-printable; OPTIONAL, default is an empty Local-part followed by an "@" followed by the domain from the "d=" tag) ๐Ÿงš. The syntax is a standard email address where the Local-part MAY be omitted. The domain part of the address MUST be the same as or a subdomain of the value of the "d=" tag.

Internationalized domain names MUST be converted using the steps listed in Section 4 of [RFC3490] using the "ToASCII" function ๐ŸŒ.

sig-i-tag =   %x69 [FWS] "=" [FWS] [ Local-part ] "@" domain-name

INFORMATIVE NOTE: The Local-part of the "i=" tag is optional because, in some cases, a signer may not be able to establish a verified individual identity. In such cases, the signer may wish to assert that although it is willing to go as far as signing for the domain, it is unable or unwilling to commit to an individual user name within their domain. It can do so by including the domain part but not the Local-part of the identity ๐ŸŽ€.

INFORMATIVE DISCUSSION: This document does not require the value of the "i=" tag to match the identity in any message header fields. This is considered to be a verifier policy issue. Constraints between the value of the "i=" tag and other identities in other header fields seek to apply basic authentication into the semantics of trust associated with a role such as content author. Trust is a broad and complex topic, and trust mechanisms are subject to highly creative attacks. The real-world efficacy of any but the most basic bindings between the "i=" value and other identities is not well established, nor is its vulnerability to subversion by an attacker. Hence, reliance on the use of these options should be strictly limited. In particular, it is not at all clear to what extent a typical end-user recipient can rely on any assurances that might be made by successful use of the "i=" options ๐Ÿค”.

l= Body length count (plain-text unsigned decimal integer; OPTIONAL, default is entire body) ๐ŸŒˆ. This tag tells the verifier the number of octets in the body of the email after canonicalization that were included in the cryptographic hash, starting from 0 immediately following the CRLF preceding the body. This value MUST NOT be larger than the actual number of octets in the canonicalized message body.

INFORMATIVE IMPLEMENTATION WARNING: Use of the "l=" tag might allow display of fraudulent content without appropriate warning to end users ๐Ÿšจ. The "l=" tag is intended for increasing signature robustness when sending to mailing lists that both modify their content and do not sign their messages. However, using the "l=" tag enables attacks in which an intermediary with malicious intent modifies a message to include content that solely benefits the attacker. It is possible for the appended content to completely replace the original content in the end recipient's eyes, and to defeat duplicate message detection algorithms. Examples are described in Security Considerations (Section 8). To avoid this attack, signers should be extremely wary of using this tag, and verifiers might wish to ignore the tag or remove text that appears after the specified content length ๐Ÿ’€.

INFORMATIVE NOTE: The value of the "l=" tag is constrained to 76 decimal digits. This constraint is not intended to predict the size of future messages or to require implementations to use an integer representation large enough to represent the maximum possible value. It's intended to remind the implementer to check the length of this and all other tags during verification, and to test for integer overflow when decoding the value. Implementers may need to limit the actual value expressed to a value smaller than 10^76, e.g., to allow a message to fit within the available storage space ๐Ÿงฎ.

sig-l-tag    = %x6c [FWS] "=" [FWS] 1*76DIGIT

okay bestie, we're picking up right in the middle of the DKIM-Signature tag list ๐Ÿ’… so q= is up first ๐ŸŽ€

q= is a colon-separated list of query methods used to fetch the public key ๐Ÿ” It's plain-text and OPTIONAL, and if you don't set it the default is "dns/txt" ๐Ÿ’– Each method looks like "type[/options]", and what the options mean depends on the type and the options you pick โœจ If you list more than one query mechanism, the choice of mechanism MUST NOT change how the signature is interpreted ๐Ÿซถ And implementations MUST try the recognized query mechanisms in the order they're listed ๐Ÿงš

Right now "dns/txt" is the only valid value, and it's the DNS TXT record lookup algorithm described elsewhere in this document ๐ŸŒธ The only option defined for the "dns" query type is "txt", and that one MUST be included ๐Ÿ’œ Verifiers and signers MUST support "dns/txt" ๐Ÿ˜ญ

sig-q-tag        = %x71 [FWS] "=" [FWS] sig-q-tag-method
               *([FWS] ":" [FWS] sig-q-tag-method)
sig-q-tag-method = "dns/txt" / x-sig-q-tag-type
               ["/" x-sig-q-tag-args]
x-sig-q-tag-type = hyphenated-word  ; for future extension
x-sig-q-tag-args = qp-hdr-value

s= is the selector, and it's how the namespace for the "d=" (domain) tag gets subdivided ๐Ÿฆ‹ It's plain-text and REQUIRED, so you can't skip it ๐Ÿ™ˆ

sig-s-tag    = %x73 [FWS] "=" [FWS] selector

t= is the Signature Timestamp ๐Ÿ•ฐ๏ธ It's a plain-text unsigned decimal integer and RECOMMENDED, and if it's missing the default is an unknown creation time โญ It records when the signature was created, counted as seconds since 00:00:00 on January 1, 1970 in the UTC time zone ๐ŸŒˆ The value is an unsigned integer in decimal ASCII, and it is NOT limited to fit into a 31- or 32-bit integer, so it can get really big ๐Ÿ‘‘ Implementations SHOULD be ready to handle values up to at least 10^12, which is around AD 200,000 and fits into 40 bits ๐Ÿ”ฅ To dodge denial-of-service attacks, implementations MAY treat any value longer than 12 digits as infinite ๐Ÿ›ก๏ธ Leap seconds are not counted ๐Ÿ“ And implementations MAY ignore signatures whose timestamp is in the future ๐Ÿ™ˆ

sig-t-tag    = %x74 [FWS] "=" [FWS] 1*12DIGIT

x= is the Signature Expiration ๐Ÿ’€ It's a plain-text unsigned decimal integer and RECOMMENDED, and the default is no expiration at all โœจ It uses the same format as "t=", but it's an absolute date and not a time delta from the signing timestamp ๐ŸŽ€ The value is an unsigned decimal ASCII integer that follows the same constraints as the "t=" tag ๐Ÿ’… Signatures MAY be considered invalid if the verification time at the verifier is past the expiration date ๐Ÿ˜ญ The verification time SHOULD be the time the message first arrived in the verifier's administrative domain, as long as that time is reliably available ๐Ÿซถ If it isn't, the current time SHOULD be used instead ๐Ÿงš And if both "t=" and "x=" are present, the value of "x=" MUST be greater than the value of "t=" ๐Ÿ‘‘

INFORMATIVE NOTE: The "x=" tag is not intended as an anti-replay defense. Please don't rely on it for that ๐Ÿ™…โ€โ™€๏ธ
sig-x-tag    = %x78 [FWS] "=" [FWS] 1*12DIGIT

z= is for copied header fields ๐ŸŒท The text there is dkim-quoted-printable, but check the description, it's a bit of a twist ๐Ÿฆ‹ It's OPTIONAL, and the default is null ๐Ÿ“ It's a vertical-bar-separated list of selected header fields that were present when the message got signed, and each one includes both the field name and the value ๐Ÿ’– You don't have to copy every header that existed at signing time, and the fields in "z=" don't have to match the ones listed in "h=" either ๐ŸŒธ Any vertical bar ("|", %x7C) inside a copied header has to be encoded, because bars in the "z=" text are metacharacters ๐Ÿ™ˆ All whitespace has to be encoded too, including whitespace between the colon and the header value ๐Ÿ’… After encoding, FWS MAY be added at arbitrary spots to keep lines from getting super long, but that whitespace is NOT part of the header value and MUST be removed before decoding ๐Ÿ˜ญ

The header fields referenced by the "h=" tag point at the fields in the RFC 2822 header of the message, not at any copied fields in "z=" ๐Ÿงš Copied header values are only for diagnostic use ๐Ÿ”

Header fields with characters that need conversion (maybe from legacy MTAs that aren't [RFC2822] compliant) SHOULD be converted as described in MIME Part Three [RFC2047] ๐ŸŒˆ

sig-z-tag      = %x7A [FWS] "=" [FWS] sig-z-tag-copy
             *( [FWS] "|" sig-z-tag-copy )
sig-z-tag-copy = hdr-name ":" qp-hdr-value
qp-hdr-value   = dkim-quoted-printable    ; with "|" encoded

Here's an INFORMATIVE EXAMPLE of a signature header spread across a few continuation lines ๐ŸŽ€

DKIM-Signature: a=rsa-sha256; d=example.net; s=brisbane;
   c=simple; q=dns/txt; i=@eng.example.net;
   t=1117574938; x=1118006938;
   h=from:to:subject:date;
   z=From:foo@eng.example.net|To:joe@example.com|
     Subject:demo=20run|Date:July=205,=202005=203:44:08=20PM=20-0700;
   bh=MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=;
   b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZ
            VoG4ZHRNiYzR

3.6. Key Management and Representation ๐Ÿ”‘

okay this part is giving trust ๐Ÿฅน Signature applications need some level of assurance that the verification public key really belongs to the signer who's claimed ๐Ÿ’– Lots of apps get that by using public key certificates from a trusted third party ๐ŸŽ€ But DKIM can reach a solid level of security with way better scalability, just by having the verifier query the purported signer's DNS entry (or some security-equivalent) to get the public key โœจ That's the move ๐Ÿ’…

DKIM keys can be stored in several kinds of key servers and in several formats ๐ŸŒˆ How keys are stored and formatted doesn't matter to the rest of the DKIM algorithm ๐Ÿซถ

The inputs to the key lookup algorithm are the lookup type (the "q=" tag), the signer's domain (the "d=" tag of the DKIM-Signature header field), and the selector (the "s=" tag) ๐Ÿงš

public_key = dkim_find_key(q_val, d_val, s_val)

This document defines one binding, which uses DNS TXT records to distribute the keys ๐ŸŒธ Other bindings may be defined in the future ๐Ÿ”ฎ

3.6.1. Textual Representation โœจ

okay so bestie, a lot of key servers are gonna store keys as plain, unstructured text ๐ŸŽ€ (an XML form doesn't count as unstructured text for this, fyi ๐Ÿ™ˆ). Whenever a DKIM key shows up in that kind of text, this exact definition MUST be used ๐Ÿ’…. The overall syntax is a tag-list, same as in Section 3.2 ๐Ÿฆ‹. Here are the tags that are valid right now. Other tags MAY be present, and any implementation that doesn't understand them MUST ignore them ๐Ÿซถ

v= ๐Ÿ’– This is the version of the DKIM key record (plain-text, RECOMMENDED, and it defaults to "DKIM1"). If it's there, the tag MUST be set to "DKIM1" (no quotes). It also MUST be the very first tag in the record, and any record starting with a v= tag set to something else MUST be discarded ๐Ÿ˜ญ. Verifiers have to do a string comparison on this value, so "DKIM1" is NOT the same as "DKIM1.0" ๐Ÿ™…โ€โ™€๏ธ

ABNF:

key-v-tag    = %x76 [FWS] "=" [FWS] "DKIM1"

g= ๐Ÿงš This is the granularity of the key (plain-text, OPTIONAL, default is ""). The value MUST match the Local-part of the i= tag in the DKIM-Signature header field (or the default empty string if i= isn't specified). There's a single optional "" that matches zero or more arbitrary characters, which is called wildcarding โœจ. If the signing address doesn't match this value, that's a failed verification ๐Ÿ’€. The whole point is to limit which signing address can use this selector, like when you hand a key to a third party that should only be used for special purposes ๐Ÿ‘‘. Wildcarding lets you match things like "user+" or "-offer" ๐Ÿ“. An empty g= value never matches any addresses ๐Ÿ™ˆ

ABNF:

key-g-tag       = %x67 [FWS] "=" [FWS] key-g-tag-lpart
key-g-tag-lpart = [dot-atom-text] ["*" [dot-atom-text] ]

h= ๐ŸŒธ These are the acceptable hash algorithms (plain-text, OPTIONAL, and it defaults to allowing all algorithms). It's a colon-separated list of hash algorithms that might get used. Signers and Verifiers MUST support the "sha256" hash algorithm, and Verifiers MUST also support the "sha1" hash algorithm ๐Ÿ”ฅ

ABNF:

key-h-tag       = %x68 [FWS] "=" [FWS] key-h-tag-alg
                     0*( [FWS] ":" [FWS] key-h-tag-alg )
key-h-tag-alg   = "sha1" / "sha256" / x-key-h-tag-alg
x-key-h-tag-alg = hyphenated-word   ; for future extension

k= ๐Ÿฆ‹ This is the key type (plain-text, OPTIONAL, default is "rsa"). Signers and verifiers MUST support the "rsa" key type โœจ. "rsa" means an ASN.1 DER-encoded [ITU.X660.1997] RSAPublicKey [RFC3447] (see Sections 3.1 and A.1.1) is being used in the p= tag. (Side note: the p= tag also encodes the value using base64 ๐Ÿ’…)

ABNF:

key-k-tag        = %x76 [FWS] "=" [FWS] key-k-tag-type
key-k-tag-type   = "rsa" / x-key-k-tag-type
x-key-k-tag-type = hyphenated-word   ; for future extension

n= ๐Ÿ“ These are notes that might be of interest to a human (qp-section, OPTIONAL, default is empty). No program interprets them. Use this one sparingly in any key server mechanism with space limits (DNS, I'm looking at you ๐Ÿ‘€). It's meant for administrators, not end users ๐Ÿ“

ABNF:

key-n-tag    = %x6e [FWS] "=" [FWS] qp-section

p= ๐Ÿ’œ This is the public-key data (base64, REQUIRED). An empty value means the public key has been revoked ๐Ÿ’€. The syntax and semantics of this tag's value before base64 encoding are defined by the k= tag.

INFORMATIVE RATIONALE: If a private key has been compromised or otherwise disabled (e.g., an outsourcing contract has been terminated), a signer might want to explicitly state that it knows about the selector, but all messages using that selector should fail verification. Verifiers should ignore any DKIM-Signature header fields with a selector referencing a revoked key.

ABNF:

key-p-tag    = %x70 [FWS] "=" [ [FWS] base64string ]
INFORMATIVE NOTE: A base64string is permitted to include white space (FWS) at arbitrary places; however, any CRLFs must be followed by at least one WSP character. Implementors and administrators are cautioned to ensure that selector TXT records conform to this specification. ๐Ÿ™

s= ๐ŸŒˆ This is the service type (plain-text, OPTIONAL, default is "*"). It's a colon-separated list of service types this record applies to. Verifiers for a given service type MUST ignore this record if that type isn't listed. The currently defined service types are:

  • * matches all service types
  • email is electronic mail (not necessarily limited to SMTP)

This tag exists to constrain key use for other purposes, in case DKIM gets defined for other services down the road ๐Ÿ”ฎ

ABNF:

key-s-tag        = %x73 [FWS] "=" [FWS] key-s-tag-type
                   0*( [FWS] ":" [FWS] key-s-tag-type )
key-s-tag-type   = "email" / "*" / x-key-s-tag-type
x-key-s-tag-type = hyphenated-word   ; for future extension

t= โญ These are flags, a colon-separated list of names (plain-text, OPTIONAL, default is no flags set). The defined flags are:

  • y means this domain is testing DKIM. Verifiers MUST NOT treat messages from signers in testing mode differently from unsigned email, even if the signature fails to verify ๐Ÿ˜ญ. Verifiers MAY wish to track testing mode results to help the signer ๐Ÿง
  • s means any DKIM-Signature header fields using the i= tag MUST have the same domain value on the right-hand side of the "@" in the i= tag and the value of the d= tag. Basically, the i= domain MUST NOT be a subdomain of d= ๐Ÿ™…โ€โ™€๏ธ. Use of this flag is RECOMMENDED unless subdomaining is required โœจ

ABNF:

key-t-tag        = %x74 [FWS] "=" [FWS] key-t-tag-flag
                   0*( [FWS] ":" [FWS] key-t-tag-flag )
key-t-tag-flag   = "y" / "s" / x-key-t-tag-flag
x-key-t-tag-flag = hyphenated-word   ; for future extension

Unrecognized flags MUST be ignored ๐Ÿซถ

3.6.2. DNS Binding ๐ŸŒ

A binding using DNS TXT records as a key service is hereby defined, and every implementation MUST support it. No exceptions, bestie ๐Ÿ’…

3.6.2.1. Namespace ๐Ÿงš

All DKIM keys live in a subdomain called "_domainkey" ๐Ÿก. Say you've got a DKIM-Signature field with a d= tag of "example.com" and an s= tag of "foo.bar". The DNS query will be for "foo.bar._domainkey.example.com" ๐ŸŽ€

INFORMATIVE OPERATIONAL NOTE: Wildcard DNS records (e.g., *.bar._domainkey.example.com) do not make sense in this context and should not be used. Note also that wildcards within domains (e.g., s._domainkey.*.example.com) are not supported by the DNS. ๐Ÿ™ˆ

3.6.2.2. Resource Record Types for Key Storage ๐Ÿ’พ

The DNS Resource Record type gets picked by an option to the query-type (q=) tag. The only option defined in this base specification is "txt", which means a TXT Resource Record (RR) ๐Ÿ“œ. A later extension of this standard might define another RR type, so keep an eye out ๐Ÿ‘€

Strings in a TXT RR MUST be concatenated together before use, with no whitespace in between ๐Ÿงต. TXT RRs MUST be unique for a particular selector name. If there are multiple records in an RRset, the results are undefined, and that's a whole mess nobody wants ๐Ÿ’€

TXT RRs are encoded as described in Section 3.6.1 ๐Ÿ’–

3.7. Computing the Message Hashes โœจ

okay so before anything gets signed or checked, you have to compute two hashes over the message ๐Ÿง Signers pick the signature parameters (see Signer Actions (Section 5)), and verifiers use the parameters in the DKIM-Signature header field they're verifying. We're talking about the tag names in the DKIM-Signature header field that either exists (when verifying) or will be created (when signing). Also, canonicalization (Section 3.4) is only there to prep the email for signing or verifying. It does NOT change the transmitted email in any way ๐Ÿ’…

The signer/verifier MUST compute two hashes, one over the body of the message and one over the selected header fields of the message. ๐ŸŒธ

Signers MUST compute them in the order shown. Verifiers MAY compute them in any order convenient to the verifier, as long as the result is semantically identical to what you'd get if they'd been computed in this order ๐Ÿฆ‹

In hash step 1, the signer/verifier MUST hash the message body, canonicalized with the body canonicalization algorithm named in the c= tag, and then truncated to the length in the l= tag. Then that hash gets converted to base64 and either inserted into (signers) or compared to (verifiers) the bh= tag of the DKIM-Signature header field ๐Ÿ“

In hash step 2, the signer/verifier MUST pass the following to the hash algorithm, in this exact order:

  1. The header fields named in the h= tag, in the order that tag lists them, canonicalized with the header canonicalization algorithm from the c= tag. Each header field MUST be terminated with a single CRLF.
  2. The DKIM-Signature header field that exists (verifying) or will be inserted (signing) in the message, with the value of the b= tag deleted (i.e., treated as the empty string), canonicalized with the header canonicalization algorithm from the c= tag, and without a trailing CRLF.

Okay this part is kinda the main event ๐Ÿ”ฅ All the tags and their values in the DKIM-Signature header field get included in the cryptographic hash, with ONE exception: the value part of the b= (signature) tag, which MUST be treated as the null string. All tags MUST be included even if the verifier doesn't understand them ๐Ÿ™ˆ The DKIM-Signature header field MUST be presented to the hash algorithm after the body of the message, not with the rest of the header fields, and it MUST be canonicalized as specified in the c= (canonicalization) tag. The DKIM-Signature header field MUST NOT be included in its own h= tag, although other DKIM-Signature header fields MAY be signed (see Section 4) โœจ

When you're hashing messages that will be sent using base64 or quoted-printable encoding, signers MUST compute the hash AFTER the encoding. Likewise, the verifier MUST incorporate the values into the hash before decoding the base64 or quoted-printable text. BUT the hash MUST be computed before transport level encodings like SMTP "dot-stuffing" (the thing where lines starting with a "." get modified so they don't get confused with the SMTP end-of-message marker, as specified in [RFC2821]) ๐Ÿ’€

Except for the canonicalization procedure in Section 3.4, the DKIM signing process treats the body of messages as just a string of octets. DKIM messages MAY be either plain-text or MIME format, and no special treatment is afforded to MIME content. Message attachments in MIME format MUST be included in the content that is signed ๐Ÿ“Ž

Here's the more formal version of the algorithm:

    body-hash = hash-alg(canon_body)
    header-hash = hash-alg(canon_header || DKIM-SIG)
    signature = sig-alg(header-hash, key)

where "sig-alg" is the signature algorithm named by the "a=" tag, "hash-alg" is the hash algorithm named by the "a=" tag, "canon_header" and "canon_body" are the canonicalized message header and body (respectively) as defined in Section 3.4 (excluding the DKIM-Signature header field), and "DKIM-SIG" is the canonicalized DKIM-Signature header field sans the signature value itself, but with "body-hash" included as the "bh=" tag.

INFORMATIVE IMPLEMENTERS' NOTE: Many digital signature APIs give you both hashing and application of the RSA private key in one single "sign()" primitive. If you're using one of those, the last two steps in the algorithm would probably get combined into one call that does both the "hash-alg" and the "sig-alg" ๐Ÿงš

3.8. Signing by Parent Domains ๐ŸŒณ

Sometimes a domain wants to sign on behalf of any of its subdomains WITHOUT having to maintain separate selectors (key records) in each subdomain. By default, private keys tied to key records can be used to sign messages for any subdomain of the domain they live in. For example, a key record for the domain example.com can verify messages where the signing identity (the i= tag of the signature) is sub.example.com, or even sub1.sub2.example.com ๐Ÿ˜Œ

To limit what these keys can do when that's not intended, the "s" flag may be set in the t= tag of the key record. That constrains the validity of the record to exactly the domain of the signing identity. If the referenced key record has the "s" flag as part of its t= tag, the domain of the signing identity (the "i=" flag) MUST be the same as the d= domain. If that flag is absent, the domain of the signing identity MUST be the same as, or a subdomain of, the d= domain. Key records that are NOT meant for use with subdomains SHOULD specify the "s" flag in the t= tag ๐Ÿ”’


4. Semantics of Multiple Signatures ๐Ÿฆ„

4.1. Example Scenarios ๐ŸŽ€

There are lots of reasons a message might end up with multiple signatures. For example, a signer might sign multiple times, maybe with different hashing or signing algorithms during a transition phase ๐Ÿ”„

INFORMATIVE EXAMPLE: Say SHA-256 is someday found to be insufficiently strong, and DKIM usage transitions to SHA-1024. A signer might immediately sign using the newer algorithm, but keep signing with the older algorithm for interoperability with verifiers that haven't upgraded yet. The signer would do this by adding two DKIM-Signature header fields, one using each algorithm. Older verifiers that didn't recognize SHA-1024 as an acceptable algorithm would skip that signature and use the older algorithm. Newer verifiers could use either signature at their option, and all other things being equal might not even bother trying to verify the other signature ๐Ÿ’…

Similarly, a signer might sign a message including all headers and no l= tag (to satisfy strict verifiers), AND a second time with a limited set of headers and an l= tag (in anticipation of possible message modifications on the way to other verifiers). Verifiers could then choose which signature they prefer ๐Ÿ‘‘

INFORMATIVE EXAMPLE: A verifier might receive a message with two signatures, one covering more of the message than the other. If the signature covering more of the message verifies, the verifier could make one set of policy decisions. If that signature fails but the signature covering less of the message verifies, the verifier might make a different set of policy decisions ๐Ÿง

Of course, a message might also have multiple signatures because it went through multiple signers. A common case is expected to be a signed message that passes through a mailing list that also signs every message. Assuming both of those signatures verify, a recipient might choose to accept the message if either signature is known to come from a trusted source ๐Ÿ“ฌ

INFORMATIVE EXAMPLE: Recipients might choose to whitelist mailing lists they've subscribed to and that have acceptable anti-abuse policies, so they accept messages sent to that list even from unknown authors. They might also subscribe to less trusted mailing lists (e.g., ones without anti-abuse protection) and be willing to accept all messages from specific authors, but insist on doing additional abuse scanning for other messages ๐Ÿ›ก๏ธ

Another related example of multiple signers is forwarding services, like the ones commonly associated with academic alumni sites ๐ŸŽ“

INFORMATIVE EXAMPLE: A recipient might have an address at members.example.org, a site with anti-abuse protection that's somewhat less effective than the recipient would prefer. That recipient might have specific authors whose messages would be trusted absolutely, but messages from unknown authors that had passed the forwarder's scrutiny would have only medium trust ๐ŸŒˆ

4.2. Interpretation โœจ

Okay so bestie, when a signer is just adding a signature to a message, it literally makes a brand new DKIM-Signature header, using the usual h= option semantics ๐Ÿ’…. A signer MAY sign previously existing DKIM-Signature header fields too, using the method from Section 5.4 to sign trace header fields ๐Ÿฆ‹.

Informative note: Signers should be cognizant that signing DKIM-Signature header fields may result in signature failures with intermediaries that don't recognize DKIM-Signature header fields as trace header fields. Those intermediaries might unwittingly reorder them, which breaks the signatures ๐Ÿ˜ญ. So signing existing DKIM-Signature header fields is unadvised, albeit legal ๐Ÿ™ˆ.
Informative note: If a header field with multiple instances is signed, those instances are always signed from the bottom up ๐Ÿ”. That means you can't sign only specific DKIM-Signature header fields. For example, if the message already has three DKIM-Signature header fields A, B, and C, you can sign all of them, B and C only, or C only. You can't do A only, B only, A and B only, or A and C only. Not me being picky ๐Ÿ’€.

A signer MAY add more than one DKIM-Signature header field using different parameters. Like, during a transition period a signer might want to produce signatures with two different hash algorithms ๐ŸŒˆ.

Signers SHOULD NOT remove any DKIM-Signature header fields from messages they're signing, even if they know those signatures can't be verified ๐Ÿซถ.

When a verifier evaluates a message with multiple signatures, it SHOULD evaluate each signature independently and on its own merits โœจ. For example, a verifier whose policy chooses not to accept signatures made with deprecated cryptographic algorithms would consider those signatures invalid ๐Ÿ”ฅ. Verifiers MAY process signatures in any order they like. Some verifiers might choose to process signatures corresponding to the From field first, before the others ๐Ÿ‘‘. See Section 6.1 for more info about signature choices ๐Ÿงš.

Informative implementation note: Verifier attempts to correlate valid signatures with invalid signatures, trying to guess why a signature failed, are ill-advised ๐Ÿ™…โ€โ™€๏ธ. Especially because there's no general way for a verifier to determine that an invalid signature was ever valid ๐Ÿ’….

Verifiers SHOULD ignore failed signatures as though they weren't present in the message. Verifiers SHOULD continue checking signatures until one successfully verifies to the satisfaction of the verifier ๐Ÿ“. To limit potential denial-of-service attacks, verifiers MAY limit the total number of signatures they'll attempt to verify ๐Ÿ›ก๏ธ.


5. Signer Actions ๐Ÿ’…

The following steps are performed in order by signers ๐Ÿ“‹.

5.1. Determine Whether the Email Should Be Signed and by Whom ๐Ÿค”

A signer can obviously only sign email for domains it has a private key for, plus the necessary knowledge of the corresponding public key and selector information ๐Ÿ”‘. But there are a bunch of other reasons, beyond not having a private key, why a signer could choose not to sign an email ๐Ÿ™ˆ.

Informative note: Signing modules can be built into any part of the mail system, including an MUA, a SUBMISSION server, or an MTA ๐Ÿงš. Wherever it's implemented, signers should be careful about signing (and thereby asserting responsibility for) messages that might be problematic ๐Ÿ˜ฌ. For example, inside a trusted enclave the signing address might be derived from the header according to local policy. SUBMISSION servers might only sign messages from users who are properly authenticated and authorized ๐Ÿ”.
Informative implementer advice: SUBMISSION servers should not sign Received header fields if the outgoing gateway MTA obfuscates Received header fields, for example to hide the details of internal topology ๐Ÿ•ต๏ธโ€โ™€๏ธ.

If an email can't be signed for some reason, what happens to it is a local policy decision ๐ŸŽ€.

5.2. Select a Private Key and Corresponding Selector Information ๐Ÿ—๏ธ

This specification doesn't define how a signer should choose which private key and selector information to use ๐Ÿคทโ€โ™€๏ธ. Right now, all selectors are equal as far as this spec is concerned, so the decision is mostly about administrative convenience ๐Ÿ’…. Distribution and management of private keys is also outside the scope of this document ๐ŸŒธ.

Informative operations advice: A signer should not sign with a private key when the selector containing the corresponding public key is expected to be revoked or removed before the verifier has a chance to validate the signature โณ. The signer should expect that verifiers might defer validation, maybe until the message is actually read by the final recipient ๐Ÿ“ฌ. Especially when rotating to a new key pair, signing should immediately start with the new private key, and the old public key should be kept for a reasonable validation interval before it's removed from the key server ๐Ÿ”„.

5.3. Normalize the Message to Prevent Transport Conversions ๐Ÿงผ

Some messages, especially ones using 8-bit characters, get modified in transit, most notably converted to 7-bit form ๐Ÿ˜ฑ. Those conversions will break DKIM signatures. To minimize the chances of breakage, signers SHOULD convert the message to a suitable MIME content transfer encoding, like quoted-printable or base64, as described in MIME Part One [RFC2045], before signing ๐ŸŽ€. That conversion is outside the scope of DKIM. The actual message SHOULD be converted to 7-bit MIME by an MUA or MSA before it's presented to the DKIM algorithm โœจ.

If the message is submitted to the signer with any local encoding that will be modified before transmission, that modification to canonical [RFC2822] form MUST be done before signing ๐Ÿ™Œ. Specifically, bare CR or LF characters (some systems use these as a local line separator convention) MUST be converted to the SMTP-standard CRLF sequence before the message is signed ๐Ÿ’œ. Any conversion like this SHOULD be applied to the message actually sent to the recipient(s), not just to the version presented to the signing algorithm ๐Ÿฆ‹.

More generally, the signer MUST sign the message as it's expected to be received by the verifier, rather than in some local or internal form ๐Ÿ‘‘.

5.4. Determine the Header Fields to Sign ๐Ÿ“

The From header field MUST be signed (that is, included in the h= tag of the resulting DKIM-Signature header field) ๐Ÿ’–. Signers SHOULD NOT sign an existing header field that's likely to be legitimately modified or removed in transit ๐Ÿ™…โ€โ™€๏ธ. Specifically, [RFC2821] explicitly permits modification or removal of the Return-Path header field in transit ๐Ÿ›ค๏ธ. Signers MAY include any other header fields present at the time of signing, at the signer's discretion ๐ŸŒท.

Informative operations note: Choosing which header fields to sign is kinda non-obvious ๐Ÿคฏ. One strategy is to sign all existing, non-repeatable header fields. Another is to sign only header fields that are likely to be displayed to, or otherwise likely to affect how the message is processed by, the receiver. A third strategy is to sign only "well known" headers ๐Ÿญ. Note that verifiers may treat unsigned header fields with extreme skepticism, including refusing to display them to the end user, or even ignoring the signature if it doesn't cover certain header fields ๐Ÿ‘€. For this reason, signing fields present in the message such as Date, Subject, Reply-To, Sender, and all MIME header fields is highly advised ๐Ÿซถ.

The DKIM-Signature header field is always implicitly signed, and it MUST NOT be included in the h= tag except to indicate that other preexisting signatures are also signed ๐Ÿ”.

Signers MAY claim to have signed header fields that don't exist. That is, signers MAY include the header field name in the h= tag even if that header field isn't in the message ๐Ÿซฅ. When computing the signature, the non-existing header field MUST be treated as the null string, including the header field name, header field value, all punctuation, and the trailing CRLF ๐Ÿ’€.

Informative rationale: This lets signers explicitly assert the absence of a header field. If that header field gets added later, the signature will fail ๐Ÿšจ.
Informative note: A header field name only needs to be listed once more than the actual number of that header field in a message at the time of signing, in order to prevent any further additions ๐Ÿงฎ. For example, if there's a single Comments header field at signing time, listing Comments twice in the h= tag is enough to prevent any number of Comments header fields from being appended. It's not necessary (but it's legal) to list Comments three or more times in the h= tag ๐ŸŒˆ.

Signers who choose to sign an existing header field that occurs more than once in the message (such as Received) MUST sign the physically last instance of that header field in the header block ๐Ÿซก. Signers who want to sign multiple instances of such a header field MUST include the header field name multiple times in the h= tag of the DKIM-Signature header field, and MUST sign those header fields in order from the bottom of the header field block to the top โฌ†๏ธ. The signer MAY include more instances of a header field name in h= than there are actual corresponding header fields, to indicate that additional header fields of that name SHOULD NOT be added ๐Ÿšซ.

Informative example:

If the signer wants to sign two existing Received header fields, and the existing header contains:

Received: <A>
Received: <B>
Received: <C>

then the resulting DKIM-Signature header field should read:

DKIM-Signature: ... h=Received : Received : ...

and Received header fields <C> and <B> will be signed in that order ๐Ÿฆ‹.

Signers should be careful about signing header fields that might get additional instances added later in the delivery process ๐Ÿ˜ฌ. Those header fields might be inserted after the signed instance, or otherwise reordered. Trace header fields (such as Received) and Resent-* blocks are the only fields [RFC2822] prohibits from being reordered ๐Ÿ›ค๏ธ. Since DKIM-Signature header fields may be reordered by some intermediate MTAs, signing existing DKIM-Signature header fields is error-prone ๐Ÿ’….

Informative admonition: Even though [RFC2822] permits header fields to be reordered (except Received header fields), reordering of signed header fields with multiple instances by intermediate MTAs will break DKIM signatures ๐Ÿ’”. Such anti-social behavior should be avoided ๐Ÿ™…โ€โ™€๏ธ.
Informative implementer's note: Although this specification doesn't require it, all end-user visible header fields should be signed to avoid possible "indirect spamming" ๐Ÿšซ๐Ÿ“ง. For example, if the Subject header field isn't signed, a spammer can resend a previously signed mail and replace the legitimate subject with a one-line spam ๐Ÿคก. Not okay!! ๐Ÿ˜ค

Okay bestie, so to play nicely with the widest range of verifiers ๐Ÿฆ‹, it is recommended that signers follow the practices in this section when signing a message. These are generic recommendations for the general case, though, so specific senders may wish to tweak them for their own situations โœจ. Verifiers MUST be capable of verifying signatures even if one or more of the recommended header fields is not signed (with the exception of From, which must always be signed), or if one or more of the disrecommended header fields is signed ๐Ÿซถ. Verifiers do have the option of ignoring signatures that don't cover a sufficient portion of the header or body, just as they may ignore signatures from an identity they don't trust ๐Ÿ’€.

The following header fields SHOULD be included in the signature, if they are present in the message being signed:

  • From (REQUIRED in all signatures)
  • Sender, Reply-To
  • Subject
  • Date, Message-ID
  • To, Cc
  • MIME-Version
  • Content-Type, Content-Transfer-Encoding, Content-ID, Content-Description
  • Resent-Date, Resent-From, Resent-Sender, Resent-To, Resent-Cc, Resent-Message-ID
  • In-Reply-To, References
  • List-Id, List-Help, List-Unsubscribe, List-Subscribe, List-Post, List-Owner, List-Archive

The following header fields SHOULD NOT be included in the signature:

  • Return-Path
  • Received
  • Comments, Keywords
  • Bcc, Resent-Bcc
  • DKIM-Signature

Optional header fields (the ones not mentioned above) normally SHOULD NOT be included in the signature ๐Ÿ™ˆ, because additional header fields of the same name might get legitimately added or reordered before verification. There are likely to be legitimate exceptions to this rule, though, because there's a huge variety of application-specific header fields that can be applied to a message, and some of them are unlikely to be duplicated, modified, or reordered ๐ŸŒˆ.

Signers SHOULD choose canonicalization algorithms based on the types of messages they process and how much risk they're willing to take. For example, e-commerce sites sending mostly purchase receipts, which aren't expected to go through mailing lists or other software likely to modify messages, will generally prefer "simple" canonicalization. Sites sending mostly person-to-person email will likely prefer to be more resilient to modification during transport, so they'll use "relaxed" canonicalization ๐Ÿ“.

Signers SHOULD NOT use l= unless they intend to accommodate intermediate mail processors that append text to a message. For example, lots of mailing list processors append "unsubscribe" info to message bodies. If signers do use l=, they SHOULD include the entire message body that exists at the time of signing when computing the count. In particular, signers SHOULD NOT specify a body length of 0, since some verifiers may interpret that as a meaningless signature ๐Ÿ‘‘.

5.6. Compute the Message Hash and Signature ๐Ÿ”ฅ

The signer MUST compute the message hash as described in Section 3.7 and then sign it using the selected public-key algorithm. This results in a DKIM-Signature header field that includes the body hash and a signature of the header hash, where that header includes the DKIM-Signature header field itself ๐Ÿงš.

Entities such as mailing list managers that implement DKIM, and that modify the message or a header field (for example, inserting unsubscribe information) before retransmitting the message, SHOULD check any existing signature on input and MUST make such modifications before re-signing the message ๐Ÿ’œ.

The signer MAY elect to limit the number of bytes of the body that will be included in the hash, and hence signed. The length actually hashed should be inserted in the l= tag of the DKIM-Signature header field โœจ.

5.7. Insert the DKIM-Signature Header Field ๐ŸŽ€

Finally, the signer MUST insert the DKIM-Signature header field created in the previous step before transmitting the email. The DKIM-Signature header field MUST be the same as the one used to compute the hash as described above, except that the value of the b= tag MUST be the appropriately signed hash computed in the previous step. That hash gets signed using the algorithm specified in the a= tag of the DKIM-Signature header field, and using the private key corresponding to the selector given in the s= tag of the DKIM-Signature header field, as chosen above in Section 5.2 ๐Ÿฆ‹

The DKIM-Signature header field MUST be inserted before any other DKIM-Signature fields in the header block.

INFORMATIVE IMPLEMENTATION NOTE: The easiest way to achieve this is to insert the DKIM-Signature header field at the beginning of the header block. In particular, it may be placed before any existing Received header fields. This is consistent with treating DKIM-Signature as a trace header field ๐ŸŒธ.

6. Verifier Actions ๐Ÿซง

Since a signer MAY remove or revoke a public key at any time, it is recommended that verification happen in a timely manner. In many configurations, the most timely place is during acceptance by the border MTA or shortly after. In particular, deferring verification until the end user accesses the message is discouraged ๐Ÿ˜ญ.

A border or intermediate MTA MAY verify the message signature(s). An MTA that has performed verification MAY communicate the result by adding a verification header field to incoming messages. This considerably simplifies things for the user, who can now use an existing mail user agent ๐ŸŒˆ. Most MUAs can filter messages based on header fields or content, and those filters would be used to implement whatever policy the user wants regarding unsigned mail โœจ.

A verifying MTA MAY implement a policy regarding unverifiable mail, regardless of whether it applies the verification header field to signed messages ๐Ÿ’….

Verifiers MUST produce a result that is semantically equivalent to applying the following steps in the order listed. In practice, several of these steps can be performed in parallel to improve performance ๐Ÿ“.

6.1. Extract Signatures from the Message ๐Ÿ”

The order in which verifiers try DKIM-Signature header fields is not defined; verifiers MAY try signatures in any order they like. For example, one implementation might try the signatures in textual order, whereas another might try signatures by identities that match the contents of the From header field before trying other signatures. Verifiers MUST NOT attribute ultimate meaning to the order of multiple DKIM-Signature header fields. In particular, there's reason to believe some relays will reorder the header fields in potentially arbitrary ways ๐Ÿ™ƒ.

INFORMATIVE IMPLEMENTATION NOTE: Verifiers might use the order as a clue to signing order in the absence of any other information. However, other clues as to the semantics of multiple signatures (such as correlating the signing host with Received header fields) may also be considered.

A verifier SHOULD NOT treat a message that has one or more bad signatures and no good signatures differently from a message with no signature at all. That kind of treatment is a matter of local policy and is beyond the scope of this document ๐Ÿซถ.

When a signature successfully verifies, a verifier will either stop processing or attempt to verify any other signatures, at the discretion of the implementation. A verifier MAY limit the number of signatures it tries, to avoid denial-of-service attacks ๐Ÿ›ก๏ธ.

INFORMATIVE NOTE: An attacker could send messages with large numbers of faulty signatures, each of which would require a DNS lookup and corresponding CPU time to verify the message. This could be an attack on the domain that receives the message, slowing the verifier down by requiring it to do a huge number of DNS lookups and/or signature verifications. It could also be an attack against the domains listed in the signatures, essentially by enlisting innocent verifiers to launch an attack against the DNS servers of the actual victim ๐Ÿ˜ฑ.

In the following description, text reading "return status (explanation)" (where "status" is one of PERMFAIL or TEMPFAIL) means that the verifier MUST immediately cease processing that signature. The verifier SHOULD proceed to the next signature, if any is present, and completely ignore the bad signature. If the status is PERMFAIL, the signature failed and should not be reconsidered. If the status is TEMPFAIL, the signature couldn't be verified at this time but may be tried again later. A verifier MAY either defer the message for later processing, perhaps by queueing it locally or issuing a 451/4.7.5 SMTP reply, or try another signature. If no good signature is found and any of the signatures resulted in a TEMPFAIL status, the verifier MAY save the message for later processing. The "(explanation)" is not normative text; it's provided solely for clarification ๐ŸŒŸ.

Verifiers SHOULD ignore any DKIM-Signature header fields where the signature does not validate. Verifiers that are prepared to validate multiple signature header fields SHOULD proceed to the next signature header field, should one exist. However, verifiers MAY make note of the fact that an invalid signature was present, for consideration at a later step ๐Ÿ’–.

INFORMATIVE NOTE: The rationale for this requirement is to permit messages that have invalid signatures but also a valid signature to work. For example, a mailing list exploder might opt to leave the original submitter's signature in place even though the exploder knows it's modifying the message in a way that will break that signature, and the exploder inserts its own signature. In this case, the message should succeed even in the presence of the known-broken signature ๐ŸŽ‰.

For each signature to be validated, the following steps should be performed in such a manner as to produce a result that is semantically equivalent to performing them in the indicated order ๐Ÿง

6.1.1. Validate the Signature Header Field โœจ

Okay bestie, this is the bouncer step ๐Ÿง๐Ÿ’… Implementers MUST meticulously validate the format and values in the DKIM-Signature header field. If there's any inconsistency or unexpected value, the header field MUST be completely ignored and the verifier MUST return PERMFAIL (signature syntax error). Being "liberal in what you accept" is definitely a bad strategy in this security context. ๐Ÿšซ Quick catch though: unknown tags in a DKIM-Signature header field are totally allowed, so they don't count as an inconsistency. ๐Ÿฆ‹

Verifiers MUST ignore DKIM-Signature header fields with a v= tag that is inconsistent with this specification, and they MUST return PERMFAIL (incompatible version). ๐Ÿ™…โ€โ™€๏ธ

INFORMATIVE IMPLEMENTATION NOTE: An implementation may, of course, choose to also verify signatures generated by older versions of this specification. ๐ŸŒธ

If any tag listed as "required" in Section 3.5 is missing from the DKIM-Signature header field, the verifier MUST ignore the DKIM-Signature header field and MUST return PERMFAIL (signature missing required tag). ๐Ÿฅบ

INFORMATIONAL NOTE: The tags listed as required in Section 3.5 are v=, a=, b=, bh=, d=, h=, and s=. Should there be a conflict between this note and Section 3.5, Section 3.5 is normative. ๐Ÿ“Œ

If the DKIM-Signature header field doesn't have an i= tag, the verifier MUST behave as though that tag's value were @d, where d is the value from the d= tag. ๐Ÿงš

Verifiers MUST confirm that the domain in the d= tag is the same as, or a parent domain of, the domain part of the i= tag. If it isn't, the DKIM-Signature header field MUST be ignored and the verifier SHOULD return PERMFAIL (domain mismatch). ๐Ÿ’œ

If the h= tag doesn't include the From header field, the verifier MUST ignore the DKIM-Signature header field and MUST return PERMFAIL (From field not signed). Not it! ๐Ÿ™…โ€โ™€๏ธ

Verifiers MAY ignore the DKIM-Signature header field and return PERMFAIL (signature expired) if it has an x= tag and the signature has expired. โฐ

Verifiers MAY ignore the DKIM-Signature header field if the domain the signer used in the d= tag isn't associated with a valid signing entity. For example, signatures with d= values such as com and co.uk may be ignored. The list of unacceptable domains SHOULD be configurable. ๐ŸŒ

Verifiers MAY ignore the DKIM-Signature header field and return PERMFAIL (unacceptable signature header) for any other reason. For example, if the signature doesn't sign header fields the verifier considers essential. As a case in point, if MIME header fields aren't signed, certain attacks may be possible, and the verifier would prefer to avoid them. ๐Ÿ›ก๏ธ๐Ÿ”ฅ

6.1.2. Get the Public Key ๐Ÿ”‘

The public key for a signature is needed to finish the verification process. How you fetch it depends on the query type set by the q= tag in the DKIM-Signature header field. Obviously, you only need to retrieve a public key if extracting the signature information went completely smoothly. ๐Ÿง Details of key management and representation are in Section 3.6. The verifier MUST validate the key record and MUST ignore any public key records that are malformed. ๐Ÿ™ˆ

When validating a message, a verifier MUST perform the following steps in a way that's semantically the same as doing them in the order listed. (Some implementations may parallelize or reorder these steps, as long as the semantics stay unchanged. Smart. ๐Ÿง โœจ)

  1. Retrieve the public key as described in Section 3.6, using the algorithm in the q= tag, the domain from the d= tag, and the selector from the s= tag. ๐Ÿ”Ž
  2. If the query for the public key doesn't respond, the verifier MAY defer accepting this email and return TEMPFAIL (key unavailable). If verification happens during the incoming SMTP session, this MAY be done with a 451/4.7.5 SMTP reply code. Alternatively, the verifier MAY store the message in the local queue for a later try, or ignore the signature. Heads up, though: storing messages in a local queue is subject to denial-of-service attacks. ๐Ÿ˜ฌ
  3. If the query for the public key fails because the corresponding key record doesn't exist, the verifier MUST immediately return PERMFAIL (no key for signature). ๐Ÿšซ
  4. If the query for the public key returns multiple key records, the verifier may choose one of them, or may cycle through them and perform the remaining steps on each record at the implementer's discretion. The order of the key records is unspecified. ๐ŸŽฒ If the verifier cycles through the key records, then the "return ..." wording in the rest of this section means "try the next key record, if any; if none, return to try another signature in the usual way." ๐Ÿ”
  5. If the result from the query doesn't follow the format this specification defines, the verifier MUST ignore the key record and MUST return PERMFAIL (key syntax error). Verifiers are urged to validate the syntax of key records carefully to avoid attempted attacks. ๐Ÿ›‘ In particular, the verifier MUST ignore keys with a version code (v= tag) that they don't implement. ๐Ÿ’…
  6. If the g= tag in the public key doesn't match the Local-part of the i= tag in the message signature header field, the verifier MUST ignore the key record and MUST return PERMFAIL (inapplicable key). If the Local-part of the i= tag on the message signature isn't present, the g= tag must be * (valid for all addresses in the domain), or the entire g= tag must be omitted (which defaults to g=*). Otherwise, the verifier MUST ignore the key record and MUST return PERMFAIL (inapplicable key). Apart from this test, verifiers SHOULD NOT treat a message signed with a key record that has a g= tag any differently than one without. In particular, verifiers SHOULD NOT prefer messages that seem to have an individual signature because of a g= tag versus a domain signature. ๐ŸŽ€
  7. If the h= tag exists in the public key record, and the hash algorithm implied by the a= tag in the DKIM-Signature header field isn't in the contents of the h= tag, the verifier MUST ignore the key record and MUST return PERMFAIL (inappropriate hash algorithm). ๐Ÿ™…โ€โ™€๏ธ
  8. If the public key data (the p= tag) is empty, then this key has been revoked. The verifier MUST treat this as a failed signature check and MUST return PERMFAIL (key revoked). There's no defined semantic difference between a revoked key and a key record that's been removed. ๐Ÿชฆ
  9. If the public key data isn't suitable for the algorithm and key types defined by the a= and k= tags in the DKIM-Signature header field, the verifier MUST immediately return PERMFAIL (inappropriate key algorithm). ๐Ÿ™ƒ

6.1.3. Compute the Verification ๐Ÿงฎ

Given a signer and a public key, verifying a signature means doing actions that are semantically equivalent to these steps. ๐Ÿ’–

  1. Based on the algorithm defined in the c= tag, the body length in the l= tag, and the header field names in the h= tag, prepare a canonicalized version of the message as described in Section 3.7. (This version doesn't actually need to be instantiated, babe. ๐Ÿ™Œ) When matching header field names in the h= tag against the actual message header fields, comparisons MUST be case-insensitive. ๐Ÿ” 
  2. Based on the algorithm indicated in the a= tag, compute the message hashes from the canonical copy as described in Section 3.7. ๐Ÿงพ
  3. Verify that the hash of the canonicalized message body computed in the previous step matches the hash value in the bh= tag. If the hash doesn't match, the verifier SHOULD ignore the signature and return PERMFAIL (body hash did not verify). ๐Ÿ’”
  4. Using the signature in the b= tag, verify the signature against the header hash, using the mechanism for the public key algorithm described in the a= tag. If the signature doesn't validate, the verifier SHOULD ignore the signature and return PERMFAIL (signature did not verify). ๐Ÿ’”
  5. Otherwise, the signature has verified correctly. ๐ŸŽ‰๐ŸŒˆ
INFORMATIVE IMPLEMENTER'S NOTE: Implementations might wish to start the public-key query in parallel with calculating the hash, since the public key isn't needed until the final decryption is calculated. Implementations may also verify the signature on the message header before checking that the message hash listed in the bh= tag matches the actual message body. However, if the body hash doesn't match, the entire signature must be considered to have failed. ๐Ÿงฉ

A body length in the l= tag of the signature limits how many bytes of the body get passed to the verification algorithm. All data beyond that limit is not validated by DKIM. ๐Ÿ‘€ Hence, verifiers might treat a message containing bytes beyond the indicated body length with suspicion. For example, they could truncate the message at the indicated body length, declare the signature invalid (e.g., by returning PERMFAIL (unsigned content)), or pass the partial verification along to the policy module. ๐Ÿ•ต๏ธโ€โ™€๏ธ

INFORMATIVE IMPLEMENTATION NOTE: Verifiers that truncate the body at the indicated body length might pass along a malformed MIME message if the signer used the "N-4" trick (omitting the final "--CRLF") described in the informative note in Section 3.4.5. Such verifiers may wish to check for this case and include a trailing "--CRLF" to avoid breaking the MIME structure. A simple way to do that might be to append "--CRLF" to any "multipart" message with a body length. If the MIME structure is already correctly formed, this will appear in the postlude and won't be displayed to the end user. ๐ŸŽ€โœจ

6.2. Communicate Verification Results ๐Ÿ“ฃ

Verifiers that want to share the results of verification with other parts of the mail system may do so however they like. ๐Ÿ’… For example, implementations might choose to add an email header field to the message before passing it on. Any such header field SHOULD be inserted before any existing DKIM-Signature or preexisting authentication status header fields in the header field block. ๐Ÿงฑ

INFORMATIVE ADVICE to MUA filter writers: Patterns that search for results header fields to visibly mark authenticated mail for end users should verify that the header field was added by the appropriate verifying domain, and that the verified identity matches the author identity the MUA will display. In particular, MUA filters should not be influenced by bogus results header fields added by attackers. To get around this attack, verifiers may wish to delete existing results header fields after verification and before adding a new one. ๐Ÿ›ก๏ธ๐Ÿ’œ

6.3. Interpret Results/Apply Local Policy โœจ

Okay so, what a verifier actually does with the result is literally out of scope for this spec ๐Ÿ’… But here's the tea: a signed email gives the receiving system something an unsigned one just can't. An authenticated email creates a predictable identifier, so other decisions like trust and reputation can be managed reliably ๐Ÿฆ‹ Unsigned email has no reliable identifier to hang trust and reputation on, so it's reasonable to treat it as having zero trust and no positive reputation ๐Ÿ™ˆ

In general, verifiers SHOULD NOT reject messages just because they have no signature or an unverifiable one ๐Ÿ’€ Doing that would cause severe interoperability problems, and nobody wants that drama. Still, if a verifier does decide to reject these messages (say, it's talking to a peer who agreed beforehand to only send signed messages ๐Ÿค), and the verifier runs synchronously with the SMTP session and the signature is missing or doesn't verify, the MTA SHOULD use a 550/5.7.x reply code.

If the public key can't be fetched, maybe because the key server is down, a temporary failure message MAY be generated with a 451/4.7.5 reply code, like this:

451 4.7.5 Unable to verify signature - key server unavailable

Real talk: temporary failures like being unable to reach the key server or some other external service are the ONLY conditions that SHOULD use a 4xx SMTP reply code ๐Ÿซถ Cryptographic signature verification failures MUST NOT return 4xx SMTP replies. Not ever, not even a little.

Once the signature has been verified, that info MUST be passed along to higher-level systems (like explicit allow/whitelists and reputation systems) and/or to the end user ๐ŸŽ€ If the message is signed on behalf of any address other than the one in the From: header field, the mail system SHOULD take pains to make sure the actual signing identity is clear to the reader ๐Ÿ‘‘

The verifier MAY treat unsigned header fields with extreme skepticism, including marking them as untrusted or even deleting them before the end user sees them ๐Ÿงš

Okay but here's the part that's lowkey tricky ๐Ÿ˜ญ When verification fails, the symptom is obvious (the signature doesn't verify), but figuring out the exact cause can be way harder. If a selector can't be found, did the selector get removed, or did the value get changed somehow in transit? If the signature line is missing, was it never there, or did an overzealous filter nuke it? For diagnostic purposes, the exact reason the verification failed SHOULD be made available to the policy module and possibly recorded in the system logs ๐Ÿ” And if the email can't be verified, it SHOULD be rendered the same as all unverified email, regardless of whether or not it looks like it was signed ๐Ÿ’–


7. IANA Considerations ๐ŸŒธ

DKIM introduces some new namespaces that have been registered with IANA. In all cases, new values are assigned only for values that have been documented in a published RFC that has IETF Consensus [RFC2434]. Iykyk, it's a whole vibe ๐Ÿ’…

7.1. DKIM-Signature Tag Specifications ๐Ÿท๏ธ

A DKIM-Signature provides for a list of tag specifications. IANA has established the DKIM-Signature Tag Specification Registry for tag specifications that can be used in DKIM-Signature fields.

The initial entries in the registry comprise:

                         +------+-----------------+
                         | TYPE | REFERENCE       |
                         +------+-----------------+
                         | v    | (this document) |
                         | a    | (this document) |
                         | b    | (this document) |
                         | bh   | (this document) |
                         | c    | (this document) |
                         | d    | (this document) |
                         | h    | (this document) |
                         | i    | (this document) |
                         | l    | (this document) |
                         | q    | (this document) |
                         | s    | (this document) |
                         | t    | (this document) |
                         | x    | (this document) |
                         | z    | (this document) |
                         +------+-----------------+

          DKIM-Signature Tag Specification Registry Initial Values

7.2. DKIM-Signature Query Method Registry ๐Ÿ”Ž

The "q=" tag-spec (specified in Section 3.5) provides for a list of query methods.

IANA has established the DKIM-Signature Query Method Registry for mechanisms that can be used to retrieve the key that will permit validation processing of a message signed using DKIM.

The initial entry in the registry comprises:

                     +------+--------+-----------------+
                     | TYPE | OPTION | REFERENCE       |
                     +------+--------+-----------------+
                     | dns  | txt    | (this document) |
                     +------+--------+-----------------+

             DKIM-Signature Query Method Registry Initial Values

7.3. DKIM-Signature Canonicalization Registry ๐Ÿงผ

The "c=" tag-spec (specified in Section 3.5) provides for a specifier for canonicalization algorithms for the header and body of the message.

IANA has established the DKIM-Signature Canonicalization Algorithm Registry for algorithms for converting a message into a canonical form before signing or verifying using DKIM.

The initial entries in the header registry comprise:

                        +---------+-----------------+
                        | TYPE    | REFERENCE       |
                        +---------+-----------------+
                        | simple  | (this document) |
                        | relaxed | (this document) |
                        +---------+-----------------+

         DKIM-Signature Header Canonicalization Algorithm Registry
                               Initial Values

The initial entries in the body registry comprise:

                        +---------+-----------------+
                        | TYPE    | REFERENCE       |
                        +---------+-----------------+
                        | simple  | (this document) |
                        | relaxed | (this document) |
                        +---------+-----------------+

          DKIM-Signature Body Canonicalization Algorithm Registry
                               Initial Values

7.4. _domainkey DNS TXT Record Tag Specifications ๐Ÿ“œ

A _domainkey DNS TXT record provides for a list of tag specifications. IANA has established the DKIM _domainkey DNS TXT Tag Specification Registry for tag specifications that can be used in DNS TXT Records.

The initial entries in the registry comprise:

                         +------+-----------------+
                         | TYPE | REFERENCE       |
                         +------+-----------------+
                         | v    | (this document) |
                         | g    | (this document) |
                         | h    | (this document) |
                         | k    | (this document) |
                         | n    | (this document) |
                         | p    | (this document) |
                         | s    | (this document) |
                         | t    | (this document) |
                         +------+-----------------+

          DKIM _domainkey DNS TXT Record Tag Specification Registry
                               Initial Values

7.5. DKIM Key Type Registry ๐Ÿ”‘

The "k=" <key-k-tag> (specified in Section 3.6.1) and the "a=" <sig-a-tag-k> (specified in Section 3.5) tags provide for a list of mechanisms that can be used to decode a DKIM signature.

IANA has established the DKIM Key Type Registry for such mechanisms.

The initial entry in the registry comprises:

                            +------+-----------+
                            | TYPE | REFERENCE |
                            +------+-----------+
                            | rsa  | [RFC3447] |
                            +------+-----------+

                        DKIM Key Type Initial Values

7.6. DKIM Hash Algorithms Registry ๐Ÿงฎ

The "h=" <key-h-tag> (specified in Section 3.6.1) and the "a=" <sig-a-tag-h> (specified in Section 3.5) tags provide for a list of mechanisms that can be used to produce a digest of message data.

IANA has established the DKIM Hash Algorithms Registry for such mechanisms.

The initial entries in the registry comprise:

                       +--------+-------------------+
                       | TYPE   | REFERENCE         |
                       +--------+-------------------+
                       | sha1   | [FIPS.180-2.2002] |
                       | sha256 | [FIPS.180-2.2002] |
                       +--------+-------------------+

                     DKIM Hash Algorithms Initial Values

7.7. DKIM Service Types Registry ๐Ÿ›Ž๏ธ

The "s=" <key-s-tag> tag (specified in Section 3.6.1) provides for a list of service types to which this selector may apply.

IANA has established the DKIM Service Types Registry for service types.

The initial entries in the registry comprise:

                         +-------+-----------------+
                         | TYPE  | REFERENCE       |
                         +-------+-----------------+
                         | email | (this document) |
                         | *     | (this document) |
                         +-------+-----------------+

                 DKIM Service Types Registry Initial Values

7.8. DKIM Selector Flags Registry ๐Ÿšฉ

The "t=" <key-t-tag> tag (specified in Section 3.6.1) provides for a list of flags to modify interpretation of the selector.

IANA has established the DKIM Selector Flags Registry for additional flags.

The initial entries in the registry comprise:

                         +------+-----------------+
                         | TYPE | REFERENCE       |
                         +------+-----------------+
                         | y    | (this document) |
                         | s    | (this document) |
                         +------+-----------------+

                 DKIM Selector Flags Registry Initial Values

7.9. DKIM-Signature Header Field ๐Ÿ“จ

IANA has added DKIM-Signature to the "Permanent Message Header Fields" registry (see [RFC3864]) for the "mail" protocol, using this document as the reference.


8. Security Considerations ๐Ÿ”

It has been observed that any mechanism that is introduced that attempts to stem the flow of spam is subject to intensive attack. Bestie, this is serious โœจ DKIM needs to be carefully scrutinized to identify potential attack vectors and the vulnerability to each. See also [RFC4686].

8.1. Misuse of Body Length Limits ("l=" Tag) ๐Ÿšจ

Body length limits (in the form of the "l=" tag) are subject to several potential attacks.

8.1.1. Addition of New MIME Parts to Multipart/*

Okay bestie, this one is spicy ๐Ÿ™ˆ. If the body length limit doesn't cover the closing MIME multipart section (including the trailing --CRLF bit), an attacker can intercept a properly signed multipart message and tack on a brand-new body part ๐Ÿ˜ญ. Whether that actually does damage depends on the MIME type, the verifying MTA, and the receiving MUA, but it could let the attacker swap what the end user sees for something that looks like it came from a trusted source ๐Ÿ’€.

For example, if attackers can append stuff to a text/html body part, they might exploit a bug in some MUAs that keep reading after a </html> marker. Then they could display HTML text on top of text that's already on screen ๐Ÿซฃ. If a message has a multipart/alternative body part, they might be able to add a new body part that the displaying MUA prefers, which is giving sneaky energy ๐Ÿฆ‹.

8.1.2. Addition of new HTML content to existing content

Several receiving MUA implementations don't stop rendering after a ""</html>"" tag ๐Ÿ™ƒ. Literally, this lets attackers do things like overlay images on top of existing text, and it's not cute ๐Ÿ˜ค.

INFORMATIVE EXAMPLE: Appending the following text to an existing, properly closed message will in many MUAs result in inappropriate data being rendered on top of existing, correct data:
<div style="position: relative; bottom: 350px; z-index: 2;">
<img src="http://www.ietf.org/images/ietflogo2e.gif"
  width=578 height=370>
</div>

8.2. Misappropriated Private Key

Okay so, if a user's private key lives on their computer and isn't protected by an appropriately secure mechanism, malware can send mail as that user and as any other user sharing the same private key ๐Ÿ’€. The malware wouldn't be able to generate signed spoofs of other signers' addresses though, which would actually help identify the infected user and limit the sketchy social-engineering attacks ๐Ÿ•ต๏ธโ€โ™€๏ธ. This threat mainly applies to MUA-based implementations. Protecting private keys on servers can be easily achieved with specialized cryptographic hardware ๐Ÿ”.

A bigger problem happens if malware on lots of users' computers grabs their private keys and sends them over a covert channel to a site where they can be shared ๐Ÿ˜ฑ. The compromised users would likely not know about it until they get "bounce" messages from mail they supposedly sent. Plenty of users might not understand what those bounces mean, so they wouldn't do anything about it ๐Ÿฅฒ.

One countermeasure is to use a user-entered passphrase to encrypt the private key. BUT users tend to pick weak passphrases and reuse them for other stuff, which could let an attack against DKIM spill over into other domains ๐Ÿ™ˆ. The decoded private key might also be briefly exposed to malware when it's entered, or get captured by keystroke logging. Also, the added hassle of typing a passphrase every time someone sends a message would tend to discourage using a secure one ๐Ÿ˜ฉ.

A somewhat more effective countermeasure is to send messages through an outgoing MTA that can authenticate the submitter with existing techniques (for example, SMTP Authentication). It can also validate the message itself (for example, checking that the header is legit and that the content passes a spam content check), and then sign the message with a key that matches the submitter's address โœจ. Such an MTA can also cap how much outgoing mail each user is allowed to send, which further limits malware's ability to blast out bulk email ๐Ÿ›‘.

8.3. Key Server Denial-of-Service Attacks

Since the key servers are distributed (potentially separate for each domain), the number of servers you'd need to attack to defeat this mechanism Internet-wide is huge ๐ŸŒ. Still, key servers for individual domains could be attacked, which would stop verification of messages from that domain ๐Ÿ˜ฌ. This isn't that different from an attacker denying service to the mail exchangers for a given domain, although it affects outgoing mail rather than incoming mail ๐Ÿ“ค.

A variation on this is that if a very large amount of mail were sent using spoofed addresses from a given domain, the key servers for that domain could get overwhelmed with requests ๐ŸŒŠ. However, verification has low overhead compared with handling the email message itself, so an attack like that would be hard to pull off ๐Ÿ’….

8.4. Attacks Against the DNS

Since the DNS is a required binding for key services, specific attacks against the DNS must be considered ๐Ÿง.

While the DNS is currently insecure [RFC3833], these security problems are the motivation behind DNS Security (DNSSEC) [RFC4033], and all users of the DNS will reap the benefit of that work ๐ŸŽ€.

DKIM is only intended as a "sufficient" method of proving authenticity. It is NOT meant to give strong cryptographic proof about authorship or contents. Other technologies like OpenPGP [RFC2440] and S/MIME [RFC3851] handle those requirements ๐Ÿ“œ.

A second DNS security issue is the increased DNS traffic that comes from fetching selector-based data as well as fetching signing domain policy ๐Ÿ“ˆ. Widespread DKIM deployment will cause a significant increase in DNS queries to the claimed signing domain. In the case of forgeries on a large scale, DNS servers could see a substantial spike in queries ๐Ÿ˜ญ.

A specific DNS security issue that DKIM verifiers should consider is the name chaining attack described in Section 2.3 of the DNS Threat Analysis [RFC3833]. While verifying a DKIM-Signature header field, a DKIM verifier could be prompted to retrieve a key record of an attacker's choosing ๐Ÿ‘€. This threat can be minimized by making sure that the name servers the verifier uses, including recursive name servers, strictly check "glue" and other additional information in DNS responses. That way they aren't vulnerable to this attack ๐Ÿ›ก๏ธ.

8.5. Replay Attacks

In this attack, a spammer sends a message to be spammed to an accomplice, which gets the message signed by the originating MTA ๐ŸŽญ. The accomplice then resends the message, original signature and all, to a large number of recipients. They might do this by sending it to many compromised machines acting as MTAs ๐Ÿคก. Since the messages weren't modified by the accomplice, they still have valid signatures. Chef's kiss, but make it villain ๐Ÿ˜ˆ.

Partial solutions involve using reputation services to share the fact that a specific email address is being used for spam, and that messages from that signer are likely to be spam too ๐Ÿšฉ. This needs a real-time detection mechanism to react quickly enough. Still, these measures might be prone to abuse. For example, an attacker could resend a large number of messages received from a victim to make them look like a spammer ๐Ÿ˜ต.

Large verifiers might be able to detect unusually large volumes of mail with the same signature in a short time period ๐Ÿ“Š. Smaller verifiers can get substantially the same volume of information through existing collaborative systems ๐Ÿซถ.

8.6. Limits on Revoking Keys

When a large domain detects undesirable behavior from one of its users, it might want to revoke the key used to sign that user's messages. That way it can disavow responsibility for messages that haven't been verified yet or that are the subject of a replay attack ๐Ÿ™…โ€โ™€๏ธ. BUT the domain's ability to do that can be limited if the same key, for scalability reasons, is used to sign messages for many other users ๐Ÿ˜ค. Mechanisms for explicitly revoking keys on a per-address basis have been proposed, but they still need further study regarding their utility and the DNS load they'd represent ๐Ÿง .

8.7. Intentionally Malformed Key Records

It's possible for an attacker to publish key records in DNS that are intentionally malformed, aiming to cause a denial-of-service attack on a non-robust verifier implementation ๐Ÿ’€. The attacker could then make a verifier read the malformed key record by sending a message to one of its users that references the malformed record in a (not necessarily valid) signature ๐Ÿชค. Verifiers MUST thoroughly verify all key records retrieved from the DNS, and they MUST be robust against intentionally as well as unintentionally malformed key records ๐Ÿ› ๏ธ. No cap, this one is non-negotiable ๐Ÿ’….

8.8. Intentionally Malformed DKIM-Signature Header Fields

Verifiers MUST be prepared to receive messages with malformed DKIM-Signature header fields, and they MUST thoroughly verify the header field before depending on any of its contents ๐Ÿซก. Trust nothing until you've checked it, periodt ๐Ÿ”.

8.9. Information Leakage

An attacker could figure out when a particular signature was verified by using a per-message selector and then monitoring their DNS traffic for the key lookup ๐Ÿ‘๏ธ. This would act as the equivalent of a "web bug" for verification time, rather than for when the message was read ๐Ÿ•ฐ๏ธ. Sneaky, right? ๐Ÿ™ˆ

8.10. Remote Timing Attacks

In some cases, it may be possible to extract private keys using a remote timing attack [BONEH03] ๐Ÿ˜ฑ. Implementations should consider obfuscating the timing to prevent such attacks ๐ŸŽญ.

8.11. Reordered Header Fields

Existing standards allow intermediate MTAs to reorder header fields ๐Ÿ”€. If a signer signs two or more header fields of the same name, this can cause spurious verification errors on otherwise legit messages ๐Ÿ™ƒ. In particular, signers that sign any existing DKIM-Signature fields run the risk of having messages incorrectly fail to verify ๐Ÿ˜ญ.

8.12. RSA Attacks

An attacker could create a large RSA signing key with a small exponent, which would force the verification key to have a large exponent instead. This makes verifiers burn considerable computing resources to verify the signature ๐Ÿ”ฅ. Verifiers might avoid this attack by refusing to verify signatures that reference selectors whose public keys have unreasonable exponents ๐Ÿ™…โ€โ™€๏ธ.

In general, an attacker might try to overwhelm a verifier by flooding it with messages that need verification ๐ŸŒŠ. This is similar to other MTA denial-of-service attacks and should be dealt with in a similar fashion ๐Ÿ’ช.

8.13. Inappropriate Signing by Parent Domains

The trust relationship described in Section 3.8 could conceivably be used by a parent domain to sign messages with identities in a subdomain that isn't administratively related to the parent ๐Ÿ‘‘. For example, the ".com" registry could create messages with signatures using an i= value in the example.com domain ๐Ÿ˜ณ. There's no general solution to this problem, since the administrative cut could occur anywhere in the domain name. For example, in the domain "example.podunk.ca.us" there are three administrative cuts (podunk.ca.us, ca.us, and us), and any of them could create messages with an identity in the full domain ๐Ÿ—บ๏ธ.

INFORMATIVE NOTE: This is considered an acceptable risk for the same reason that it is acceptable for domain delegation. For example, in the example above any of the domains could potentially simply delegate "example.podunk.ca.us" to a server of their choice and completely replace all DNS-served information. Note that a verifier MAY ignore signatures that come from an unlikely domain such as ".com", as discussed in Section 6.1.1 โœจ.

9. References

9.1. Normative References

These are the must-read docs, the ones the spec builds on, so you need them to really get it ๐Ÿ“šโœจ

  • [FIPS.180-2.2002] U.S. Department of Commerce, "Secure Hash Standard", FIPS PUB 180-2, August 2002.
  • [ITU.X660.1997] "Information Technology - ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)", ITU-T Recommendation X.660, 1997.
  • [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies", RFC 2045, November 1996.
  • [RFC2047] Moore, K., "MIME (Multipurpose Internet Mail Extensions) Part Three: Message header field Extensions for Non-ASCII Text", RFC 2047, November 1996.
  • [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
  • [RFC2821] Klensin, J., "Simple Mail Transfer Protocol", RFC 2821, April 2001.
  • [RFC2822] Resnick, P., "Internet Message Format", RFC 2822, April 2001.
  • [RFC3447] Jonsson, J. and B. Kaliski, "Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1", RFC 3447, February 2003.
  • [RFC3490] Faltstrom, P., Hoffman, P., and A. Costello, "Internationalizing Domain Names in Applications (IDNA)", RFC 3490, March 2003.
  • [RFC4234] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", RFC 4234, October 2005.

9.2. Informative References

These are the extra reading vibes ๐Ÿง ๐Ÿ’…. Not required, but so worth a peek if you're curious about the deep stuff.

  • [BONEH03] Proc. 12th USENIX Security Symposium, "Remote Timing Attacks are Practical", 2003.
  • [RFC1847] Galvin, J., Murphy, S., Crocker, S., and N. Freed, "Security Multiparts for MIME: Multipart/Signed and Multipart/Encrypted", RFC 1847, October 1995.
  • [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 2434, October 1998.
  • [RFC2440] Callas, J., Donnerhacke, L., Finney, H., and R. Thayer, "OpenPGP Message Format", RFC 2440, November 1998.
  • [RFC3766] Orman, H. and P. Hoffman, "Determining Strengths for Public Keys Used For Exchanging Symmetric Keys", RFC 3766, April 2004.
  • [RFC3833] Atkins, D. and R. Austein, "Threat Analysis of the Domain Name System (DNS)", RFC 3833, August 2004.
  • [RFC3851] Ramsdell, B., "S/MIME Version 3 Message Specification", RFC 3851, June 1999.
  • [RFC3864] Klyne, G., Nottingham, M., and J. Mogul, "Registration Procedures for Message Header Fields", BCP 90, September 2004.
  • [RFC4033] Arends, R., Austein, R., Larson, M., Massey, D., and S. Rose, "DNS Security Introduction and Requirements", RFC 4033, March 2005.
  • [RFC4686] Fenton, J., "Analysis of Threats Motivating DomainKeys Identified Mail (DKIM)", RFC 4686, September 2006.
  • [RFC4870] Delany, M., "Domain-Based Email Authentication Using Public Keys Advertised in the DNS (DomainKeys)", RFC 4870, May 2007.

Appendix A. Example of Use (INFORMATIVE) โœจ

Okay, this part is my favourite ๐Ÿฅน. It walks through a whole email's journey, from the moment it's sent to the moment it lands, and shows how all the pieces click together. The key used in this example is in Appendix C.

A.1. The User Composes an Email

Joe is writing to Suzie about dinner. Iconic, honestly ๐Ÿ“.

From: Joe SixPack <joe@football.example.com>
To: Suzie Q <suzie@shopping.example.net>
Subject: Is dinner ready?
Date: Fri, 11 Jul 2003 21:00:37 -0700 (PDT)
Message-ID: <20030712040037.46341.5F8J@football.example.com>

Hi.

We lost the game. Are you hungry yet?

Joe.

A.2. The Email Is Signed

Now the example.com outbound email server signs it, and the email gets its signature glow-up โœจ๐ŸŽ€. It now looks like this:

DKIM-Signature: v=1; a=rsa-sha256; s=brisbane; d=example.com;
      c=simple/simple; q=dns/txt; i=joe@football.example.com;
      h=Received : From : To : Subject : Date : Message-ID;
      bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
      b=AuUoFEfDxTDkHlLXSZEpZj79LICEps6eda7W3deTVFOk4yAUoqOB
        4nujc7YopdG5dWLSdNg6xNAZpOPr+kHxt1IrE+NahM6L/LbvaHut
        KVdkLLkpVaVVQPzeRDI009SO2Il5Lu7rDNH6mZckBdrIx0orEtZV
        4bmp/YzhwvcubU4=;
Received: from client1.football.example.com  [192.0.2.1]
      by submitserver.example.com with SUBMISSION;
      Fri, 11 Jul 2003 21:01:54 -0700 (PDT)
From: Joe SixPack <joe@football.example.com>
To: Suzie Q <suzie@shopping.example.net>
Subject: Is dinner ready?
Date: Fri, 11 Jul 2003 21:00:37 -0700 (PDT)
Message-ID: <20030712040037.46341.5F8J@football.example.com>

Hi.

We lost the game. Are you hungry yet?

Joe.

The signing email server needs the private key tied to the "brisbane" selector to make this signature. No private key, no signature, no cap ๐Ÿ”.

A.3. The Email Signature Is Verified

Usually an inbound SMTP server or possibly the final delivery agent does the checking ๐Ÿ•ต๏ธโ€โ™€๏ธ. Intervening MTAs can also verify it if they choose to, which is lowkey a power move. The verification takes the domain "example.com" from the "d=" tag and the selector "brisbane" from the "s=" tag in the DKIM-Signature header field, and builds the DNS DKIM query for:

brisbane._domainkey.example.com

Verification starts with the physically last Received header field, then the From header field, and so on, in the order listed in the "h=" tag. After that comes a single CRLF, then the body (starting with "Hi."). The email gets canonically prepared for verifying with the "simple" method. The result of the query and the signature check gets stored (in this example) in the X-Authentication-Results header field line. Once it passes, the email looks like this:

X-Authentication-Results: shopping.example.net
      header.from=joe@football.example.com; dkim=pass
Received: from mout23.football.example.com (192.168.1.1)
      by shopping.example.net with SMTP;
      Fri, 11 Jul 2003 21:01:59 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; s=brisbane; d=example.com;
      c=simple/simple; q=dns/txt; i=joe@football.example.com;
      h=Received : From : To : Subject : Date : Message-ID;
      bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
      b=AuUoFEfDxTDkHlLXSZEpZj79LICEps6eda7W3deTVFOk4yAUoqOB
        4nujc7YopdG5dWLSdNg6xNAZpOPr+kHxt1IrE+NahM6L/LbvaHut
        KVdkLLkpVaVVQPzeRDI009SO2Il5Lu7rDNH6mZckBdrIx0orEtZV
        4bmp/YzhwvcubU4=;
Received: from client1.football.example.com  [192.0.2.1]
      by submitserver.example.com with SUBMISSION;
      Fri, 11 Jul 2003 21:01:54 -0700 (PDT)
From: Joe SixPack <joe@football.example.com>
To: Suzie Q <suzie@shopping.example.net>
Subject: Is dinner ready?
Date: Fri, 11 Jul 2003 21:00:37 -0700 (PDT)
Message-ID: <20030712040037.46341.5F8J@football.example.com>

Hi.

We lost the game. Are you hungry yet?

Joe.

Appendix B. Usage Examples (INFORMATIVE) ๐ŸŒˆ

DKIM signing and validating can be used in lots of different ways for lots of different operational scenarios ๐Ÿ’–. This appendix talks through some of the common ones.

NOTE: Everything in this appendix is for information only ๐Ÿซถ. It describes various ways DKIM can be used, given particular constraints and needs. None of these examples are meant as explanation or guidance on DKIM spec details when you're building an implementation, so don't take them as that!

B.1. Alternate Submission Scenarios

In the simplest setup, a user's MUA, MSA, and Internet (boundary) MTA all live in the same administrative environment and use the same domain name. So every part involved in submission and the first transfer is related, very neat โœจ. But it's pretty common for two or more of those parts to be run by different admins, and that makes it tricky to pick and manage the domain name for signing, and to line it up with the common email identity header fields ๐Ÿค”.

B.1.1. Delegated Business Functions

Some organizations hand specific business jobs to separate groups, whether they're inside the org or outside it ๐Ÿ’ผ. The goal is to let that group sign some mail, but to limit which signatures they can make. DKIM selectors (the "s=" signature tag) and granularity (the "g=" key tag) make this kind of restricted permission possible ๐Ÿ”‘. Outsourced jobs like legit email marketing providers and corporate benefits providers are classic examples.

Here, the delegated group needs to send signed messages using the email domain of the client company ๐Ÿ“จ. At the same time, the client is usually hesitant to register a key for the provider that lets them send for any address in the domain. Understandable, bestie ๐Ÿ™ˆ.

There are a few ways to run this. In one, the client org handles all of the public query service (like DNS) admin. In another, it uses DNS delegation so the delegated group can manage the DKIM key record going forward ๐Ÿงš.

If the client org keeps all of the DNS admin, the outsourcing company can generate a key pair and give the public key to the client. The client then registers it in the query service under a unique selector that only authorizes a specific From header field Local-part. For example, a client with the domain "example.com" could set the selector record to "g=winter-promotions", so the signature only counts for mail with a From address of "winter-promotions@example.com". The provider can send with that specific address and it'll verify properly ๐Ÿ’…. The client keeps control of that address too, because they can revoke the key at any time ๐Ÿ”ฅ.

If the client wants the delegated group to handle the DNS admin, the domain name tied to the selector can point to the provider's DNS server. The provider then creates and maintains all of the DKIM signature info for that selector ๐ŸŒธ. In this setup the client can't put constraints on the Local-part of the addresses that get signed. But they can still pull the provider's signing rights by removing the DNS delegation record ๐Ÿ‘‘.

B.1.2. PDAs and Similar Devices

okay so PDAs are literally the reason we need multiple keys per domain ๐ŸŽ€ say John Doe wants to send mail from his work address, jdoe@example.com ๐Ÿ’ผ, but his tiny device can't set up a Virtual Private Network (VPN) connection to the corporate network. maybe the device is limited, or maybe his Internet access provider has restrictions ๐Ÿ™ˆ. if the device has a private key registered for jdoe@example.com by the administrator of the example.com domain, plus software that can sign messages, John can sign the message right on the device before it goes out through the access provider's outgoing network โœจ slay.

B.1.3. Roaming Users

roaming users end up in situations where it's handy or necessary to use an SMTP server that isn't their home server ๐Ÿงณ think conferences, lots of hotels, that kind of thing. in those cases, a signature added by the submission service uses an identity that's different from the user's home system, which is kind of a plot twist ๐Ÿ˜ญ

ideally, roaming users would connect back to their home server through either a VPN or a SUBMISSION server running with SMTP AUTHentication on port 587 ๐Ÿ”. if signing can happen on the roaming user's laptop, they can sign before submitting, although the risk of further modification is high ๐Ÿ’€. if neither of those is possible, these roaming users won't be able to send mail signed with their own domain key ๐Ÿฅฒ

B.1.4. Independent (Kiosk) Message Submission

standalone services like walk-up kiosks and web-based info services don't have any lasting email relationship with the user, but users sometimes ask for mail to be sent on their behalf ๐Ÿ“ฌ. like, a news website often lets you forward an article to a friend. this is usually done with the reader's own email address so it shows who the author is ๐Ÿ’Œ. people call this the "Evite problem", named after the website of the same name that lets you send invites to friends ๐ŸŽ‰

a common fix is to keep the reader's address in the From header field, but put an address owned by the posting site in the Sender header field. the posting site can then sign the message using the domain in the Sender field. this gives the receiving email site useful info, since it can connect the signing domain with the initial submission role โœจ

receiving sites often want to show end users info about mail handled this way ๐Ÿ‘€. honestly, the real efficacy of different approaches is something for human factors usability research ๐Ÿง , but one technique is for the verifying system to rewrite the From header field so it shows the address that was verified. for example:

From: John Doe via news@news-site.com <jdoe@example.com>
(Note that such rewriting will break a signature, unless it is done after the verification pass is complete.) โš ๏ธ

B.2. Alternate Delivery Scenarios

email often lands in a mailbox with a different address from the one used at submission ๐Ÿ“ญ. in those cases, an intermediary works at the address originally used and then passes the message on to the final destination. this mediation creates some challenges for DKIM signatures ๐Ÿฆ‹

B.2.1. Affinity Addresses

"Affinity addresses" let a user keep a stable email address even while they hop between email providers ๐Ÿงš. they're usually tied to college alumni groups, professional orgs, and rec clubs the user expects a long-term relationship with ๐Ÿซ. these domains usually forward incoming mail, and they often have a web app that logs the user in and lets them change the forwarding address ๐Ÿ’ป. but these services usually rely on users sending outgoing mail through their own providers' MTAs. so mail signed with the affinity address's domain isn't signed by an entity administered by the organization that owns that domain ๐Ÿค”

with DKIM, affinity domains could use the web app to let users register per-user keys for signing messages on behalf of their affinity address ๐Ÿ”‘. the user takes away the secret half of the key pair for signing, and the affinity domain publishes the public half in DNS so verifiers can access it ๐Ÿซถ

this is another use of user-level keying, and affinity domains would typically have a huge number of user-level keys ๐Ÿ“ˆ. alternatively, the affinity domain could handle outgoing mail itself, running a mail submission agent that authenticates users before accepting and signing messages for them ๐Ÿ›ก๏ธ. of course, this depends on the user's service provider not blocking the TCP ports used for mail submission ๐Ÿšซ

B.2.2. Simple Address Aliasing (.forward)

sometimes a recipient can set up an email address to automatically redirect messages from the original address to another, like with a Unix .forward file ๐Ÿช„. here, the recipient's domain's mail handling service usually redirects the message without changing it, except it adds a Received header field and changes the envelope recipient address ๐Ÿ“จ. so the person at the final mailbox will probably be able to verify the original signature, since the signed content hasn't changed, and DKIM can validate the message signature ๐Ÿ’…

B.2.3. Mailing Lists and Re-Posters

services that take delivery of a message and then resubmit it behave all over the place ๐ŸŒ€. the big example is mailing lists (collectively called "forwarders" below) ๐Ÿ“‹. some change nothing except adding a Received header field and changing the envelope info. others add header fields, change the Subject header field, add content to the body (usually at the end), or reformat the body somehow โœ๏ธ. the simple ones produce messages pretty similar to the automated alias services, and the more elaborate ones basically create a new message ๐ŸŽญ

a Forwarder that doesn't modify the body or signed header fields is likely to keep the existing signature valid โœ…. it also could choose to add its own signature.

Forwarders that modify a message in a way that could break an existing signature are especially good candidates for adding their own signatures (e.g., mailing-list-name@example.net) ๐Ÿ’–. since (re-)signing means taking responsibility for the message content, these signing forwarders are likely to be picky ๐Ÿง. they forward or re-sign a message only if it was received with a valid signature, or if they have some other basis for knowing it isn't spoofed ๐Ÿ•ต๏ธ

a common habit among systems that are mostly mail redistributors is adding a Sender header field to identify the address used to sign the message ๐Ÿ“ฃ. this practice removes any preexisting Sender header field, as required by [RFC2822] ๐Ÿ“œ. the forwarder applies a new DKIM-Signature header field carrying the signature, public key, and related info of the forwarder ๐ŸŒˆ

Appendix C. Creating a Public Key (INFORMATIVE)

the default signature is an RSA signed SHA256 digest of the complete email ๐Ÿ”. to keep things easy to follow, we use the openssl command to show how keys and signatures get managed ๐Ÿ’ป. one way to generate a 1024-bit, unencrypted private key suitable for DKIM is to use openssl like this:

$ openssl genrsa -out rsa.private 1024

for increased security, the "-passin" parameter can also be added to encrypt the private key ๐Ÿ”’. using this parameter will require entering a password for several of the following steps. servers may prefer to use hardware cryptographic support ๐Ÿ–ฅ๏ธ

the genrsa step results in the file rsa.private containing key information similar to this:

-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYtIxN2SnFC
jxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v/RtdC2UzJ1lWT947qR+Rcac2gb
to/NMqJ0fzfVjH4OuKhitdY9tf6mcwGjaNBcWToIMmPSPDdQPNUYckcQ2QIDAQAB
AoGBALmn+XwWk7akvkUlqb+dOxyLB9i5VBVfje89Teolwc9YJT36BGN/l4e0l6QX
/1//6DWUTB3KI6wFcm7TWJcxbS0tcKZX7FsJvUz1SbQnkS54DJck1EZO/BLa5ckJ
gAYIaqlA9C0ZwM6i58lLlPadX/rtHb7pWzeNcZHjKrjM461ZAkEA+itss2nRlmyO
n1/5yDyCluST4dQfO8kAB3toSEVc7DeFeDhnC1mZdjASZNvdHS4gbLIA1hUGEF9m
3hKsGUMMPwJBAPW5v/U+AWTADFCS22t72NUurgzeAbzb1HWMqO4y4+9Hpjk5wvL/
eVYizyuce3/fGke7aRYw/ADKygMJdW8H/OcCQQDz5OQb4j2QDpPZc0Nc4QlbvMsj
7p7otWRO5xRa6SzXqqV3+F0VpqvDmshEBkoCydaYwc2o6WQ5EBmExeV8124XAkEA
qZzGsIxVP+sEVRWZmW6KNFSdVUpk3qzK0Tz/WjQMe5z0UunY9Ax9/4PVhp/j61bf
eAYXunajbBSOLlx4D+TunwJBANkPI5S9iylsbLs6NkaMHV6k5ioHBBmgCak95JGX
GMot/L2x0IYyMLAz6oLWh2hm7zwtb0CgOrPo1ke44hFYnfc=
-----END RSA PRIVATE KEY-----

to pull the public-key component out of the private key, use openssl like this:

$ openssl rsa -in rsa.private -out rsa.public -pubout -outform PEM

this results in the file rsa.public containing key information similar to this:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkM
oGeLnQg1fWn7/zYtIxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v/R
tdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhitdY9tf6mcwGjaNBcWToI
MmPSPDdQPNUYckcQ2QIDAQAB
-----END PUBLIC KEY-----

this public-key data (without the BEGIN and END tags) is placed in the DNS ๐ŸŒ:

brisbane IN  TXT  ("v=DKIM1; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQ"
                   "KBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkMoGeLnQg1fWn7/zYt"
                   "IxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v"
                   "/RtdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhi"
                   "tdY9tf6mcwGjaNBcWToIMmPSPDdQPNUYckcQ2QIDAQAB")

Appendix D. MUA Considerations ๐Ÿ’…

Okay bestie, so when a DKIM signature gets verified, the processing system sometimes passes the result along to the recipient's MUA (that's the mail app, basically ๐Ÿ“ฌ). How to show this to users in a way that actually helps them is still being figured out through ongoing human factors usability research ๐Ÿง . The usual move is to have the MUA highlight the address tied to the signing identity, so the user can see where the mail really came from โœจ. An MUA could do that with visual cues like graphics, put the address in an alternate view, or even rewrite the original From address using the verified info. It's giving clarity ๐Ÿฆ‹.

Some MUAs might show which header fields were protected by the validated DKIM signature. That could be a positive mark on the signed header fields, a negative mark on the unsigned ones, hiding the unsigned ones completely, or some mix of all of these ๐ŸŽ€. If an MUA uses visual signals for signed header fields, it probably needs to be careful not to display unsigned header fields in a way the end user might think were signed ๐Ÿ™ˆ. That's a big "not me" moment waiting to happen. And if the message has an l= tag whose value doesn't reach the end of the message, the MUA might also hide or mark the part of the message body that wasn't signed ๐Ÿ’€.

The info above is not meant to be exhaustive, so the MUA can highlight, accentuate, hide, or otherwise display any other information the MUA author thinks is important to the end user ๐ŸŒˆ. Main character energy for the MUA, honestly.


Appendix E. Acknowledgements ๐Ÿซถ

The authors wanted to thank a whole squad of people for their valuable suggestions and constructive criticism ๐Ÿ’–: Russ Allbery, Edwin Aoki, Claus Assmann, Steve Atkins, Rob Austein, Fred Baker, Mark Baugher, Steve Bellovin, Nathaniel Borenstein, Dave Crocker, Michael Cudahy, Dennis Dayman, Jutta Degener, Frank Ellermann, Patrik Faeltstroem, Mark Fanto, Stephen Farrell, Duncan Findlay, Elliot Gillum, Olafur Gu[eth]mundsson, Phillip Hallam-Baker, Tony Hansen, Sam Hartman, Arvel Hathcock, Amir Herzberg, Paul Hoffman, Russ Housley, Craig Hughes, Cullen Jennings, Don Johnsen, Harry Katz, Murray S. Kucherawy, Barry Leiba, John Levine, Charles Lindsey, Simon Longsdale, David Margrave, Justin Mason, David Mayne, Thierry Moreau, Steve Murphy, Russell Nelson, Dave Oran, Doug Otis, Shamim Pirzada, Juan Altmayer Pizzorno, Sanjay Pol, Blake Ramsdell, Christian Renaud, Scott Renfro, Neil Rerup, Eric Rescorla, Dave Rossetti, Hector Santos, Jim Schaad, the Spamhaus.org team, Malte S. Stretz, Robert Sanders, Rand Wacker, Sam Weiler, and Dan Wing ๐ŸŒธ. Iconic list, no notes.

The DomainKeys specification was a primary source for this spec, so it gets a big thank you too ๐Ÿ‘‘. More info about DomainKeys is in [RFC4870] โœจ.


Authors' Addresses ๐Ÿ“‡

Eric Allman
Sendmail, Inc.
6425 Christie Ave, Suite 400
Emeryville, CA  94608
USA

Phone: +1 510 594 5501
EMail: eric+dkim@sendmail.org
URI:


Jon Callas
PGP Corporation
3460 West Bayshore
Palo Alto, CA  94303
USA

Phone: +1 650 319 9016
EMail: jon@pgp.com
Mark Delany
Yahoo! Inc
701 First Avenue
Sunnyvale, CA  95087
USA

Phone: +1 408 349 6831
EMail: markd+dkim@yahoo-inc.com
URI:


Miles Libbey
Yahoo! Inc
701 First Avenue
Sunnyvale, CA  95087
USA

EMail: mlibbeymail-mailsig@yahoo.com
URI:


Jim Fenton
Cisco Systems, Inc.
MS SJ-9/2
170 W. Tasman Drive
San Jose, CA  95134-1706
USA

Phone: +1 408 526 5914
EMail: fenton@cisco.com
URI:


Michael Thomas
Cisco Systems, Inc.
MS SJ-9/2
170 W. Tasman Drive
San Jose, CA  95134-1706

Phone: +1 408 525 5386
EMail: mat@cisco.com

Copyright (C) The IETF Trust (2007). Yes, that's the official vibe ๐Ÿ–‹๏ธ.

This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights ๐Ÿ’œ.

This document and the information in it are provided on an "AS IS" basis, and the legal disclaimer is serious business, so here it is word for word:

THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Intellectual Property ๐Ÿ”ฅ

The IETF doesn't take a position on whether any Intellectual Property Rights (or other rights) someone claims over the tech in this document are valid or how far they reach. It also doesn't say whether licenses are available, and it hasn't checked for any rights on its own ๐Ÿ™…. For how RFC documents handle rights, check BCP 78 and BCP 79 ๐Ÿ“œ.

You can get copies of IPR disclosures sent to the IETF Secretariat, plus any assurances of licenses, or the results of attempts to get a general license or permission for implementers or users, from the IETF online IPR repository at http://www.ietf.org/ipr ๐Ÿ”.

The IETF also invites anyone to point out any copyrights, patents, patent applications, or other proprietary rights that might cover technology needed to implement this standard. Send that info to the IETF at ietf-ipr@ietf.org ๐Ÿ’Œ.


Acknowledgement ๐ŸŽ€

Funding for the RFC Editor function is currently provided by the Internet Society ๐Ÿ’–.

๐Ÿ“ฆ where this page comes from