{
 "slug": "signed-revocation-lists-vs-ocsp-building-fast-offline",
 "topic_id": "TOPIC-093",
 "cluster": "Cryptography & Standards",
 "tier": "Tier B",
 "title": "Revocation without a network call at verification time",
 "summary": "Online status checking leaks what you are verifying, adds latency, and fails open when the responder is down. A signed, cacheable list has none of those properties.",
 "lede": "The certificate ecosystem spent two decades learning that asking a server whether something is revoked, at the moment you need to know, is a bad design. That lesson transfers directly.",
 "date": "2024-10-30",
 "category": "Comparison",
 "author_id": "elias-vanterpool-osei",
 "tags": [
  "revocation",
  "OCSP",
  "CRL",
  "offline verification",
  "privacy",
  "nonce"
 ],
 "image_title": "Revocation Without Network Call",
 "schema": "Article",
 "key_takeaways": [
  "Online status checks leak the subject being verified to the responder and add latency to every verification.",
  "Because an unreachable responder cannot be allowed to block everything, implementations fail open — which makes the check optional in practice.",
  "A signed list distributed ahead of time is checkable offline, leaks nothing, and fails closed on the entries it covers."
 ],
 "body": [
  {
   "type": "h2",
   "text": "The three problems with asking"
  },
  {
   "type": "diagram",
   "kind": "compare",
   "alt": "Asking versus knowing",
   "caption": [],
   "nodes": "The list is distributed ahead of time, so the verification path has no network dependency.",
   "left": {
    "title": "Online status check",
    "items": [
     "Leaks the subject to the responder",
     "Round trip on the critical path",
     "Fails open when unreachable",
     "Usually cached for hours anyway"
    ]
   },
   "right": {
    "title": "Signed list",
    "items": [
     "Leaks nothing",
     "No call at verification",
     "Cached list still enforced",
     "Fresh to the publication interval"
    ]
   }
  },
  {
   "type": "table",
   "head": [
    "Problem",
    "Detail"
   ],
   "rows": [
    [
     "Privacy",
     "The responder learns which subject you are verifying, when, and from where"
    ],
    [
     "Latency",
     "A network round trip on the verification path, at the worst possible moment"
    ],
    [
     "Availability",
     "An unreachable responder blocks verification, so implementations fail open"
    ]
   ]
  },
  {
   "type": "p",
   "html": "The third is the one that matters most. A check that is skipped when the responder is unavailable is a check an attacker can disable by making the responder unavailable."
  },
  {
   "type": "h2",
   "text": "What a signed list does instead"
  },
  {
   "type": "p",
   "html": "Publish the revoked set, signed, on a schedule. Verifiers fetch it periodically and check locally."
  },
  {
   "type": "code",
   "text": "{\n  \"issued\": \"2026-03-09T12:00:00Z\",\n  \"next_update\": \"2026-03-09T12:05:00Z\",\n  \"revoked_keys\": [\n    { \"kid\": \"2025-11-c\", \"effective\": \"2026-02-14T08:22:00Z\",\n      \"reason\": \"key_compromise\" }\n  ],\n  \"revoked_nonces\": [\n    \"n_88213aa1\", \"n_4471bf02\", \"n_9c1f8a3e\"\n  ],\n  \"signature\": \"...\"\n}"
  },
  {
   "type": "p",
   "html": "Two categories, and both matter. Revoking a key invalidates a class of signatures from a point in time. Revoking a nonce invalidates one specific authorisation — which is what you need when an individual approval was obtained fraudulently."
  },
  {
   "type": "h2",
   "text": "The staleness objection, addressed"
  },
  {
   "type": "p",
   "html": "A list is as fresh as its publication interval. The usual complaint is that this is worse than asking in real time."
  },
  {
   "type": "p",
   "html": "In practice online responses are also cached, often for hours, so the freshness advantage is smaller than it appears. And the interval is a choice: a list published every minute is a minute stale, which for revocation of a specific authorisation is entirely adequate."
  },
  {
   "type": "table",
   "head": [
    "Interval",
    "Suitable for"
   ],
   "rows": [
    [
     "Every few seconds",
     "Halting an in-flight automated process"
    ],
    [
     "Every minute",
     "Revoking a specific authorisation"
    ],
    [
     "Every hour",
     "Key revocation, which is rare"
    ],
    [
     "Daily",
     "Not adequate for anything that matters"
    ]
   ]
  },
  {
   "type": "h2",
   "text": "Keeping the list small"
  },
  {
   "type": "p",
   "html": "The obvious concern with a full list is that it grows without bound. Three techniques keep it bounded."
  },
  {
   "type": "ol",
   "items": [
    "<strong style=\"font-weight:600\">Expire entries.</strong> A nonce for a statement that has expired can leave the list — the statement is unusable anyway.",
    "<strong style=\"font-weight:600\">Use effective times for keys.</strong> One entry with a timestamp covers every signature from that key after that moment, rather than listing signatures.",
    "<strong style=\"font-weight:600\">Partition by scope.</strong> A verifier for one service does not need revocations for another, so publish per-scope lists."
   ]
  },
  {
   "type": "p",
   "html": "With those, the steady-state list for a typical deployment is small enough to fetch frequently without concern."
  },
  {
   "type": "h2",
   "text": "The stapling middle ground"
  },
  {
   "type": "p",
   "html": "A third option worth knowing: the party presenting the receipt includes a recent signed status assertion alongside it."
  },
  {
   "type": "p",
   "html": "This removes the verifier's network call and the privacy leak, and shifts the freshness burden to the presenter. It is a good fit where receipts are presented rather than looked up, and it adds a moving part."
  },
  {
   "type": "h2",
   "text": "Comparing honestly"
  },
  {
   "type": "table",
   "head": [
    "Property",
    "Online status check",
    "Signed list",
    "Stapled assertion"
   ],
   "rows": [
    [
     "Verifier network call",
     "Yes",
     "Only to refresh",
     "No"
    ],
    [
     "Privacy leak to responder",
     "Yes",
     "No",
     "No"
    ],
    [
     "Freshness",
     "Real time, if uncached",
     "Publication interval",
     "As stapled"
    ],
    [
     "Behaviour when unreachable",
     "Fails open in practice",
     "Cached list still works",
     "Presenter cannot staple"
    ],
    [
     "Implementation complexity",
     "Low",
     "Low",
     "Moderate"
    ]
   ]
  },
  {
   "type": "p",
   "html": "The signed list wins on the properties that determine whether the control actually operates, and gives up real-time freshness that online checks mostly do not deliver either."
  },
  {
   "type": "h2",
   "text": "Where to check"
  },
  {
   "type": "p",
   "html": "At the point of effect, not at a gateway. A revocation checked at an API gateway does not stop work already past the gateway: queued jobs, retries, in-flight requests."
  },
  {
   "type": "p",
   "html": "And fail closed on unknown. If a receipt references a key id that is not in your cached key set, that is not an unknown-but-probably-fine situation; it is a refusal."
  },
  {
   "type": "h2",
   "text": "Two kinds of revocation, both needed"
  },
  {
   "type": "table",
   "caption": "What each entry invalidates",
   "head": [
    "Entry",
    "Scope"
   ],
   "rows": [
    [
     "Revoked key, with an effective time",
     "Every signature from that key after that moment"
    ],
    [
     "<strong style=\"font-weight:600\">Revoked nonce</strong>",
     "<strong style=\"font-weight:600\">One specific authorisation</strong>"
    ]
   ]
  },
  {
   "type": "p",
   "html": "The second is what you need when an individual approval was obtained fraudulently and the credential itself is fine. Key-level revocation is too blunt for that case and is what most systems offer."
  },
  {
   "type": "h2",
   "text": "Objections and honest limits"
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“A list is staler than a real-time check.”</strong> Online responses are typically cached for hours, so the freshness advantage is smaller than it appears. A list published every minute is frequently fresher than what the callback actually delivers."
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“The list will grow without bound.”</strong> Expire entries whose statements have expired, use effective times for keys rather than listing signatures, and partition by scope. Steady-state size is small."
  }
 ],
 "faq": [
  {
   "q": "Why do online status checks fail open?",
   "a": "Because an unreachable responder would otherwise block all verification. That makes the check optional whenever the responder can be made unavailable."
  },
  {
   "q": "Isn't a list staler than a real-time check?",
   "a": "Online responses are typically cached for hours anyway. A list published every minute is adequate for revoking a specific authorisation."
  },
  {
   "q": "How do you keep the list small?",
   "a": "Expire entries for statements that have expired, use effective timestamps for keys rather than listing signatures, and partition by scope."
  },
  {
   "q": "Where should the check happen?",
   "a": "At the point of effect. A gateway check misses queued work, retries and in-flight requests already past it."
  },
  {
   "q": "Why do online checks fail open?",
   "a": "Because blocking all verification when a responder is down is unacceptable, which makes the check optional whenever the responder can be made unavailable."
  },
  {
   "q": "Is a published list too stale?",
   "a": "Online responses are cached for hours anyway. A list published every minute is adequate for revoking a specific authorisation."
  },
  {
   "q": "Why revoke a nonce rather than a key?",
   "a": "Because when one approval was obtained fraudulently the credential is fine. Key revocation is too blunt for that case."
  }
 ],
 "sources": [
  {
   "t": "RFC 6960 — Online Certificate Status Protocol",
   "u": "https://www.rfc-editor.org/rfc/rfc6960"
  },
  {
   "t": "RFC 5280 — Internet X.509 PKI certificate and CRL profile",
   "u": "https://www.rfc-editor.org/rfc/rfc5280"
  },
  {
   "t": "CA/Browser Forum baseline requirements",
   "u": "https://cabforum.org/working-groups/server/baseline-requirements/documents/"
  }
 ],
 "related": [
  {
   "slug": "fail-closed-cryptographic-key-management-implementing-dual-key",
   "title": "Rotating signing keys without breaking verification",
   "category": "Compliance"
  },
  {
   "slug": "building-agent-kill-switch-actually-works-cryptographic-nonce",
   "title": "Building an agent kill-switch",
   "category": "Comparison"
  },
  {
   "slug": "building-manav-verify-write-open-source-offline-verifier",
   "title": "Writing an offline verifier with no dependencies",
   "category": "Compliance"
  }
 ],
 "image": "https://cdn.twc.sh/images/igcache/Revocation%20Without%20Network%20Call/1200_630/blog.jpg",
 "wordcount": 884,
 "url": "/blog/signed-revocation-lists-vs-ocsp-building-fast-offline.html",
 "reading_time": "4 min read",
 "meta_description": "Online status checking leaks what you are verifying, adds latency, and fails open when the responder is down.",
 "hub": {
  "slug": "topics/receipt-cryptography",
  "title": "Receipt cryptography and standards"
 },
 "answer": "Because an unreachable responder cannot be allowed to block all verification. The certificate ecosystem learned this over two decades: a check that is skipped when the responder is unavailable is a check an attacker can disable by making the responder unavailable.",
 "answer_q": "Why does online revocation checking fail open?",
 "glossary": [
  {
   "term": "Soft-fail",
   "def": "Proceeding when a revocation check cannot be completed — the near-universal behaviour, and the reason the check is optional in practice."
  },
  {
   "term": "Effective time",
   "def": "The moment from which a key revocation applies, so earlier signatures remain valid."
  },
  {
   "term": "Stapling",
   "def": "The presenter attaching a recent signed status assertion, removing the verifier's network call."
  }
 ],
 "checklist": {
  "title": "Implementing revocation well",
  "id": "revocation",
  "desc": "Four steps.",
  "steps": [
   {
    "name": "Publish a signed, cacheable list.",
    "text": "Per scope, on a short interval."
   },
   {
    "name": "Support both key and nonce revocation.",
    "text": "Blunt and precise."
   },
   {
    "name": "Check at the point of effect.",
    "text": "A gateway check misses queued work and retries."
   },
   {
    "name": "Fail closed on unknown key ids.",
    "text": "An unrecognised key is a refusal, not a maybe."
   }
  ]
 },
 "cta": {
  "title": "Where this fits in Manav",
  "html": "Manav's receipt is deliberately small: RFC 8785 canonicalisation, one signature algorithm, keys published at a well-known URL, and a verifier short enough to audit or reimplement.",
  "href": "../architecture.html",
  "label": "Read the architecture"
 }
}