You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
External-audit pack (docs/audit/) — audit-readiness groundwork for the
funded external review: a code-cited threat-model annex
(per-suite and per-parser attack-surface tables, the fail-closed invariants
catalog I1–I15, and a residual-risk register R1–R8), an assurance report
(property-based fuzz coverage, the harden-next gap map, and the
adversarial-review history), and a reviewer index
with the suggested review scope and EU funding routes. No code change — the
external audit itself stays gated on funding.
(#75)
Security
Fail closed on hostile deeply-nested JSON across the verify pipeline. A
deeply-nested (but valid) JSON credential — an SD-JWT header/payload/disclosure, a chain of SD-JWT disclosures each carrying the next _sd digest, a VC-JWT
header/payload, or an enveloped VC's data: payload — made json.loads (or the
SD-JWT _unpack recursion) raise RecursionError (a RuntimeError, not an OpenvcError). On the untrusted peek/unwrap path that escaped verify_many's
per-credential isolation and aborted the whole batch (denial-of-service; not a
wrong-accept) — reachable unauthenticated and, on CPython 3.10–3.13, with ~1 KB of
input. SD-JWT _unpack now caps recursion at depth 100 (parity with cbor=64 / _jcs=100), the did:webvh genesis SCID walk (_deep_replace_scid) is depth-bounded
too, and every attacker-facing json.loads — SD-JWT, VC-JWT peek/verify, _jws,
the enveloped unwrap, jwe, and the did:jwk / did:webvh / fetch / status-resolver
paths — now maps RecursionError to a typed error, so hostile input fails closed and verify_many isolates it across all formats. Resolves the R1 residual risk from
the audit pack. (#117)