Hi!

After I saw the JSON changes upcoming, I refrained
from commenting on earlier versions, in spite of my
promise.

Thanks for the work of consolidating this new idea
into the actual document, fleshing out the details.

On 2/18/26 21:37, Richard Clayton wrote:
I have submitted a revision to the DKIM2 specification document

    https://datatracker.ietf.org/doc/draft-clayton-dkim2-spec/

This new version places the recipes, the SMTP parameters and the crypto
into JSON objects which are then base64 encoded and put into the
Message-Instance and DKIM2-Signature fields. This was originally
Tobias's idea and it has worked out well (if a little slowly).

Other values have been left as tag=value so that human readers of email
headers (who are not all that common of course) can get an idea of what
is going on. People who feed messages into tools that then present the
information in the header fields in a human-friendly way will not of
course care one way or the other!  Anyway, it's obviously possible to
move more values in or out of the JSON.

I like the general approach of keeping to tag-value syntax, "escaping"
to JSON/b64 in some specific places.

First I had the thought that perhaps everything could be just one big
JSON/b64 blob (one for each Message-Instance and one for each
DKIM2-Signature), but during an absence in Februar I had the thought
that that would have made a few things more complicated.

The recipes are complex and JSON is much more straightforward for
systems to parse. They are a separate object so that you can easily see
they are there (and get a feel for how complicated they might be).

Putting the crypto (hashes and signatures) into the JSON means that the
bodge of a1= a2= etc disappears and I have removed the restriction on
just two hash/signature algorithms. We might want to set an upper bound
mind you.

I like the approach of not putting "administrative"/operational limits
in the syntax itself but possibly in some section about such limits.

Perhaps something like signers SHOULD not use more than 2 algorithms,
verifiers MUST be able to work with up to 3.

(Rationale: Currently we specify RSA or ED25519, with sha256.
For a transition within traditional algorithms, 2 should be
enough.  Even for a pqc transition, 2 should be enough, just
that the algorithm would be a traditional+pqc hybrid, as the IETF
is specifying in more and more ways, i.e. *one* algorithm
specifier would be a hybrid between some pqc + ED25519, for
example.  However to be open to the different approach,
not using a hybrid as one algorithm, we could also allow
for 3 algorithms for a transition from RSA to a pqc combination:
RSA; ED25519; pqc - with the idea that the combination of
ED25519 and pqc would be the transition goal.  Hence the
idea to make receivers allow at least 3 just in case.)

Perhaps there should also be operational limits for the maximum
number of Message-Instance versions and DKIM2-Signatures.

Putting the SMTP parameters into JSON does not help the human readers of
headers (albeit they are no worse off than with DKIM1 since this is a
new feature of DKIM2). However, if we don't do that then as Hannah so
helpfully pointed out you need a fully-fledged RFC5321 parser in order
not to misinterpret the superficially easy to parse tag=value format.

There would have been other possibilities (like using hashes for
localparts and restricting the syntax to hash @ domain), but JSON
solves these issues more elegantly.

Localpart hashes might only be a consideration now for partial
envelope privacy, without obstructing MTAs to do all required checks,
and verifiers to still check alignment.

I also removed the restriction on no recipes on the v=1 Message-
Instance. It is argued that being able to recreate the message as it was
before it entered the DKIM2 world would be useful ... one could see if
was an OK DKIM1 message for example, and this might make it easier for
people to handle messages from mailing lists which were DKIM2 enabled
but not all the messages submitted to them were DKIM2 messages.

That's an interesting consideration.  But then, how would verifier
actions be adjusted?  Like what are they advised to do with v=1
recipes?

(Are there any cases where it's really ok that v>1 headers have
_no_ recipes?)

I have not yet had time to take up Dave Crocker's excellent suggestion
to have a section not just on Signers and Verifiers but also on what
Forwarders should be doing. I also aim to reorganise the sections a bit
(to put the algorithms for creating hashes alongside the description of
the JSON blobs to hold the results). This might reduce some of the
repetition that remains in the document.

