Skip to content

Brand Protocol response-signing: payload-envelope JWS replay protection #4716

Description

@bokelley

Gap

The verify_brand_claim / verify_brand_claims response signature is a JWS payload envelope under adcp_use: "response-signing" (see security.mdx — designated-task response signing). The envelope schema currently carries no freshness binding — no iat, no nonce, no echo of the request that triggered the response.

Threat: an attacker (or stale cache) that captures one signed disputed response can replay it indefinitely against any future query for the same claim. The trust model treats rejection direction as authoritative, which means a captured-and-replayed disputed blocks legitimate future business association. The signature continues to verify under the brand's published key — disputed is permanent until the JWK rotates.

Surfaced by the security-review pass on PR #4703 (#NNNN — link to be added). The reviewer flagged this as ship-blocking on the brand-protocol primitive itself, but out of scope for the docs-consistency PR that named the primitive.

Proposed change

One of:

  1. Schema-level freshness binding. Add normative envelope fields:
    • iat (issued-at timestamp, integer seconds since epoch).
    • nonce (echo of the request's idempotency key or a request-bound identifier — claim_id + caller identity is the natural choice for verify_brand_claim).
    • Verifier MUST reject envelopes with iat outside an implementation-declared freshness window AND MUST reject duplicate (nonce, kid) pairs within the window.
  2. Normative trust-model disclaimer. If schema change is too costly to add post-ship, the trust model section MUST explicitly disclaim freshness: "captured signed responses are valid indefinitely until the signing JWK rotates; consumers SHOULD treat responses older than declared max-age as untrusted regardless of signature."

Option 1 is the correct shape protocol-wise. Option 2 is a defensible holding pattern if implementers have already shipped envelopes that can't accommodate new required fields.

Acceptance

  • Decision recorded on this issue (option 1 vs. 2) before 3.2 freeze.
  • If option 1: envelope schema PR with iat and nonce rules; conformance fixtures for both positive (fresh) and negative (stale, replayed) cases.
  • If option 2: spec text amendment to verify_brand_claim.mdx#trust-model and a follow-up to revisit at 4.0.

Out of scope

  • JWK uniqueness across brand-agents (filed separately).
  • Tenant binding in the payload envelope (filed separately).
  • RFC 9421 §2.2.9 transport response signing (forbidden in 3.x, deferred to 4.0).

Metadata

Metadata

Assignees

No one assigned

    Labels

    brandIssue concerns the brand protocol domainclaude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.needs-wg-reviewBlocked on a working-group decision — surface in WG meeting agendasseverity:significantspec / protocoltriage:verify-closeNeeds verification that linked work landed, then close if satisfied

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions