Skip to content

fix(crypto): jti registry lifecycle — issuing service can never verify its own tokens, per-process replay windows, issuance ignores duplicate jti #85

Description

Summary

Three defects in the jti replay-registry lifecycle, one coherent fix. All CONFIRMED BY EXECUTION in the 2026-08-23 audit of v0.2.0 @ 571073c (additions posted on #18).

1. The issuing service can never verify its own attestations

sign_verdict() registers the jti at signing (crypto.py:361); verify_attestation() checks-and-registers the same jti at verification (crypto.py:462) — against the same per-instance registry. Executed: sign a token, immediately verify it on the same A2ACryptoServiceFalse, "Replay detected: jti already seen". Every self-verification of a freshly issued token fails. Issuance and consumption need separate semantics (drop sign-time registration, or use distinct issuance/consumption registries).

2. Replay protection is per-process — multi-worker replay succeeds

JtiRegistry is in-memory per A2ACryptoService instance (crypto.py:73-123). Executed: token issued via worker-1's service verifies first-time on a fresh worker-2 service → accepted. Any horizontally scaled deployment (or restart, pre-persistence) has replay windows across workers. Same fix class as qwed-verification's multi-worker state findings: shared store (Redis et al.) or accept-and-document single-process scope.

3. Issuance ignores the dedupe result — contradictory same-jti JWTs

sign_verdict calls check_and_register(trace_id) and discards the return value. Executed: two calls with the same trace_id mint two distinct, cryptographically valid JWTs sharing one jti with different verdict claims (forwarded vs blocked). RFC 7519 jti uniqueness is violated at the source; whichever token a consumer sees first poisons the registry for the other. Fix: raise or refuse on duplicate trace_id at issuance (ties into #18's caller-controlled trace_id).

Remediation shape

One change to the registry contract: issuance registers with "seen-locally" semantics that don't poison verification, verification registers consumption, duplicates at issuance raise, and the store is shared across workers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions