diff --git a/README.md b/README.md
index 4d45d87..477b03c 100644
--- a/README.md
+++ b/README.md
@@ -33,13 +33,13 @@ threat modeling framework to the ANS architecture. Maps threats and mitigations
across all seven layers, from foundation models through the agent ecosystem,
using a concrete multi-agent travel-booking scenario.
-## Exploratory
+## Related
**[SENDER_VERIFICATION_SPEC.md](SENDER_VERIFICATION_SPEC.md)** — Per-sender
-email verification. Applies ANS principles (per-entity signing keys, SCITT
-transparency log, DNS discovery) to email senders. Targets business email
+email verification. JWS header-only signing with key bindings sealed in a
+SCITT transparency log, discoverable via DNS. Targets business email
compromise where domain-level authentication (DKIM/SPF/DMARC) passes but the
-sender is not who they claim. Exploratory sketch, not an ANS protocol feature.
+sender is not who they claim. Not an ANS protocol feature.
## API
diff --git a/SENDER_VERIFICATION_SPEC.md b/SENDER_VERIFICATION_SPEC.md
index c17a6e0..451786f 100644
--- a/SENDER_VERIFICATION_SPEC.md
+++ b/SENDER_VERIFICATION_SPEC.md
@@ -1,8 +1,8 @@
-# Email sender trust: design sketch
+# Sender verification specification
-Status: exploratory (2026-04-08). Draft 0.6.
-Not an ANS feature. Inspired by the Agent Name Service (ANS), which anchors
-AI agent identity to domain names using the same three components described here.
+Version 1.0 (2026-04-09).
+Not an ANS feature. Applies principles from the Agent Name Service (ANS),
+which anchors AI agent identity to domain names.
## The problem
@@ -19,28 +19,19 @@ and changes the bank account number. The recipient sees a familiar sender,
a familiar subject line, and a valid signature on every message in the
conversation. The only difference is the routing number.
-The same failure occurs when an attacker registers `acme-billing.com` and
-configures all three protocols correctly. The signatures pass. The domain
-is fake.
+An attacker who registers `acme-billing.com` and configures all three
+protocols correctly passes every check. The domain is fake.
S/MIME (the standard for signing individual email messages) and PGP can bind
a signature to a specific sender, but both require the receiver to already
trust the sender's Certificate Authority or to have exchanged keys manually.
Neither publishes key bindings to a transparency log, an independent,
append-only record that receivers could check without trusting the sender's
-organization. When a key is compromised, the damage is discovered afterward.
-
-These attacks share a trait: the email travels from a known sender to a
-specific recipient. Wire instructions, payment notifications, executive
-requests. This design targets that class of email. The per-sender proof
-survives intact through intermediaries that don't modify the body. When a
-security gateway does modify the body (URL rewriting, banner injection),
-the gateway verifies the proof first, then applies its changes.
+organization.
What if the receiver could check the sender's signing key against an
independent log, without trusting the sender's CA or exchanging keys in
-advance? That requires three things working together: per-sender keys, a
-transparency log, and DNS-based discovery.
+advance?
```mermaid
flowchart LR
@@ -62,59 +53,55 @@ flowchart LR
```
*DKIM, SPF, and DMARC prove the email came from the domain. They cannot
-prove which sender on that domain sent it. Per-sender keys, sealed into a
-transparency log and discoverable via DNS, close the gap.*
+prove which sender on that domain sent it.*
## How it works
-ANS proved this combination for AI agents. The same four components apply
-to email senders.
-
```mermaid
flowchart TD
RA["Registration Authority
(domain owner)"]
TL["Transparency Log
(independent, append-only)"]
DNS["DNS TXT record
(one per domain)"]
- Sender["Sender's email client
(signs with per-sender key)"]
- Receiver["Receiver's email client
(verifies signature + TL proof)"]
+ Sender["Sender's email client
(signs headers with JWS)"]
+ Receiver["Receiver's email client
(verifies JWS + TL proof)"]
RA -- "seals binding" --> TL
RA -- "publishes pointer" --> DNS
- Sender -- "staples TL proof
inside S/MIME" --> Receiver
+ Sender -- "JWS header +
TL proof" --> Receiver
Receiver -- "looks up TL URL" --> DNS
- Receiver -- "fetches binding +
inclusion proof" --> TL
+ Receiver -- "verifies inclusion
proof" --> TL
```
-*The RA and the log are operated independently. A compromised RA cannot
-rewrite the log; a compromised log cannot fabricate RA signatures. The
-receiver needs no prior trust relationship with either.*
+*A compromised RA cannot rewrite the log. A compromised log cannot fabricate
+RA signatures. The receiver needs no prior trust relationship with either.*
### Per-sender signing key
Each protected sender gets a dedicated key pair (Ed25519 or ECDSA P-256).
-The private key stays in a hardware security module (HSM), secure enclave, or
-managed service account on the sender's infrastructure. It never leaves that
-boundary.
+An organization whose CFO already has a PGP key pair can register that
+existing key instead of generating a new one. The private key stays in a
+hardware security module (HSM), secure enclave, or managed service account
+on the sender's infrastructure. It never leaves that boundary.
-The identifier is the plain email address: `billing@acme.com`. The
-transparency log indexes by sender address. When the key rotates, a new
-event is sealed with a fresh key ID. No version numbers are needed because
-email senders, unlike software agents, don't change code between rotations.
+The identifier is the plain email address: `billing@acme.com`. In public
+logs, the address is stored as a salted hash to prevent enumeration. When
+the key rotates, a new event is sealed with a fresh key ID.
### Transparency log
An append-only log based on SCITT (RFC 9943, a standard for supply-chain
integrity that defines tamper-evident logs with cryptographic proofs). Every
key registration, rotation, and revocation is a sealed event accompanied by
-an inclusion proof, a compact receipt that proves the event exists in the log
-without downloading the entire log.
+an inclusion proof, a compact receipt that proves the event exists in the
+log without downloading the entire log.
The receiver verifies the inclusion proof without trusting the registrar. If
-the registrar is compromised, the log's signed checkpoints (periodic snapshots
-of the log state, signed by the log operator) expose unauthorized entries.
+the registrar is compromised, the log's signed checkpoints (periodic
+snapshots of the log state, signed by the log operator) expose unauthorized
+entries.
-The log can be public (internet-scale, like Certificate Transparency logs for
-TLS), private (enterprise internal), or hybrid (private with selective
+The log can be public (internet-scale, like Certificate Transparency logs
+for TLS), private (enterprise internal), or hybrid (private with selective
publication to a public log).
### DNS discovery
@@ -126,7 +113,6 @@ _[prefix].[domain]. IN TXT "v=[VER]1; log=https://tl.example.com; ttl=300"
```
The record prefix and version tag are placeholders pending a project name.
-The fields:
| Field | Required | Purpose |
| ----- | -------- | ------- |
@@ -134,6 +120,8 @@ The fields:
| `log` | Yes | Transparency log URL for sender key lookups |
| `ra` | No | RA identifier (for federated deployments) |
| `ttl` | No | Recommended receiver cache duration in seconds (default 300) |
+| `salt` | No | Hex-encoded salt for sender address hashing in public logs |
+| `strict` | No | When `1`, receivers MUST quarantine email from Tier 1 senders that arrives without a Sender-Verification header |
Receivers that don't support this system ignore the record.
@@ -142,9 +130,8 @@ a DMARC lookup failure should not prevent the log lookup.
### Registration Authority
-The domain owner controls sender registration. An enterprise can run its own
-RA (the same model used for internal ANS deployments) or delegate to a
-third-party RA.
+The domain owner controls sender registration. An enterprise can run its
+own RA or delegate to a third party.
The RA registers sender bindings and seals them to the log. When the CEO's
account is compromised, the IT security team revokes the CEO's key through
@@ -157,40 +144,77 @@ or alter events the log has already sealed.
Not every sender in a 50,000-employee enterprise needs an individual key.
-**Tier 1 (high-value senders).** Per-address registration. The CFO, the billing
-system, the automated payment notifications, customer support addresses. These
-are the senders whose compromise causes wire transfers, credential theft, or
-regulatory exposure. Each gets a dedicated signing key, sealed to the log,
-revocable by the domain's RA.
+**Tier 1 (high-value senders).** Per-address registration. The CFO, the
+billing system, the automated payment notifications, customer support
+addresses. These are the senders whose compromise causes wire transfers,
+credential theft, or regulatory exposure. Each gets a dedicated signing
+key, sealed to the log, revocable by the domain's RA.
-**Tier 2 (general staff).** No per-address log entry. General staff relies on
-DKIM for domain-level authentication. An optional domain-level "catch-all"
-binding in the log could cover all non-Tier-1 senders, but the primary defense
-remains DKIM/SPF/DMARC.
+**Tier 2 (general staff).** No per-address log entry. General staff relies
+on DKIM for domain-level authentication. An optional domain-level
+"catch-all" binding in the log could cover all non-Tier-1 senders, but the
+primary defense remains DKIM/SPF/DMARC.
The dividing line is financial exposure. An employee sharing a meeting link
does not need the same key management as the CFO authorizing a wire transfer.
## Sending an email
-The sender signs the email with S/MIME using their per-sender private key.
-The proof travels inside the S/MIME signed attributes, a section of the CMS
-(Cryptographic Message Syntax) envelope that carries additional authenticated
-data alongside the signature. The sender staples two items:
-
-- The transparency log inclusion proof for the current key binding.
-- A signed checkpoint proving freshness.
-
-No new email headers. The proof rides inside the CMS envelope through any
-mail transfer agent (MTA), the servers that relay messages between sender and
-receiver. An MTA that strips unknown headers cannot strip signed attributes
-inside a CMS structure.
+The sender signs the email's headers with a JWS detached signature
+(RFC 7515, Appendix F). The signature and the transparency log inclusion
+proof travel together in a new email header. An ECDSA P-256 signature is
+approximately 86 characters. ARC (RFC 8617, the Authenticated Received
+Chain) already uses header-only signing in email, so the pattern has
+precedent.
+
+The signed header set:
+
+| Header | Required | What it prevents |
+| ------ | -------- | ---------------- |
+| From | Yes | Sender impersonation |
+| Date | Yes | Replay of old messages |
+| Message-ID | Yes | Replay of old messages |
+| To | Yes | Replay to unintended recipients |
+| Cc | Yes | Replay to unintended recipients |
+| Reply-To | Yes | Response redirection by an intermediary |
+| In-Reply-To | Yes | Thread hijacking (forging a reply into an existing conversation) |
+| References | Yes | Thread hijacking (forging a reply into an existing conversation) |
+| Subject | Recommended | Mailing lists that add a prefix invalidate only this field |
+
+When a header in the signed set is absent from the original message
+(for example, In-Reply-To on a message that is not a reply), signing
+the absence prevents an intermediary from adding it later.
+
+Header signing does not prove the body arrived intact. Tier 1 senders
+SHOULD include a body hash (`bh=`). When present, the receiver can detect
+body modification. When absent or non-matching, the sender identity is
+still verified but the body is not.
+
+Example header (simplified):
+
+```text
+Sender-Verification: v=SV1;
+ a=ES256;
+ s=billing@acme.com;
+ h=from:date:subject:message-id:to:cc:reply-to:in-reply-to:references;
+ bh=sha256:7d8e...f1a2;
+ sig=MEUCIQD...;
+ proof=eyJ0cmVlU2l6ZSI6MTQyMzg3...
+```
-S/MIME already defines email signing, and the CMS structure is extensible
-via signed attributes. A new signature format would require changes to every
-mail transfer agent in the path. Layering on S/MIME requires only email
-client changes and a new OID (Object Identifier, the numeric tag that
-identifies this attribute type within the CMS structure).
+| Field | Purpose |
+| ----- | ------- |
+| `v` | Format version |
+| `a` | Signing algorithm |
+| `s` | Sender address in plaintext (matches From header). The receiver hashes this with the published salt for log lookup. |
+| `h` | Signed header fields |
+| `bh` | Body hash for integrity verification. RECOMMENDED for Tier 1 senders. |
+| `sig` | JWS detached signature over the listed headers |
+| `proof` | Base64-encoded binding + SCITT receipt (see Data formats) |
+
+Mail transfer agents relay headers they don't recognize, so no MTA changes
+are required. Verification happens at the receiver's email client or
+security gateway.
## Receiving an email
@@ -200,13 +224,15 @@ sequenceDiagram
participant D as DNS
participant T as Transparency Log
- R->>R: Parse From: address + S/MIME signature
+ R->>R: Parse Sender-Verification header
+ R->>R: Extract binding + SCITT receipt from proof
+ R->>R: Hash binding, compare to bindingHash in receipt
R->>D: Look up _[prefix].[domain] TXT
D-->>R: TL URL (cached per TTL)
- R->>T: Query current binding for billing@acme.com
- T-->>R: Public key + inclusion proof + checkpoint
- R->>R: Verify signature matches bound key
- R->>R: Verify inclusion proof (not revoked)
+ R->>T: Verify inclusion proof against log
+ T-->>R: Confirmed (entry exists, not revoked)
+ R->>R: Extract public key from binding
+ R->>R: Verify JWS signature over signed headers
alt All checks pass
R->>R: Accept (even if DKIM failed)
else Any check fails
@@ -214,19 +240,77 @@ sequenceDiagram
end
```
-*The receiver's three lookups: DNS for the log URL, the log for the key
-binding, then local signature verification. No prior trust relationship
-with the sender's RA or CA is required.*
+*The binding travels with the email. The log confirms it was registered.
+No key-fetch round trip to the RA. No prior CA trust required.*
If no DNS record exists for the sender's domain, the receiver treats the
email as legacy.
+A domain that publishes the DNS record can also declare which senders are
+Tier 1 in the transparency log. When an email arrives from a listed sender
+without a Sender-Verification header, the absence is a scoring signal. The
+domain told the world to expect a proof from that sender, and the proof is
+missing. A Trust Index that consumes the log can lower the sender's score
+on that basis, the same way it lowers an agent's integrity score when a
+Trust Card is missing. The receiver's gateway acts on the score.
+
+## Headers and intermediaries
+
+```mermaid
+flowchart TB
+ subgraph email ["Email message"]
+ subgraph headers ["Headers"]
+ FROM["From: billing@acme.com"]
+ DATE["Date / Subject / Message-ID"]
+ SV["Sender-Verification: sig + proof"]
+ DKIMH["DKIM-Signature: domain key"]
+ end
+ subgraph body ["Body"]
+ CONTENT["Message content
(text, HTML, attachments)"]
+ end
+ end
+ GW["Security gateway
rewrites body"]
+ GW -.->|"modifies"| CONTENT
+ style headers fill:#e8f5e9,stroke:#388e3c
+ style body fill:#fff3e0,stroke:#f57c00
+ style GW fill:#ffebee,stroke:#c62828
+```
+
+*The JWS signature covers the headers (green). A security gateway that
+rewrites the body (orange) does not touch the headers. The sender
+verification survives.*
+
+The signature covers email headers, not the message body. Security gateways
+that rewrite URLs, inject banners, or append disclaimers change the body but
+leave the signed headers intact.
+
+The sender verification passes regardless of body modification.
+
+When a message carries a body hash (`bh=`), the gateway knows the sender
+claimed body integrity. The gateway can handle this in two ways:
+
+- **Preserve the body.** Skip URL rewriting and banner injection for
+ this message. Verify the sender proof and body hash, then deliver
+ intact. Mail flow rules already do this today for S/MIME-signed
+ messages.
+- **Verify then modify.** Check the body hash, record whether it
+ matched, then apply normal rewrites. The body hash breaks, but the
+ gateway has already recorded the integrity result.
+
+Mailing lists sometimes modify the Subject header by adding a prefix. A
+modified Subject invalidates that one field, not the verification.
+
+Intermediaries that strip unknown headers would remove the
+Sender-Verification header entirely. This is the same risk DKIM-Signature
+faces, and it is rare in practice: mail servers relay headers they don't
+recognize.
+
## Revocation
| Property | Behavior |
| -------- | -------- |
| Authority | The domain owner's RA can revoke any sender under its domain. The IT security team revokes through the RA without needing the key holder's participation. |
-| Propagation | Within the DNS cache TTL (default 300 seconds). Tier 1 domains SHOULD use a 60-second TTL. Tier 1 senders MUST staple fresh proofs, giving receivers zero-cache verification without waiting for TTL expiry. |
+| Propagation | Within the DNS cache TTL (default 300 seconds). Tier 1 domains SHOULD use a 60-second TTL. Tier 1 senders MUST include fresh proofs, giving receivers zero-cache verification without waiting for TTL expiry. |
| Push notification | The log can support webhook or subscription notification for Tier 1 senders who opt into immediate propagation. |
| Forensics | Historical bindings remain queryable with proofs. A revoked key's log history shows when it was registered, who owned it, and when it was revoked. |
@@ -245,65 +329,77 @@ Every lifecycle event is sealed to the log:
## Data formats
-### Sender binding (TL entry)
+The log and the binding are separate structures. The log seals hashes and
+identifiers only. The full binding travels with the email in the stapled
+proof, carrying the public key and owner metadata. This follows the ANS
+pattern: the Transparency Log seals a hash of the registration metadata,
+not the metadata itself.
+
+The log stores less per entry, and no personal data enters the public
+record by construction. The receiver verifies entirely from the message
+header and the log, with no round trip to the RA for key material.
-The log seals this structure for each event, indexed by sender address:
+### TL entry (what the log seals)
+
+Hashes and identifiers only. No keys, no owner names, no constraints.
```json
{
"eventType": "REGISTER",
- "sender": "billing@acme.com",
- "keyId": "ed25519:9f4a...3e2d",
- "publicKey": "MCowBQYDK2VwAyEA...",
- "owner": "Finance Ops Team ",
- "constraints": {
- "allowedIps": ["198.51.100.0/24"],
- "configHash": "sha256:7d8e...f1a2"
- },
+ "senderHash": "sha256:a1b2c3d4...e5f6",
+ "keyId": "es256:9f4a...3e2d",
+ "bindingHash": "sha256:7d8e...f1a2",
"previousKeyId": null,
- "timestamp": "2026-04-08T14:22:15Z",
- "scittLeafHash": "sha256:7d8e...f1a2"
+ "timestamp": "2026-04-08T14:22:15Z"
}
```
-The log returns this structure plus a fresh inclusion proof on query.
+The `bindingHash` is the SHA-256 of the full binding JSON. The log proves
+that a binding was registered and when. It does not store what the binding
+contains.
-### Stapled proof (CMS signed attribute)
+### Full binding (what the sender staples)
-The proof is a CMS signed attribute with a placeholder OID
-(1.3.6.1.4.1.55555.1.1, to be IANA-registered):
+The sender includes the full binding alongside the SCITT receipt in the
+Sender-Verification header. The receiver hashes the binding and compares
+the result against the `bindingHash` in the TL entry. If they match, the
+binding is authentic.
-```asn1
--- Inside signedAttrs of the SignerInfo
+```json
{
- type 1.3.6.1.4.1.55555.1.1, -- placeholder OID
- values {
- SEQUENCE {
- sender IA5String, -- "billing@acme.com"
- key-id OCTET STRING, -- 32-byte key fingerprint
- binding-hash OCTET STRING, -- SHA-256 of binding JSON
- scitt-receipt SEQUENCE {
- log-id OCTET STRING,
- tree-size INTEGER,
- leaf-index INTEGER,
- inclusion-path SEQUENCE OF OCTET STRING,
- signed-checkpoint SEQUENCE {
- timestamp GeneralizedTime,
- signature OCTET STRING
- }
- }
- }
- }
+ "senderHash": "sha256:a1b2c3d4...e5f6",
+ "keyId": "es256:9f4a...3e2d",
+ "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...",
+ "owner": "Finance Ops Team",
+ "did": null,
+ "constraints": {
+ "allowedIps": ["198.51.100.0/24"],
+ "configHash": "sha256:7d8e...f1a2"
+ }
}
```
-JSON representation (for readability):
+The `did` field is optional. A did:web identifier maps directly from the
+email address: `cfo@acme.com` becomes `did:web:acme.com:users:cfo`. The
+email address remains the primary lookup key.
+
+### Sender-Verification header proof
+
+The proof field carries both structures, Base64-encoded:
```json
{
- "sender": "billing@acme.com",
- "keyId": "ed25519:9f4a...3e2d",
- "bindingHash": "sha256:7d8e...f1a2",
+ "binding": {
+ "senderHash": "sha256:a1b2c3d4...e5f6",
+ "keyId": "es256:9f4a...3e2d",
+ "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...",
+ "owner": "Finance Ops Team",
+ "did": null,
+ "constraints": {
+ "allowedIps": ["198.51.100.0/24"],
+ "configHash": "sha256:7d8e...f1a2"
+ }
+ },
"scittReceipt": {
"logId": "sha256:550e8400-e29b-41d4-a716-446655440000",
"treeSize": 142387,
@@ -320,108 +416,44 @@ JSON representation (for readability):
}
```
-The JSON is DER-encoded (a compact binary serialization that CMS uses
-internally) inside the CMS attribute. Receivers extract and verify it
-against the log.
-
-## Body modification and gateway verification
-
-```mermaid
-flowchart TB
- subgraph email ["Email message"]
- subgraph outer ["Outer layer (DKIM / ARC)"]
- DKIM["DKIM signature
(domain key)"]
- ARC["ARC-Seal
(added by forwarder)"]
- end
- subgraph inner ["Inner layer (S/MIME + stapled proof)"]
- SIG["S/MIME signature
(per-sender key)"]
- PROOF["Stapled TL proof
(inclusion + checkpoint)"]
- end
- end
- style inner fill:#e8f5e9,stroke:#388e3c
- style outer fill:#fff3e0,stroke:#f57c00
-```
-
-*Intermediaries that only re-sign (orange layer) leave the inner S/MIME
-proof (green layer) intact. Intermediaries that rewrite the body break
-both layers. The gateway-first verification path handles the second case.*
-
-The stapled proof lives inside S/MIME signed attributes, which are protected
-by the original sender's signature. Forwarding services that re-sign with
-ARC (RFC 8617, the Authenticated Received Chain that preserves authentication
-results across mail forwarding hops) add an ARC-Seal over the outer DKIM
-chain but do not alter the inner CMS structure.
-
-When an email passes through a forwarder that only re-signs the DKIM layer,
-the inner S/MIME signature and stapled proof survive intact. The receiver
-verifies the original proof first, before evaluating any ARC seals.
-
-**The body modification problem.** Many intermediaries do more than re-sign.
-Security gateways rewrite URLs for click tracking, inject banners, and
-append disclaimers. Mailing lists add footers. Corporate relays re-encode
-attachments. Any change to the message body breaks the S/MIME signature,
-and the stapled proof breaks with it. ARC preserves DKIM authentication
-across these hops but cannot preserve an inner S/MIME signature over
-modified content.
-
-This is not a rare edge case. Security gateways sit in the inbound path of
-most enterprise email. A normal message from an external sender to a
-corporate recipient passes through the recipient's gateway, which rewrites
-the body before delivery. The DKIM body hash changes. The S/MIME signature
-breaks. The stapled proof is lost.
-
-S/MIME has had this problem since 1995. It is a primary reason S/MIME never
-reached mainstream adoption. This design inherits the limitation because it
-chose S/MIME as the carrier. The trade-off was deliberate: CMS stapling
-avoids requiring changes to every mail transfer agent in the path, which is
-the only realistic route to incremental adoption. But "no MTA changes
-required" and "survives content modification by intermediaries" are mutually
-exclusive.
-
-**Gateway-first verification.** The practical deployment path is to verify
-the proof at the ingress gateway before any body modification occurs. The
-gateway parses the S/MIME signature and stapled proof, checks the sender's
-key binding against the transparency log, records the result, then applies
-its URL rewriting, banners, and disclaimers to the now-verified message.
-The inner signature breaks, but the verification already happened.
-
-Gateways are consumers of the transparency log, not operators. The
-independence between RA, log, and receiver is the same architectural
-boundary that makes Certificate Transparency work: the verifier does not
-control the log, and the log does not control the registrar. A gateway
-that also acted as the RA or log operator would centralize trust in the
-receiver, which is the opposite of what this design provides.
-
-**Scope boundary.** Per-sender verification protects email that either
-arrives unmodified (direct sender-to-receiver) or passes through a gateway
-that verifies before rewriting. The CFO's wire transfer instructions, the
-billing system's payment notifications, the support address responding to a
-customer. These hit the gateway first. Tier 1 senders should use secure
-portal links for high-value content rather than email bodies that
-intermediaries will modify after verification.
-
-For email that routinely passes through content-modifying relays without
-pre-verification (mailing lists, forwarding rules), domain-level DKIM
-remains the authentication layer.
+The Base64-encoded proof is approximately 1 KB, comparable to a
+DKIM-Signature or ARC-Seal header.
## Relationship to existing standards
| Standard | Relationship |
| -------- | ------------ |
| DKIM/SPF/DMARC | Runs in parallel. This design adds per-sender verification on top. |
-| S/MIME | Layers on top. Reuses the CMS signature format with a new signed attribute OID. |
+| JWS (RFC 7515) | The signature format. Detached signature over email headers. |
+| ARC (RFC 8617) | Precedent for header-only signing in email. |
| Certificate Transparency | Same concept applied to email sender keys instead of TLS certificates. |
| SCITT (RFC 9943) | The log implementation. Reuses append-only log, inclusion proofs, and signed checkpoints. |
-| ARC (RFC 8617) | Inner CMS proof preserved through forwarding. ARC governs the outer DKIM chain only. |
-| BIMI / VMC / CMC | Complementary. BIMI binds a brand logo to a domain via DNS; a Verified Mark Certificate (VMC) or Common Mark Certificate (CMC) is an X.509 cert that verifies the logo belongs to the domain owner. This design verifies the *sender*. BIMI verifies the *brand*. Both signals can travel in the transparency log binding. |
+| DID (W3C) | Optional identity format in the sender binding. Not required for verification. |
+| BIMI / VMC / CMC | Complementary. BIMI verifies the brand. This design verifies the sender. Both signals can travel in the log binding. |
+
+## Why not per-sender DKIM selectors?
+
+Per-sender DKIM selectors achieve per-sender signing with existing
+infrastructure. DKIM stores its keys in DNS, which the domain owner
+controls. If the domain is compromised, the attacker can add a new DKIM
+selector, sign fraudulent messages with it, and remove the old selector
+record. No audit trail of the change survives in DNS.
+
+This design stores key events in a transparency log that the domain owner
+cannot rewrite. Every registration, rotation, and revocation is a
+permanent entry. A compromised domain can revoke a key but cannot erase
+the fact that it existed or that messages were signed with it before
+revocation.
+
+DKIM verification succeeds when the domain is compromised. It should not.
## BIMI integration
-BIMI (Brand Indicators for Message Identification) already solves a related
-problem for email: proving which brand owns the sending domain. A domain with
-a VMC has passed trademark verification. A domain with a CMC has proven at
-least one year of logo use. Self-asserted BIMI publishes a logo via DNS
-without certificate verification.
+BIMI (Brand Indicators for Message Identification) proves which brand owns
+the sending domain. A domain with a VMC (Verified Mark Certificate) has
+passed trademark verification. A domain with a CMC (Common Mark Certificate)
+has proven at least one year of logo use. Self-asserted BIMI publishes a
+logo via DNS without certificate verification.
The per-sender key binding proves that `billing@acme.com` is an authorized
sender. The VMC proves that `acme.com` is a verified brand. A single
@@ -435,10 +467,6 @@ The sender binding structure can carry BIMI status as an optional field:
| `vmcUrl` | URL to the VMC or CMC certificate (when present) |
| `logoHash` | SHA-256 of the verified logo SVG |
-A receiver or discovery service that indexes the log can then distinguish
-three levels of brand trust without performing its own DNS lookups and
-certificate downloads for every sender:
-
| BIMI status | Brand verification | Visual treatment |
| ----------- | ----------------- | ---------------- |
| VMC | Registered trademark verified by CA | Verified badge |
@@ -447,8 +475,6 @@ certificate downloads for every sender:
| Absent | No BIMI record | Generic icon |
Brands that have already invested in VMC for email reuse that proof here.
-One log query returns both the sender's key binding and the brand's
-verification status.
## Security properties
@@ -457,43 +483,81 @@ verification status.
| No CA monopoly | The receiver trusts the log and DNSSEC (DNS Security Extensions, which cryptographically sign DNS records), not a specific CA. Any RA can register senders. |
| RA compromise is detectable | Unauthorized registrations appear in the log's signed checkpoints. The log cannot suppress events the RA sealed. |
| Key compromise is bounded | Revocation propagates within the cache TTL. Historical proofs identify exactly which emails were sent with the compromised key before revocation. |
-| Backward compatible | Non-supporting receivers see ordinary S/MIME email. Mail transfer agents pass the signed attribute through untouched. |
+| Survives body modification | The JWS signature covers headers, not the body. Security gateways that rewrite URLs or inject banners do not invalidate the sender verification. |
+| Replay and thread-hijack resistant | To, Cc, Reply-To, In-Reply-To, References, Date, and Message-ID are in the mandatory signed set. Replaying to a different recipient, injecting a Reply-To, or forging a reply into an existing thread invalidates the signature. |
+| Backward compatible | Non-supporting receivers see an unrecognized header and ignore it. |
## Failure modes
| Mode | Likelihood | Mitigation |
| ---- | ---------- | ---------- |
-| Email clients don't parse custom CMS signed attributes | High today | Deploy at the security gateway first (mail filtering appliances already inspect S/MIME). Push client updates to major vendors after gateway adoption proves the value. |
-| Forwarding services strip CMS attributes | Medium | Same services already break S/MIME today. No worse than the status quo. Tier 1 senders should avoid forwarding for wire instructions and use portal links instead. |
+| Intermediary strips the Sender-Verification header | Low | Same risk as stripping DKIM-Signature. Mail servers relay headers they don't recognize. |
+| Mailing list modifies Subject header | Medium | Subject is recommended, not mandatory. A modified Subject invalidates that field only. |
+| Replay to unintended recipient | Low | To and Cc are in the mandatory signed set. Replaying to a different recipient invalidates the signature. |
+| Reply-To injection by intermediary | Low | Reply-To is in the mandatory signed set. Adding or modifying Reply-To after signing invalidates the signature. |
+| Thread hijacking via forged In-Reply-To | Low | In-Reply-To and References are in the mandatory signed set. Forging a reply into an existing conversation invalidates the signature. |
+| Header stripped on strict-mode domain | Medium | When the DNS record includes `strict=1`, the receiver quarantines email from Tier 1 senders that arrives without the header. |
| Key rotation burden for Tier 1 senders | Medium | Automated RA console with HSM integration. Same operational model as certificate lifecycle management for TLS. |
-| 5-minute revocation window exploited | Medium | MUST-staple for Tier 1 closes the window. Push notifications via webhook give near-instant propagation for opted-in receivers. |
-| Security gateway rewrites body, breaking S/MIME proof | High in enterprises | Verify at the ingress gateway before body modification. Gateway records the result, then applies its rewrites. |
+| 5-minute revocation window exploited | Medium | Fresh proofs required for Tier 1. Push notifications via webhook give near-instant propagation for opted-in receivers. |
## Privacy
-The `owner` field in the transparency log binding can contain names and
-email addresses. For public logs:
+A public transparency log that indexes by plaintext sender address exposes
+every Tier 1 sender at a domain. An attacker who queries the log can
+enumerate the CFO, the billing system, and every other registered address.
+That is a target list.
-- The owner field MUST be redacted to a role identifier ("Finance Ops Team")
- or omitted entirely. The minimum binding is sender address + public key +
- key ID.
-- When an enterprise needs owner tracking in a public log, use a pseudonym:
- SHA-256 of the owner's email address. Only the RA can re-identify it.
-- The log supports redaction events (SCITT allows replacing a leaf with a
- redacted version). Retention should align with the domain's regulatory
- requirements; automated deletion after the retention period.
+Public logs MUST index by salted hash instead of plaintext address. The
+domain publishes a salt value (rotatable) in the DNS record or the log
+metadata. The receiver computes `SHA-256(salt || "billing@acme.com")` from
+the From header and looks up the hash in the log. The log never contains
+the plaintext address.
+
+| Property | Behavior |
+| -------- | -------- |
+| Lookup | Receiver computes hash from From header + published salt, queries the log by hash |
+| Enumeration | An attacker with the salt can hash guessed addresses but cannot enumerate registered senders from the log alone |
+| Salt rotation | The domain publishes a new salt and re-registers all Tier 1 bindings under the new hashes. Old hashes become unlinkable. |
+| Private logs | Enterprises using private logs may store plaintext addresses internally. Federation to a public log replaces them with hashes. |
+
+The `owner` field in the binding carries additional risk. For public logs,
+the owner field MUST be redacted to a role identifier ("Finance Ops Team")
+or omitted. When an enterprise needs owner tracking in a public log, a
+pseudonym (SHA-256 of the owner's email, re-identifiable only by the RA)
+is sufficient.
+
+The log supports redaction events (SCITT allows replacing a leaf with a
+redacted version). Retention should align with the domain's regulatory
+requirements.
-Enterprises using private logs keep full owner data internal. Federation
-to a public log strips personally identifiable information automatically.
The DNS record contains no personal data.
+Three additional privacy techniques apply as future extensions:
+
+- **Org-level authorization.** The log could register key authorizations at
+ the organization level ("acme.com authorizes key fingerprint abc123 for
+ email signing") without binding keys to specific senders. The key-to-sender
+ mapping would live in the organization's own infrastructure, where it can
+ control access and rate-limit queries. The log reveals how many sender
+ keys an organization has, not who they belong to. This adds an HTTPS
+ round trip to the sender's endpoint for the key-to-sender resolution.
+- **Query privacy.** A receiver that queries the log for a specific sender
+ hash reveals who it is communicating with. Private Information Retrieval
+ (PIR) would let the receiver query without the log operator learning
+ which hash was checked. The ANS architecture spec (§4.9) recommends
+ this for agent discovery.
+- **Zero-knowledge freshness.** A sender could prove that its key binding
+ was registered within a recent time window without revealing which log
+ entry or which sender hash. The ANS Trust Index Spec uses zero-knowledge
+ proofs for solvency (§4.3). The same pattern applies to registration
+ freshness.
+
## Spam-control integration
-This design becomes a signal in existing spam filters the same way DMARC
-became one. Mail security gateways already parse S/MIME signatures and
-DMARC results for intent scoring. They can add a rule: a passing per-sender
-proof alongside a passing DKIM signature boosts legitimacy; a failing proof
-(even with DKIM passing) triggers quarantine.
+Mail security gateways already parse DKIM signatures and DMARC results for
+intent scoring. They can add a rule: a passing per-sender proof alongside a
+passing DKIM signature boosts legitimacy; a failing proof (even with DKIM
+passing) triggers quarantine.
Attackers cannot forge the log proof. A spoofed domain or compromised
account produces a valid DKIM signature but no valid per-sender proof.
@@ -502,11 +566,18 @@ not add false positives.
## Adoption
-S/MIME never crossed the adoption threshold because key exchange was manual
-and CA trust was painful. Here, the log replaces the CA, but receivers still
-need a reason to check it.
+Previous attempts at per-sender email signing (S/MIME, PGP) failed because
+key exchange was manual and CA trust was painful. Here, a transparency log
+replaces the CA, and DNS discovery replaces the key exchange.
-Three standards crossed the same threshold:
+S/MIME signed the message body, which broke when security gateways rewrote
+URLs or injected banners. S/MIME's encryption also prevents gateways from
+scanning message content, forcing enterprises to write mail flow rules that
+bypass S/MIME emails entirely. Header-only signing survives body
+modifications, leaves the message body readable for gateway inspection,
+and requires neither MTA changes nor S/MIME client support.
+
+Three standards crossed a similar adoption threshold:
- **DKIM (2007).** Big senders adopted first. Gmail and Yahoo published
DKIM signatures; receivers added checks. Enforcement followed volume.
@@ -518,42 +589,41 @@ Three standards crossed the same threshold:
pressure from large receivers drove adoption.
Enterprise pilots with private logs come first. Large cloud email providers
-who control the majority of outbound enterprise mail adopt next. Receiver-side
-enforcement for domains that publish the DNS record follows. Financial
-institutions will likely require it for wire transfer instructions before
-any general mandate.
+who control the majority of outbound enterprise mail adopt next.
+Receiver-side enforcement for domains that publish the DNS record follows.
+
+Financial institutions will likely require it for wire transfer instructions
+before any general mandate.
## Trust scoring
The ANS Trust Index Spec defines a scoring engine that crawls transparency
-logs, assembles a manifest of signals, and returns a signed trust evaluation.
-It scores agents across five dimensions: integrity, identity, solvency,
-behavior, and safety.
-
-Three of those dimensions apply to email senders without modification.
-Integrity covers key age, rotation history, and receipt freshness. Identity
-covers BIMI status, DMARC policy, and certificate type (the Trust Index
-Spec already defines these as inherited trust anchors in §5.6). Behavior
-covers revocation history and complaint patterns. Solvency and safety do
-not apply.
-
-The transparency log format is the same (SCITT). The evaluation API is the
-same. The Trust Manifest schema is extensible. An implementation that scores
-ANS agents can score email senders from the same infrastructure by adding
+logs, assembles a manifest of signals, and returns a signed trust
+evaluation. It scores agents across five dimensions: integrity, identity,
+solvency, behavior, and safety.
+
+Three of those dimensions apply to email senders. Integrity covers key age,
+rotation history, and receipt freshness. Identity covers BIMI status, DMARC
+policy, and certificate type (the Trust Index Spec defines these as
+inherited trust anchors in §5.6). Behavior covers revocation history and
+complaint patterns. Solvency and safety do not apply.
+
+The log format, evaluation API, and manifest schema are shared. An
+implementation that scores ANS agents can score email senders by adding
email-specific signal blocks to the manifest.
## Open questions
1. **Project name.** Working name TBD. The DNS record prefix, format version
- tag, and OID descriptions depend on it.
-2. **S/MIME signed attribute OID.** Placeholder OID 1.3.6.1.4.1.55555.1.1
- used in this sketch. Needs IANA registration under the final project name.
+ tag, and header name depend on it.
+2. **JWS header name.** `Sender-Verification` is a placeholder. The final
+ name needs IANA registration.
3. **TL query protocol.** REST API (mirrors ANS TL) is the current
recommendation. A standardized query format would enable interoperability
across log implementations but adds specification work.
4. **Enterprise internal deployment.** Private RA + private log for
- employee-to-employee mail. Federation to a public log for external senders.
- The boundary and trust model for federation need specification.
+ employee-to-employee mail. Federation to a public log for external
+ senders. The boundary and trust model for federation need specification.
5. **Bulk registration API.** Enterprises registering hundreds of Tier 1
senders need a batch registration endpoint. Single-sender registration
doesn't scale for initial rollout.
@@ -564,3 +634,9 @@ email-specific signal blocks to the manifest.
trust? Certificate Transparency faced the same question and took years
to resolve through browser vendor policy. The email ecosystem has no
equivalent enforcement lever today.
+8. **DID integration.** The `did` field in the sender binding is optional.
+ A did:web identifier maps directly from the email address:
+ `cfo@acme.com` becomes `did:web:acme.com:users:cfo`. A receiver could
+ derive the DID from the From header, attempt resolution, and degrade
+ gracefully if no DID document exists. The email address remains the
+ primary identifier and lookup key.