BTW: the four JSON schemas are valid JSON -- but that does not mean that
they are correct :-)   ... and I will be sorting out the dangling URLs
very shortly.

There should be some reference about JSON schema syntax.  I tried
to search for that and eventually found several different approaches
for that, and while one of them seemed to be the right one, it
did take some time to identify that.

(And by the way those specs I found are hard to parse...)

Now trying some more detailed walk-through:

Section 5 (Recipes):

If I understand the schema syntax correctly, this allows
values like

{
  "h":{},
  // or:
  "h":[
    ["string1", "string2"],
    ["string3", "string4"]
  ]
  ...
}

The specification "header-field-name" seems to have no effect
in JSON schema syntax.

So this does apparently not yet fit into what we actually
want:

- Either no header changes ("h" completely absent)
- Either a "global" undisclosed change (we don't say even which
  field keys we touched) ("h":{})
- Or a set of changes for a set of field keys: For each field
  key, either a special tag (undisclosed changes specifically
  for this header field key) or an array (potentially empty)
  of instructions, each instruction being either a copy
  (from index/to index) or an insert (with literal string data).

I've not seen examples that clarify this enough later in the
document either.

We'd need something like (see below for the prefix I use in
entries):

{
  "h":{
    "field-name1":["c:1-2", "b:foo"]
    ...
  }
}

The later textual description:

   If the recipe is of the form "(" start "-" end ")" then this means
   that (relevant) header field lines from to start to end, inclusive,
   are to be emitted.  The start value of each of these recipes MUST be
   in ascending order and MUST be greater than the end value of all
   preceding recipes for this header field name.

   Otherwise the recipe is treated as a text string to which relevant
   header field name and a colon is prepended and a CRLF is appended and
   the resultant string is then emitted.

looks weird.  We have JSON now which allows for structured data, and
now we use magical syntax within a string?

And if so, how could I add a header field with the literal data
"(1-2)"?

If we should use syntax within a JSON string, we'd perhaps rather use
a prefix form for both variant, for example the good old "b:" and "c:".
I.e. a recipe which doesn't match either of these prefixes is undefined.
If it matches "b:", this is stripped and the data is used verbatim
(with field name + colon prepended + CRLF appended), if it matches "c:",
it has to be followed by "start-end" as specified.

For bodies we have
{
  ...,
  "b":{}
  // or
  "b":["string1", "string2"...]
}

In the textual description, there's the same ambiguity between
"(1-2)" as copy recipe and inserting a line containing "(1-2)"
literally.  The same suggested solution could work.

We should also clarify if certain JSON mapping frameworks/tools
can make a meaningful difference between "h" absent and "h":{}.

Section 6: Message Hash Values:

To me this looks like the spec produces values like:

{"h":["s1","s2"],"b":["s3","s4"]}

I guess the intention is that s1 and s3 are algorithm identifiers?

The description saying "header hash name and value" and
"body hash name and value" seems a bit vague.

And why not use something structured like:
{
  "h":{
    "a":"sha256",
    "v":"hashvalue"
  },
  "b":{
    "a":"sha256",
    "v":"hashvalue"
  }
}

Note that the schema does _not_ allow for multiple hashes
in contradiction to the intent stated textually!

The schema would need to allow values like:

{
  "h":[["a1","v1"],["a2","v2"],...],
  "b":<similar>
}

or

{
  "h":[{"a":"sha256","v":"foo"},{"a":"sha3","v":"bar"}],
  "b":<similar>
}

Section 7: SMTP parameters

The schema seems to allow (with an error in the spec for
"mf"):

{
  "mf":"string1",
  "rt":["string2", ...]
}

The text says that the empty mail from is represented by
"<>", but doesn't say anything about the representation of
mail addresses otherwise.

I'd think the angle brackets are not needed here as with
JSON we have clearly delimited strings.

