Verifying a BLACK BOX report independently
This page exists so a court, an advocate, or an opposing expert can verify a BLACK BOX report without BLACK BOX, without an account, and without the verifier page ever having existed. The verifier is a convenience. This specification is the trust.
0. What the signature claims — and what it does not
The signature attests that:
- the evidence section of the document is byte-for-byte what BLACK BOX signed, at the stated time, for the stated event;
- the per-chunk plaintext commitments shown in the document are the ones BLACK BOX recorded at the moment of capture — the server re-derives them from its own records when it signs, so they cannot be substituted afterwards;
- the recording, when decrypted on the survivor's own device, hashed to exactly those commitments.
The signature does not claim:
- that any particular thing happened, or that a recording depicts anything in particular. BLACK BOX does not interpret, transcribe, or assess a recording;
- that the survivor statement is true or checked. It is deliberately outside the signature (see §4);
- that the file was not copied or shown selectively. Tamper-evidence is not tamper-prevention: anyone holding a file can alter any byte. The point is that altering the evidence section is detectable.
1. The document
A report is a single self-contained HTML file with three parts:
| Part | Marker | Signed |
|---|---|---|
| Visible evidence text | <pre id="blackbox-evidence-text">…</pre> | yes (as renderedHash) |
| Machine-readable payload | <script type="application/json" id="blackbox-attestation">…</script> | yes (as evidenceHash) |
| Survivor statement | <div id="blackbox-statement"><pre>…</pre></div> | no — deliberately |
The payload is a JSON object:
{
"evidence": { … the evidence zone … },
"attestation": { … the signed object … },
"signature": "base64 ECDSA P-256 signature (raw r‖s)",
"publicKey": "base64 SPKI public key"
}
Inside the <script> block every < is escaped as the six
characters < so the JSON cannot terminate the element; un-escape before
parsing. In the visible blocks, &, < and >
are HTML-escaped as &, <, > —
reverse in that order, with & last.
2. Canonical JSON
Both hashes are taken over a canonical serialization — a small, deliberately boring subset of RFC 8785 (JCS), short enough to re-implement in any language:
- Object keys sorted ascending by Unicode code point of the raw key string.
- Members whose value is
undefinedare omitted;nullis kept. - Array order is preserved — order carries meaning (chunk sequence, notification order).
- No insignificant whitespace: no space after
:or,, no newlines. - Strings, numbers and booleans use standard JSON encoding (RFC 8259).
- Non-finite numbers are invalid and never appear.
3. The verification procedure
Step 1 — extract
Take the text between the attestation markers, replace < with
<, and JSON.parse it. Take the text between the evidence
markers and HTML-unescape it. If either marker is absent the file is not a BLACK BOX
report — a different outcome from TAMPERED.
Step 2 — check the key is the published one
payload.publicKey against the published BLACK BOX key below.
Published key (SPKI, base64):
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXqmajlR2iQOxIrPfF4AE8kTQdfofQIlLWaZn39L2mxyq2mwbcCb32QvqrNzQsTVHnBR1l3j9+0c/7nmbydR+GQ==
Also downloadable as PEM or JWK.
Step 3 — check the evidence data
sha256_hex( utf8( canonicalize( payload.evidence ) ) ) == attestation.evidenceHash
Step 4 — check the visible text
Collapse every run of whitespace in the evidence text to a single space, trim both ends, then:
sha256_hex( utf8( normalized ) ) == attestation.renderedHash
Whitespace is normalized so that reflowing, re-saving or printing a document is not mistaken for tampering. Any change that survives normalization is a real change to what the document asserts.
Step 5 — check the text matches the data
The visible text is a deterministic rendering of payload.evidence. Re-render it,
normalize as in step 4, and confirm it equals the document's own evidence text. This catches
a document that displays one thing while carrying another. If you are not
re-implementing the renderer, rely on steps 3 and 4 together — and read the visible evidence
text, since that is what a reader sees.
Step 6 — check the signature
ECDSA_P256_SHA256_verify(
key = payload.publicKey (SPKI, base64)
message = utf8( canonicalize( payload.attestation ) )
signature = payload.signature (base64, raw r‖s)
)
The signed message is the canonical attestation — not the document bytes, and not the JSON as it happens to appear in the file.
openssl pkeyutl. This is the one real gotcha in the format.
# raw r||s (64 bytes) -> DER, then verify
python3 - <<'PY'
import base64
sig = base64.b64decode(open('sig.b64').read().strip())
r, s = int.from_bytes(sig[:32],'big'), int.from_bytes(sig[32:],'big')
def enc(x):
b = x.to_bytes((x.bit_length()+8)//8 or 1, 'big')
return b'\x02' + bytes([len(b)]) + b
body = enc(r) + enc(s)
open('sig.der','wb').write(b'\x30' + bytes([len(body)]) + body)
PY
printf '%s' "$CANONICAL_ATTESTATION" > msg.bin
openssl dgst -sha256 -verify blackbox-report-public-key.pem -signature sig.der msg.bin
Verdicts
| Condition | Verdict |
|---|---|
| Markers absent / payload unparseable | Not a BLACK BOX report |
| Steps 2–6 all pass | CERTIFIED — evidence verified, unaltered since generation |
| Any of steps 2–6 fails | TAMPERED — evidence altered; not certified |
| Your runtime cannot perform the check | Could not verify — never report this as certified |
That last row matters. A verifier that cannot distinguish "unaltered" from "I could not check" is worse than no verifier, because it prints a reassuring answer on faith.
4. The statement is not signed, and that is deliberate
The statement zone is excluded from every hash above. Two consequences, both intended:
- The survivor may revise her account at any time — expand it, correct it, write it months later — and the certification of the evidence remains valid. Her evidence is not held hostage to the first words she managed to write.
- A court is told plainly that the statement is her account, not machine-verified. BLACK BOX does not vouch for it, and equally does not grade, score, or challenge it.
Editing the statement must never be reported as tampering. A verifier that flags an edited statement is incorrect.
5. The attestation object
{
"format": "blackbox-certified-report/v1",
"alg": "ECDSA-P256-SHA256",
"eventId": "…",
"evidenceHash": "sha256 hex — computed on the survivor's device",
"renderedHash": "sha256 hex — computed on the survivor's device",
"commitmentsHash": "sha256 hex — recomputed SERVER-side from its own records",
"chainHead": "integrity-chain head at signing, or null",
"chainSeq": 41,
"signedAt": "UTC ISO-8601"
}
commitmentsHash is the canonical hash of the ordered commitment list, derived by
the server from its own records — not from anything the device supplied. That is what
makes the certification non-circular: without it, the signature would attest only that the
device said what the device said.
6. Zero knowledge
BLACK BOX signs two SHA-256 hashes and an event id. It does not receive the evidence, the recording, or the statement, and holds no key that can decrypt a recording — those are decrypted on the survivor's own device with her own key. A BLACK BOX signature is therefore a signature over a fingerprint the signer cannot invert.