Internet Protocols · Email Protocols
ARC and Authentication Across Forwarding Hops
How a chain of seals lets a receiving domain trust authentication results recorded before a message was forwarded, and where that trust stops being safe.
A message that passes SPF and DKIM at its destination proves that the author’s domain authorized it. A message that was forwarded through an intermediary arrives without that proof, because the intermediary changed the IP the message came from and may have changed the message. The destination sees a failure and cannot tell a legitimate forward from a forgery. ARC is an attempt to carry the earlier verdict forward, signed by each hop that handled the message.
The problem a forward creates
Consider a university alumnus with a forwarding address. Mail sent to the old address arrives at the university’s mail system, passes SPF and DKIM there, and is then re-sent to the new address. The re-send has a new connecting IP and, usually, a new envelope sender. At the new destination, SPF fails because the university’s IP is not in the author’s record. If the forwarder also appended a footer or rewrote the subject, DKIM fails too.
Under a DMARC policy of p=reject, the message is refused. The author did nothing wrong, the forwarder did nothing wrong, and the destination applied a policy correctly. The failure is structural: the authentication mechanisms were designed for a direct path, and the policy has no way to express “trust the verdict from two hops ago”.
The naive fix is to let the forwarder add an Authentication-Results header saying the message passed, and to have the destination trust it. That header is unauthenticated text. Any sender can write one claiming a pass, so trusting it is equivalent to removing authentication entirely.
ARC’s design constraint is therefore precise: carry the earlier verdict in a form that later hops can verify was written by a specific hop, and that cannot be altered or spliced in from another message.
Three fields, one set per hop
ARC defines three header fields, and each hop that participates adds one of each. The three together form a set, identified by an instance number in the i= tag. Instance 1 is the first hop to add ARC fields; each subsequent hop increments.
ARC-Authentication-Results (AAR) records what the hop observed. It has the syntax of Authentication-Results with the instance tag added, and it names the authserv-id of the hop plus the results for SPF, DKIM, and DMARC as evaluated there.
ARC-Message-Signature (AMS) is a DKIM-style signature over the message as it exists at that hop, including the AAR. It uses the same algorithms and canonicalization rules as DKIM, described in DKIM Canonicalization and What It Actually Signs, with the instance tag and a domain and selector identifying the hop’s key.
ARC-Seal (AS) is the field that makes the chain a chain. It signs not the message but the ARC sets themselves: the seal for instance N covers the seals and the AAR and AMS fields for instances 1 through N, plus its own instance. Because each seal covers all preceding seals, altering any earlier set invalidates every seal after it.
ARC-Seal: i=1; a=rsa-sha256; t=1704067200; cv=none;
d=forwarder.example; s=arc1; b=...
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed;
d=forwarder.example; s=arc1; h=from:to:subject:date;
bh=...; b=...
ARC-Authentication-Results: i=1; mx.forwarder.example;
spf=pass smtp.mailfrom=alice@example.com;
dkim=pass header.d=example.com;
dmarc=pass header.from=example.com
The ordering of the fields in the header block matters for verification, and the specification fixes it: for each instance, the AAR comes first, then the AMS, then the AS, with instances in ascending order. A message whose ARC fields are reordered is a message whose chain does not validate, which prevents an attacker from rearranging sets to change which seal covers which.
The cv= tag on the seal records the chain validation status the hop observed when it added its set: none for the first instance, pass if the chain it received validated, fail if it did not. The value is a claim about history, and it is covered by later seals, so a hop cannot retroactively claim that a broken chain was intact.
Validation is a fold over the chain
A receiver that finds ARC fields performs a validation before using any of them. The procedure is a loop over instances, and its cost grows with the length of the chain.
For each instance from 1 upward, the verifier checks the AMS against the message and checks the AS against the accumulated ARC sets up to and including that instance. The AMS for instance N is checked against the message as it exists now, not as it existed at hop N, because the signature covers the message the hop saw, and if any later hop modified a signed field, that AMS fails.
That asymmetry is deliberate and is the main source of ARC’s limits. A forwarder that adds its own footer breaks the author’s DKIM and also breaks any earlier AMS that covered the body. The later hop’s own AMS covers the message as it now is, so the chain can still validate from that point forward, but the earlier evidence is gone.
The fold produces one of the cv outcomes. pass means every seal validated and every AMS that was expected to validate did. fail means a seal did not validate, which indicates tampering or a key problem. none means there was no chain to validate.
A chain that validates is evidence about the path, not about the message. It says that the hop which added the last seal is the hop that the key belongs to, and that the ARC fields it received were the ones the previous hop wrote. It does not say the hop was honest: a forwarder can write an AAR claiming a DMARC pass for a message that never passed anything. The seal proves authorship of the claim, not truth of the claim.
What a receiver does with a validated chain
The specification deliberately leaves the policy to the receiver. The chain is an input, and the receiver decides how much weight to give it.
The common use is to soften a DMARC failure. A message that fails DMARC at the destination but arrives with a valid ARC chain whose AAR records a DMARC pass at an earlier hop is a plausible legitimate forward. The receiver may deliver it rather than reject it, often to a less prominent folder, and record the reasoning in its own Authentication-Results.
The trust decision has to consider which hop the chain ends at. A chain whose last seal belongs to a consumer ISP is weaker evidence than one whose last seal belongs to a known mailing list operator, because the ISP’s users can send anything and the list operator’s messages are structurally constrained. Deployed receivers typically maintain a list of ARC-participating forwarders they are willing to trust, which is a reputation system layered on top of the cryptographic one.
The chain length is a cost. Each instance adds three header fields, and each field is a few hundred octets of base64. A message that has passed through several forwarders accumulates kilobytes of ARC headers before the body starts, which counts against the SIZE limit and against the practical limits of header parsing. A long chain also means more verification work per message: there are N seal verifications for N instances, but the hashed input for each seal includes every earlier ARC set, so the total hashing work grows with the square of the chain length.
Failure modes and the reason adoption is uneven
Seals break on legitimate modification. A forwarder that adds a Received header is fine, because the seal does not cover Received. A forwarder that rewrites the Subject breaks the earlier AMS and, if it also reorders or alters ARC fields, breaks the seals. The failure is indistinguishable from an attack at the verification layer.
Keys rotate and disappear. An ARC key is published in DNS like a DKIM key, under a selector at the hop’s domain. A hop that rotates keys without keeping old ones published makes its old seals unverifiable, and every chain that passes through it fails validation. The same is true of a hop that stops participating entirely: the seal remains in the message, but the key record may be gone.
The chain can be replayed. A valid ARC set from one message can be attached to a different message only if the AMS still verifies, which it will not unless the message is unchanged. The seal binds the set to the instance, but it does not bind the set to a message identifier beyond what the AMS covers. That is sufficient in practice because the AMS covers the From and the body, and a different message would fail it.
Verification is expensive and optional. A receiver that does not implement ARC ignores the fields entirely, which is legal and is what most receivers do. The mechanism only helps when both the forwarder and the destination participate, and the forwarder must choose to add the fields even though it gains nothing from doing so.
The chain does not fix the underlying problem. A domain that publishes p=reject still rejects forwarded mail that has no ARC chain, and a domain that trusts ARC chains is trusting a set of forwarders rather than the author’s domain. The mechanism moves the trust decision from “did the author authorize this IP” to “do I believe this forwarder’s record of what it saw”, which is a different question with a different threat model.
The architectural position is that ARC is a repair for a specific interaction between forwarding and DMARC, described in DMARC Alignment Between Header and Envelope Domains, and not a general improvement to authentication. It does not make a forged message pass; it makes a legitimate forwarded message less likely to be rejected. The distinction is the same one that runs through the whole authentication layer: proving which domain sent a message is not the same as proving the message is safe, and every mechanism in this chain (SPF’s IP authorization in SPF Evaluation and Its Ten-Lookup Budget, DKIM’s signature, DMARC’s alignment, and ARC’s seal) authenticates an identity rather than a intent.