Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/redacted-circuit-maintenance/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Each section below is a rule plus the incident that produced it (2026-09-27, PR
- Keys come from `src/utils/dkimKeys.ts` (a copy lives in the CLI). It tries Google + Cloudflare
DoH first, then every archived key for the selector. `@zk-email/helpers` alone tries one DNS
key, and only the first archived key, and only if the DNS lookup fails. That missed 12 of 132
corpus emails whose keys were rotated under the same selector (mostly Amazon/SES).
corpus emails whose keys were rotated under the same selector.
- archive.prove.email allows **10 requests/min per IP**. On a 429 it returns an error object, and
the helper's own fallback crashes ("data.find is not a function"). An unpaced corpus
DKIM sweep therefore misreports ~30 emails as failures. When sweeping, space archive calls
Expand Down
2 changes: 1 addition & 1 deletion skills/redacted-email-proof/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ the smallest tier that fits (RSA 1024- or 2048-bit keys):
| large | 4,096 B | 48,000 B | ~1 min on 18 cores (longer on fewer), **~7 GB RAM** |
| xl | 4,096 B | 100,352 B | ~2–4 min on 18 cores (longer on fewer), **~14 GB RAM** (16 GB+ machine) |

The large-tier figures were measured on a 34 KB PayPal receipt: 58 s, 6.6 GB peak for the native
The large-tier figures were measured on a real 34 KB receipt: 58 s, 6.6 GB peak for the native
`bb`. XL figures were measured on 87.6 KB and 94.9 KB receipts (loaded machine; faster idle). Peak
memory is the kernel's lifetime physical footprint of the `bb` process (`/usr/bin/time -l`): large
7.4 GB, XL 13.7 GB (18-core M-series Mac, 2026-09-29).
Expand Down
45 changes: 42 additions & 3 deletions skills/redacted-email-proof/scripts/redacted.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,34 @@ export function domainsAligned(fromDomain, signingDomain) {
return f === d || f.endsWith("." + d) || d.endsWith("." + f);
}

// REASON: Google Workspace signs mail for domains without their own DKIM as
// d=<from-domain, dots as dashes>.<yyyymmdd>.gappssmtp.com, and Microsoft 365 as
// d=<tenant>.onmicrosoft.com. A Google label equal to the dashed From: domain ties the signature
// to that domain's Workspace (Google signs only for its customers and only From: addresses they
// verified); Microsoft tenant names aren't tied to domains, so those stay unproven.
// NOTE: dots->dashes is ambiguous (bank.co.uk / bank-co.uk -> bank-co-uk) and Workspace's SMTP
// relay can send any From:, so only one-dot From: domains (unambiguous) count as matched.
// Same as src/utils/keyBinding.ts signerProvider (see its NOTE on the dots->dashes collision).
export function signerProvider(fromDomain, signingDomain) {
const d = signingDomain.toLowerCase();
const g = /^([a-z0-9-]+)\.\d{8}\.gappssmtp\.com$/.exec(d);
if (g) {
const f = fromDomain?.toLowerCase() ?? "";
const unambiguous = f.split(".").length === 2;
return { kind: "google-workspace", tenant: g[1], matchesFrom: unambiguous && g[1] === f.replace(/\./g, "-") };
}
const ms = /^([a-z0-9-]+)\.onmicrosoft\.com$/.exec(d);
if (ms) return { kind: "microsoft-365", tenant: ms[1], matchesFrom: false };
return null;
}

function alignmentOf(fromDomain, signingDomain) {
const provider = signerProvider(fromDomain, signingDomain);
if (fromDomain && domainsAligned(fromDomain, signingDomain)) return { fromAligned: true, alignedVia: "domain", provider };
if (provider?.matchesFrom) return { fromAligned: true, alignedVia: "google-workspace", provider };
return { fromAligned: false, alignedVia: null, provider };
}

// Decode the masked header/body a proof publishes (0x00 = masked). v2 (prefix 5) commits to the
// signed lengths and zeroes everything past them, so this is an exact slice.
// Throws on non-canonical inputs (see canonicalPublicInputs).
Expand Down Expand Up @@ -924,7 +952,7 @@ export async function checkKeyBinding(publicInputs, header, circuit, resolveKeys
if (!n) continue;
const got = await pubkeyHash(n, keyBits, version);
if (got.length === want.length && got.every((v, i) => v === want[i])) {
return { ...out, domain: d, selector: s, matched: out.tried.at(-1), fromAligned: from.domain ? domainsAligned(from.domain, d) : false };
return { ...out, domain: d, selector: s, matched: out.tried.at(-1), ...alignmentOf(from.domain, d) };
}
}
}
Expand Down Expand Up @@ -1173,6 +1201,8 @@ async function cmdVerify(ref, opts) {
fromAddress: binding?.from?.address ?? null,
fromHidden: binding?.from?.hidden ?? false,
fromAlignedWithDkimDomain: binding?.fromAligned ?? null,
fromAlignedVia: binding?.alignedVia ?? null,
signerProvider: binding?.provider ?? null,
maskedHeader: outs.headerText,
maskedBody: outs.bodyText,
};
Expand All @@ -1195,6 +1225,12 @@ async function cmdVerify(ref, opts) {
: `dkim key: NOT matched to any published key for ${pairs} ` +
`(tried: ${result.keysTried.join("; ") || "no key found"}${binding.archiveError ? `; archive: ${binding.archiveError}` : ""}). Treat the sender as unproven.`,
);
if (result.keyMatches && result.fromAlignedVia === "google-workspace") {
console.log(
`from: ${result.fromAddress} — signed by Google Workspace for ${binding.from.domain} (d=${result.dkimDomain}; ` +
`Google signs this way for Workspace domains without their own DKIM key).`,
);
}
if (result.keyMatches && result.fromAlignedWithDkimDomain === false) {
// REASON: usually innocent (a mailing service signing with its own domain), so name the
// signing domain and the matching key, and say that only the From: line is unproven.
Expand All @@ -1203,8 +1239,11 @@ async function cmdVerify(ref, opts) {
result.fromHidden
? `warning: the ${signer}, but the From: address is hidden, so it isn't proven to be on ${result.dkimDomain}.`
: result.fromAddress
? `warning: From: ${result.fromAddress} is on ${binding.from.domain}, but the ${signer}. This is often a quirk ` +
`of the sender's email service (it signs with its own domain), but the From: address is NOT proven.`
? `warning: From: ${result.fromAddress} is on ${binding.from.domain}, but the ${signer}. ` +
(result.signerProvider?.kind === "microsoft-365"
? `${result.dkimDomain} is a Microsoft 365 tenant (Microsoft signs this way when a domain has no DKIM key of its own), ` +
`and which domains a tenant owns isn't public, so the From: address is NOT proven.`
: `This is often a quirk of the sender's email service (it signs with its own domain), but the From: address is NOT proven.`)
: `warning: the ${signer}, but the From: address can't be read, so it isn't proven.`,
);
}
Expand Down
75 changes: 72 additions & 3 deletions src/utils/keyBinding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,46 @@ export function domainsAligned(fromDomain: string, signingDomain: string): boole
return f === d || f.endsWith("." + d) || d.endsWith("." + f);
}