So I'd suggest "" for the empty mail from and for other
addresses just "local-part@domain".

(If local-part is a quoted string, this works, like
"\"quoted-local-part\"@domain".)

I remember reading something about lowercaseing elsewhere,
but it's not in the text of section 7?

Section 8 Message Signature

The schema allows for values like

{
  "<key not specified???>":["string1", "string2", "string3"]
}

There's no allowance for more than one signature.

So it should rather be
[["a1","s1","b1"],["a2","s2","b2"]]

or
[
  {
    "a":"a1",
    "s":"s1",
    "b":"b1"
  },
  {
    "a":"a2",
    "s":"s2",
    "b":"b2"
  }
  ...
]

The text:

   The algorithm value is the one used to generate
   the signature.  Verifiers MUST support "RSA-SHA256" for which the
   string "rsa" is used and "Ed25519-SHA256" for which the string
   "ed25519" is used; See Section 4 for a description of these
   algorithms.

Why is the hash specification then omitted?

We have 2 levels where hashes are used: In Message-Instance
(specified there) for headers and for body, separately.
And in DKIM2-Signature, for hashing the collection of
Message-Instance fields, previous DKIM2-Signature fields as
is, and current DKIM2-Signature template (with signatures
omitted).  The latter hash algorithm is not specified if
we name only the signature algorithm itself.

I think the algorithm value would need to be "rsa-sha256"
or "ed25519-sha256", in order to allow for change not only
of signature algorithm, but also of the hash used for
Message-Instance+DKIM2-Signature header fields.

14.4 Signer actions: Calculate a Signature Value

   *  Place the header fields in order.  First come the Message-Instance
      header fields in ascending version (v=) order.  Second are the
      DKIM2-Signature header fields in ascending sequence (i=) order.
      Last of all is an incomplete DKIM2-Signature header field (the one
      that this system is creating) with all tags present except that
      the signature value (s=) is null (that is the base64 value is
      absent).

Note that previously we "dropped" only the b1=/b2= values
in the DKIM2-Signature template.  Thus each signature committed
to the a1/a2/s1/s2 values, both "its own" and that of other
signatures.

I.e. the first signature failed to validate if one manipulated
"a1" or "s1", but it also failed to validate if someone manipulated
to remove the second signature or to add an extra one.

Signature stripping might be a real attack model, for example
if we transition to pqc and the classical algorithm is broken,
the pqc signature could be stripped and the classical signature
faked (with the broken key) after an unauthorized content change.

So perhaps we need to reconsider the signature format so that
the commitment to a/s values and to the presence/absence of
other signatures is restored.  Just using the JSON, with "b"
values omitted or null or empty string, doesn't work because
JSON serialization isn't 100% unique.

By the way, even if there's only one strong signature, it
can now be replaced by another signature using a weak algorithm/
leaked key, as long as its public key is still present under
some selector for the domain!  So that's even a kind of
downgrade attack.  That's however an inherent issue.

As long as there's just one weak key/key under a weak algorithm
published for some domain, one can just strip their signature
and fake a completely new one.

Thinking about it: I think as long as messages with just one
signature are allowed, once one of the algorithms or published keys
are weak, signatures can be forged and the original presence of
a second stronger signature doesn't help because it can be stripped.

All these scenarios apply only on the last signature, or if
further signatures use a weak hash or signature algorithm/key (so
one can fake all subsequent signatures or use a hash collision
against an existing already signed Message-Instance).

15.1 Verifier output states

   Verifiers wishing to communicate the results of verification to other
   parts of the mail system may do so in whatever manner they see fit.
   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 DKIM2-Signature or pre-
   existing authentication status header fields in the header field
   block.  The Authentication-Results: header field ([RFC8601]) MAY be
   used for this purpose.  It should be noted that any "Authentication-
   Results" header field will count as a modification to the email if
   any further DKIM2-Signature header fields are to be generated.

