Fail-Closed Bitstring Status Lists v1.0
4. Fail-Closed Status Lists
Section titled “4. Fail-Closed Status Lists”4.1 Status List Architecture
Section titled “4.1 Status List Architecture”Credential revocation and suspension are evaluated using W3C Bitstring Status List v1.0 (BitstringStatusListEntry) or legacy StatusList2021 (StatusList2021Entry).
Credential Subject Status Pointer: { "type": "BitstringStatusListEntry", "statusPurpose": "revocation", "statusListIndex": "131075", "statusListCredential": "https://issuer.example/status/revocation-2026.json" } │ ▼ (Hardened HTTPS fetch through SafeHttpClient)Status List Credential: ├── Verify Credential Proof (eddsa-jcs-2022 or VC-JOSE) ├── Validate Issuer (MUST equal credential issuer) ├── Decompress Gzip Bitstring (capped at 16 MiB) ├── Check Minimum Size (MUST be ≥ 131,072 bits) └── Read Bit at index 131075: Bit = 0 ──► ACTIVE (Valid) Bit = 1 ──► SET (Revoked / Suspended)4.2 Structural & Decompression Bounds
Section titled “4.2 Structural & Decompression Bounds”- Wire Transfer Limit:
The HTTP request for the status list credential enforces
MAX_STATUS_LIST_BYTES = 1_048_576(1 MiB). - Decompression Bomb Defense:
Bitstrings are GZIP-compressed and base64url encoded. Decompression via
flate2::read::GzDecoderenforces a strict reading ceiling:If decompressed output exceeds 16 MiB, processing aborts immediately with a decompression bomb error.const MAX_DECOMPRESSED_BITS_BYTES: u64 = 16 * 1024 * 1024; // 16 MiBGzDecoder::new(compressed.as_slice()).take(MAX_DECOMPRESSED_BITS_BYTES + 1).read_to_end(&mut out) - Minimum Bitstring Length:
To prevent privacy reduction via micro-lists, a decompressed bitstring MUST contain at least 16 KiB ($16,384 \times 8 = 131,072$ bits):
if bits.len() < 16 * 1024 {ev.detail = "status list contains fewer than 131072 single-bit entries (STATUS_LIST_LENGTH_ERROR); fail-closed".into();return ev;}
- Encoding Constraints:
- For
BitstringStatusListEntry,encodedListMUST use multibase base64url encoding (prefix'u'). statusSizeMUST equal 1 (single-bit semantics). Multi-bit sizes or custom status messages returnUNSUPPORTED.statusListIndexMUST be a non-negative string integer.
- For
4.3 Same-Issuer Authority & Fail-Closed Rules
Section titled “4.3 Same-Issuer Authority & Fail-Closed Rules”- Same-Issuer Authorization Policy: The entity issuing the status list credential MUST match the issuer of the credential being evaluated: $$\text{status_list_vc.issuer} \equiv \text{credential.issuer}$$ Delegated third-party status lists are not permitted and evaluate fail-closed.
- Fail-Closed Invariant:
If any of the following occur, status evaluation yields
INDETERMINATE, preventing validation:- Network failure or timeout while fetching the status list.
- Status list signature verification failure.
- Status list expiration or invalid validity window.
statusListIndexout of bounds ($\text{index} \ge \text{bits.len()} \times 8$).- Mismatched
statusPurpose(e.g. entry declared"revocation", list declares"suspension").