export interface SignerProvider {
kind: "google-workspace" | "microsoft-365";
/** the tenant label in d= */
tenant: string;
/** the tenant provably belongs to the From: domain */
matchesFrom: boolean;
}

/**
* Mail platforms that sign customer mail with their own domain when the customer hasn't set up
* DKIM for its domain.
* REASON: plain domain alignment calls these "From: not proven", although for Google Workspace the
* d= names the customer's domain:
* - Google Workspace signs as d=<from-domain, dots as dashes>.<yyyymmdd>.gappssmtp.com
* (school.example -> school-example.20230601.gappssmtp.com). Google signs that way only for its own
* Workspace customers, so a label equal to the dashed From: domain ties the signature to that
* domain's Workspace.
* NOTE: dots->dashes is not one-to-one: bank.co.uk and bank-co.uk both give bank-co-uk, and
* mail.bank.example / mail-bank.example both give mail-bank-example. Workspace's SMTP relay can be
* set to send any From: address, so the owner of a colliding domain could produce this exact d=.
* The mapping is only unambiguous when the From: domain has exactly one dot (a TLD can't contain
* a plain hyphen), so matchesFrom is limited to those. Longer domains stay unproven with a
* Google-specific explanation. (Same reasoning made zk-email-verify#324 keep gappssmtp opt-in.)
* - Microsoft 365 signs as d=<tenant>.onmicrosoft.com. The tenant name isn't derived from any
* domain and which domains a tenant owns isn't public, so the From: line stays unproven. The
* banner just explains the quirk.
*/
export function signerProvider(fromDomain: string | null, signingDomain: string): SignerProvider | null {
const d = signingDomain.toLowerCase();
const g = /^([a-z0-9-]+)\.\d{8}\.gappssmtp\.com$/.exec(d);
if (g) {
const f = fromDomain?.toLowerCase() ?? "";
const unambiguous = f.split(".").length === 2;
return { kind: "google-workspace", tenant: g[1], matchesFrom: unambiguous && g[1] === f.replace(/\./g, "-") };
}
const ms = /^([a-z0-9-]+)\.onmicrosoft\.com$/.exec(d);
if (ms) return { kind: "microsoft-365", tenant: ms[1], matchesFrom: false };
return null;
}