This means that any MUA trying to perform their own
DKIM2 checks (instead of trusting their mail server)
will get failures and would need to heuristically try
to strip the uppermost Authentication-Results in that
case, as there's no subsequent Message-Instance+
DKIM2-Signature to tell the MUA exactly about the change.

I'll not comment on purely textual inconsistencies now
like remaining references to "z" recipes or "s1=" tags
(section 15.3 for example).

Section 15.4 Perform the Signature Verification Calculation

   The reasoning for requiring that all signatures pass is that if a
   signature scheme has recently become deprecated because it is known
   to be cryptographically flawed then Signers will use a second
   (unbroken) signature scheme.  However, such a Signer may still
   provide the other signature for the benefit of Verifiers that have
   yet to upgrade -- reasoning perhaps that attacks are too expensive to
   be a very significant security issue.  A Verifier that determines
   that one signature passes whilst the other fails may well be in a
   position to prevent an attack.

As shown above, in a header with 2 signatures, even if only
one of them is weak, the other can be downgraded away.

So the last sentence is probably misleading.

16 Delivery Status Notifications in the DKIM2 ecosystem

   A DSN MUST be addressed to the MTA that sent the message.  This
   prevents "backscatter" by passing failures back along the chain of
   MTAs that were in involved in passing the message forwards.  This is
   achieved by using the mf= tag from the highest numbered
   DKIM2-Signature field.  If this field is null ("mf=<>") then a DNS
   MUST NOT be sent.

As long as the signature has already been verified (before that
DSNs are not permitted, if I understand the DKIM2 framework
correctly), this means the match between mf= and RFC5321 MAIL FROM
has already been checked.  So no special handling is needed and
DSNs can go to MAIL FROM as usual?  (Which automatically implies
that for mf=<>, equivalent to MAIL FROM=<>, DSNs can't be sent.)

16.1 DSN contents

   If the message body has been truncated (rather than omitted
   altogether) then in order to allow verification of the DNS contents a
   Message-Instance header field MUST be added to the message with a
   body recipe of "z".

Does this really imply adding an extra header in the message/rfc822
subpart of the multipart/report top-level?  I.e. not sending the
headers as they are?

This additional header would not be referenced and signed by
any DKIM2-Signature in the inner original headers!

In my eyes it doesn't make sense to require that.  The receiver
of the DSN can trivially see that the body has been stripped
in the report and adjust its checking appropriately.


For now that's all I noticed/wanted to comment on, based on reading
the draft only (not based on implementation experience yet).

Kind regards,

Hannah.
-- Hannah Stern

Software Developer
Mail Transfer Development

1&1 Mail & Media Development & Technology GmbH |  |   |
Phone: +49 721 91374-4519
E-Mail: [email protected] | Web: www.mail-and-media.com www.gmx.net
www.web.de www.mail.com www.united-internet-media.de

Hauptsitz Montabaur, Amtsgericht Montabaur, HRB 5452

Geschäftsführer: Alexander Charles, Dr. Michael Hagenau, Thomas Ludwig,
Dr. Verena Patzelt


Member of United Internet

Diese E-Mail kann vertrauliche und/oder gesetzlich geschützte
Informationen enthalten. Wenn Sie nicht der bestimmungsgemäße Adressat
sind oder diese E-Mail irrtümlich erhalten haben, unterrichten Sie bitte
den Absender und vernichten Sie diese E-Mail. Anderen als dem
bestimmungsgemäßen Adressaten ist untersagt, diese E-Mail zu speichern,
weiterzuleiten oder ihren Inhalt auf welche Weise auch immer zu verwenden.

This e-mail may contain confidential and/or privileged information. If
you are not the intended recipient of this e-mail, you are hereby
notified that saving, distribution or use of the content of this e-mail
in any way is prohibited. If you have received this e-mail in error,
please notify the sender and delete the e-mail.

_______________________________________________
Ietf-dkim mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to