Hi Mauro, Nice work — and thanks for signing the message too, I used it as a test.
I ran it through PhoenixDKIM (a C implementation based off OpenDKIM I've been working on) and it doesn't pass, though that was to be expected: The signature itself verifies fine, but the IETF list rewrote the envelope sender and the From/Subject without adding a DKIM2 hop, so the chain is broken by the time it reaches me. On the folding side, PhoenixDKIM seems to cope with the cases you hit — it ignores whitespace inside the base64, and it lands on lowercase recipe keys like Steve's does, so +1 for the spec just saying lowercase. Is Stalwart enabling DKIM2 by default already? If you'd like to poke at it: https://github.com/edmundlod/PhoenixDKIM/tree/feature/dkim2 Kind regards, Edmund On 2026/07/04 09:25, Mauro De Gennaro wrote:
Hi all, I wanted to share some news from implementing DKIM2, along with a few findings that came out of interop testing. Incidentally, this message is itself DKIM2 signed, so if your tooling supports it, feel free to use it as a small real-world sample. I have added DKIM2 support (draft-ietf-dkim-dkim2-spec-03) to mail-auth, the Rust library for email authentication, which lives at https://github.com/stalwartlabs/mail-auth. One thing worth mentioning is that mail-auth also compiles to WebAssembly, so it can be used from JavaScript applications and other WASM environments. To make it easy to try, I put together a playground at https://mail-auth.stalw.art/ that signs and validates DKIM2, as well as DMARC, entirely from the browser, resolving the records using DNS-over-HTTP. DKIM2 is now also supported in Stalwart mail server (https://github.com/stalwartlabs/stalwart/), which uses mail-auth internally. Beyond signing and verification, I have implemented inbound DSN validation for DKIM2 as described in the draft, so that delivery status notifications coming back along the chain can themselves be authenticated. On interoperability, I tested mail-auth against Bron's Python implementation (https://github.com/dkim2wg/interop.git) and Steve's Go implementation (https://forge.turscar.ie/turscar/dkim2.git). The integration tests are at https://github.com/stalwartlabs/mail-auth/blob/main/src/dkim2/interop_test.rs and they exercise signing, verification and recipe generation across all three implementations, in both directions, including multi-hop chains. The good news is that with header folding disabled, every combination passes: signing, verifying and recipe based reconstruction all agree across the three implementations. I did hit one issue, when mail-auth folds the DKIM2-Signature and Message-Instance header fields, which is expected behaviour for long fields such as the base64 signature, both the Python and the Go implementations fail to parse them. The draft describes in section 2.12 that folding whitespace is allowed inside base64 strings and must be ignored when the value is used, so I believe mail-auth's output is conformant here, and it round-trips cleanly through my own parser. In Steve's Go library the mf= and rt= values are base64 decoded in strict mode without first removing the folding whitespace, which trips on the embedded space or tab. In Bron's Python library the s= value is split on the colon without unfolding first, so the algorithm token ends up carrying the leading CRLF and tab, and the algorithm comparison then fails. Neither looks hard to fix, and as noted, disabling folding on my side makes all tests pass. Finally, a couple of small notes on the spec itself. Overall I found it clear, but a few of the sections that describe algorithms in prose would benefit from some accompanying pseudocode, in particular the parts covering recipe application and the chain walk during verification. It would remove a little ambiguity for implementers. The other point concerns header field names in recipes. It is not entirely clear from the text whether the JSON keys must be lowercased or may retain their original case. Steve's implementation expects lowercase and rejects mixed case, whereas I initially generated recipes preserving the original header case, which the current wording about matching without regard to case seemed to permit. A sentence stating the expected behaviour would avoid this divergence. Thanks to everyone working on this. It has been a pleasure to implement, and I am happy to help with test vectors or further interop testing. Best, Mauro _______________________________________________ Ietf-dkim mailing list -- [email protected] To unsubscribe send an email to [email protected]
-- Edmund Lodewijks <[email protected]> TZ: UTC+2 / GMT+2 _______________________________________________ Ietf-dkim mailing list -- [email protected] To unsubscribe send an email to [email protected]