// ---------------------------------------------------------------------------------------------
// key binding

Expand Down Expand Up @@ -194,6 +234,10 @@ export interface KeyBinding {
from: ReturnType<typeof fromAddress>;
/** From: domain aligned with the matched signing domain; null when there is no match */
fromAligned: boolean | null;
/** how fromAligned was established */
alignedVia?: "domain" | "google-workspace";
/** the signer is a mail platform's domain (Google Workspace, Microsoft 365) */
provider?: SignerProvider | null;
/** archive lookup failed (e.g. its 10 req/min limit); an unmatched result may be a false negative */
archiveError?: string;
}
Expand Down Expand Up @@ -237,7 +281,7 @@ export async function bindDkimKey(
domain,
selector,
keySource: k.source,
fromAligned: from.domain ? domainsAligned(from.domain, domain) : false,
...alignmentOf(from.domain, domain),
};
}
}
Expand All @@ -246,6 +290,13 @@ export async function bindDkimKey(
return result;
}

function alignmentOf(fromDomain: string | null, signingDomain: string) {
const provider = signerProvider(fromDomain, signingDomain);
if (fromDomain && domainsAligned(fromDomain, signingDomain)) return { fromAligned: true, alignedVia: "domain" as const, provider };
if (provider?.matchesFrom) return { fromAligned: true, alignedVia: "google-workspace" as const, provider };
return { fromAligned: false, provider };
}

/** Banner for a verified v2 proof, from its DKIM key binding. Used by the verify page; tested in tests/key-binding.test.ts. */
export function keyBindingStatus(b: KeyBinding): { verified: boolean; warning?: boolean; message: string } {
if (!b.matched) {
Expand All @@ -262,15 +313,33 @@ export function keyBindingStatus(b: KeyBinding): { verified: boolean; warning?:
}
const keyFrom = b.keySource === "archive" ? "the DKIM key archive (no longer in DNS)" : "DNS";
const key = `the DKIM key published at ${b.selector}._domainkey.${b.domain} (${keyFrom})`;
if (b.fromAligned && b.alignedVia === "google-workspace") {
return {
verified: true,
message:
`Proof verified. Signed by Google Workspace for ${b.from.domain}: the proof's key matches ${key}. ` +
`Google signs as ${b.domain} for Workspace domains that haven't set up their own DKIM key, and the ` +
`signature names ${b.from.domain}'s Workspace.`,
};
}
if (b.fromAligned) {
return { verified: true, message: `Proof verified. Signed by ${b.domain}: the proof's key matches ${key}, and the From: address is on that domain.` };
}
// REASON: an unaligned From is usually innocent. Mailing services (newsletters, receipts,
// school/church/HR systems) send on a company's behalf and sign with THEIR domain. Say exactly
// which domain signed and which key matched, and that the From: line itself isn't proven.
const quirk =
"This is often just how the sender's email service works: services that send mail on someone's behalf " +
"commonly sign with their own domain.";
b.provider?.kind === "microsoft-365"
? `${b.domain} is a Microsoft 365 tenant: Microsoft signs this way when a domain hasn't set up its own DKIM key. ` +
"Which domains a tenant owns isn't public, so this can't be tied to the From: domain."
: b.provider?.kind === "google-workspace"
? b.from.domain && b.provider.tenant === b.from.domain.replace(/\./g, "-")
? `${b.domain} is Google Workspace signing for "${b.provider.tenant}". That name matches ${b.from.domain}, but ` +
`Google writes dots as dashes, so it would also match other domains (e.g. ${b.from.domain.replace(/\.(?=[^.]*\.)/, "-")}); ` +
"the From: domain isn't proven."
: `${b.domain} is Google Workspace signing for the Workspace "${b.provider.tenant}", which doesn't match the From: domain.`
: "This is often just how the sender's email service works: services that send mail on someone's behalf " +
"commonly sign with their own domain.";
return {
verified: false,
warning: true,
Expand Down
52 changes: 52 additions & 0 deletions tests/key-binding.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,55 @@ test("real proofs: non-canonical inputs, key match, forged key, unaligned From (
await api.destroy();
}
});

// Google Workspace / Microsoft 365 sign customer mail with their own domain when the customer has
// no DKIM key of its own (see signerProvider). Shape: From school.example, d=school-example.20230601.gappssmtp.com.
test("signerProvider: Google Workspace tenant label = dashed From: domain; Microsoft 365 never ties", () => {
for (const [name, m] of both) {
assert.deepEqual(m.signerProvider("school.example", "school-example.20230601.gappssmtp.com"),
{ kind: "google-workspace", tenant: "school-example", matchesFrom: true }, name);
assert.equal(m.signerProvider("bank.example", "school-example.20230601.gappssmtp.com").matchesFrom, false, name);
assert.equal(m.signerProvider(null, "school-example.20230601.gappssmtp.com").matchesFrom, false, name);
assert.equal(m.signerProvider("my-site.example", "my-site-example.20150623.gappssmtp.com").matchesFrom, true, name);
// ambiguous: bank.co.uk and bank-co.uk both map to bank-co-uk, so neither may claim it
assert.equal(m.signerProvider("bank.co.uk", "bank-co-uk.20230601.gappssmtp.com").matchesFrom, false, name);
assert.equal(m.signerProvider("mail.bank.example", "mail-bank-example.20230601.gappssmtp.com").matchesFrom, false, name);
assert.deepEqual(m.signerProvider("school.example", "school.onmicrosoft.com"),
{ kind: "microsoft-365", tenant: "school", matchesFrom: false }, name);
assert.equal(m.signerProvider("bank.example", "esp.example"), null, name);
// not Google's shape: no 8-digit date label, or another parent domain
assert.equal(m.signerProvider("school.example", "school-example.gappssmtp.com"), null, name);
assert.equal(m.signerProvider("school.example", "school-example.20230601.gappssmtp.com.evil.example"), null, name);
}
});

const workspaceBinding = (fromDomain: string, d: string) => {
const p = site.signerProvider(fromDomain, d);
const aligned = site.domainsAligned(fromDomain, d) || !!p?.matchesFrom;
return {
matched: true, domain: d, selector: "20230601", keySource: "dns:google",
candidates: [{ domain: d, selector: "20230601" }],
from: { address: `principal@${fromDomain}`, domain: fromDomain, hidden: false },
fromAligned: aligned, alignedVia: aligned ? ("google-workspace" as const) : undefined, provider: p,
};
};

test("verify banner: Google Workspace signature for the From: domain is verified and says so", () => {
const s = site.keyBindingStatus(workspaceBinding("school.example", "school-example.20230601.gappssmtp.com"));
assert.equal(s.verified, true);
assert.match(s.message, /Google Workspace for school\.example/);
const other = site.keyBindingStatus(workspaceBinding("bank.example", "school-example.20230601.gappssmtp.com"));
assert.equal(other.verified, false);
assert.equal(other.warning, true);
assert.match(other.message, /doesn't match the From: domain/);
const ambiguous = site.keyBindingStatus(workspaceBinding("bank.co.uk", "bank-co-uk.20230601.gappssmtp.com"));
assert.equal(ambiguous.verified, false);
assert.match(ambiguous.message, /would also match other domains/);
});

test("verify banner: Microsoft 365 tenant stays unproven, with a specific explanation", () => {
const s = site.keyBindingStatus(workspaceBinding("school.example", "school.onmicrosoft.com"));
assert.equal(s.verified, false);
assert.equal(s.warning, true);
assert.match(s.message, /Microsoft 365 tenant/);
});
Loading