Skip to content
BitExactverifier
Apache-2.0 · open source · offline · no license needed

Verify the evidence yourself.

bitexact-verifier is one open-source Python file that re-derives every hash and signature in a BitExact bundle from first principles. It imports nothing from the BitExact product, needs no license key and makes no network call, so an auditor, a regulator or a counterparty can verify a bundle offline, on an air-gapped machine if they choose.

  1. Get the file from GitHub or PyPI.
  2. Confirm its digest through your own channel.
  3. Run it on the bundle you were sent.

Sent here by a vendor? This is the tool the people checking your record run. It answers them from the evidence itself, so they don't have to take your word for it.

two bundles · two verdicts
$ python bitexact_verifier.py golden-run.bundle.json --trust-file trust.json --expect-witness witness.example=bcd33f91b6ed…
OK: golden-run - 4 steps (observed 4 / asserted 0 / synthetic 0), chain verified, sealed, signed, recorder-attested, in transparency log bitexact.log/eb0180bca503ff76 (leaf 3 of 66), witnessed by 1 of 1 pinned (1 with ML-DSA-44, post-quantum), signatures: Ed25519 + ML-DSA-65 (post-quantum), post-quantum key pinned
standing when recorded: ok (stated by the producer, not proof)
captured via sdk-python 3, unlabeled 1 (attribution, not proof)
$ echo $?
0

$ python bitexact_verifier.py golden-run.tampered.bundle.json --trust-file trust.json --expect-witness witness.example=bcd33f91b6ed…
FAIL: step 1: data hash mismatch at path result.temp - value tampered or corrupted
$ echo $?
2

The same tool confirms honest evidence and refuses altered evidence, naming the step and the field. The second bundle is the first with one recorded value changed. Every line is the verifier's own output, run on its published test vectors when this page was built; long keys are shortened for display.

Why a separate tool

Your root of trust shouldn't be the party whose evidence you're auditing.

A vendor signature on a verifier would only move the question to "do I trust their key?" So the verifier asks for no trust in BitExact at run time: it imports nothing from the product, and it is one Python file you can read end to end. The trusted base beyond it is CPython, cryptography/OpenSSL and asn1crypto. The same bytes give the same verdict on anyone's machine.

Vendor-independent at run time

No BitExact import, no network, no license. Runs on an air-gapped machine.

Readable

A single file. Read it, then confirm its checksum through your own channel.

Reimplementable

The format is specified and its conformance vectors are public, so you can check a bundle with a verifier of your own. Hashes are over aer-jcs canonical JSON, reproducible in any language.

Exit-coded

0 verifies, 2 fails, printing the exact step or check that broke, and 3 verifies a run that declares capture gaps: intact, but not complete. Drop it in CI; add --require-sealed to fail an unfinished run too.

a declared gap · exit 3
$ python bitexact_verifier.py golden-gap.bundle.json --trust-file trust.json --expect-witness witness.example=bcd33f91b6ed…
OK: golden-gap - 4 steps (observed 3 / asserted 1 / synthetic 0), chain verified, sealed, signed, recorder-attested, in transparency log bitexact.log/eb0180bca503ff76 (leaf 62 of 66), witnessed by 1 of 1 pinned (1 with ML-DSA-44, post-quantum), signatures: Ed25519 + ML-DSA-65 (post-quantum), post-quantum key pinned, 1 declared capture gap - the capture is incomplete; each gap names its cause
standing when recorded: ok (stated by the producer, not proof)
captured via sdk-python 3, unlabeled 1 (attribution, not proof)
$ echo $?
3

An intact record of an incomplete capture is not a complete record: exit 3, unless --allow-gaps accepts it. This run's recorder could not write one tool argument as JSON, withheld it and said so on the chain.

What it checks

Every claim a bundle makes, re-derived from first principles.

Hash chain

Every entry's hash over its canonical body, linked to its predecessor from genesis to the recorded head (BLAKE2b-256, or SHA-256 for FIPS-mode bundles).

Field commitments

Each committed field's salted commitment — so a redacted field still verifies while any tampered value fails, naming the exact path.

Redaction integrity

A bundle that claims to have redacted a field must actually have removed it; a false redaction claim fails.

Seal semantics

A sealed run's terminal marker must be final and attest the correct step count — so a complete run is distinguishable from a truncated one.

External anchors

A WORM anchor's recorder signature and an RFC 3161 timestamp token — CMS signature, imprint, timeStamping certificate — each bound to the head. --expect-tsa pins the authority.

Signatures & checkpoints

Signed hash-linked checkpoints and the detached bundle signature, including DSSE envelopes wrapping in-toto statements, each hybrid Ed25519 + ML-DSA (FIPS 204): the verdict names the suite it checked, and --expect-pq-key pins the post-quantum half.

Canonicalization

aer-jcs: RFC 8785 restricted to values that canonicalize identically everywhere. Values that wouldn't are refused at record time, not committed.

Reading view

A pack's data/view.html is rendered again from the bag's own bundles and must match byte for byte; a page that differs fails the pack, one that matches ends the OK line with view matches the bundles. The page is a reading aid, never a verdict, with no script and no external reference. A line after the verdict names every other file it does not check: not checked against the runs (the producer's own statements, sealed against later change only) - the workpaper, trust index and runbook.

Confirm it's genuine

Verify the copy you hold before you trust it with anything.

The math is what you trust, and you can read all of it.

Confirm the verifier's checksum against the canonical digest for the version it claims, obtained through a channel independent of wherever you got the file, and never only from the party whose evidence you audit.

$ python bitexact_verifier.py --version
bitexact-verifier 0.0.1a0
format aer-bundle, canonicalization aer-jcs, MAX_DEPTH 128, SAFE_INTEGER 9007199254740991
$ sha256sum bitexact_verifier.py  # compare to CHECKSUMS.txt at the verifier-v<version> tag

Be clear about who publishes what. BitExact publishes the verifier, on GitHub under a verifier-v<version> tag and on PyPI, so both channels are ours. If you need a digest from outside our control, get it from the producer through a separate contractual channel and record that you did. The auditor procedure walks through it.

testdata/ holds the golden conformance fixtures and the aer-jcs vector corpus, accepted bytes and refusals, that any second implementation can validate against. The transcripts on this page are run on them.

Install & use

Python 3.15+. One command.

# install, or run the single file directly
$ pip install cryptography asn1crypto && python bitexact_verifier.py bundle.json

# require a specific signing key: its Ed25519 half (hex) and its ML-DSA half (hex or a file)
$ bitexact-verifier run-42.bundle.json --expect-key <64-hex> --expect-pq-key <hex-or-file>

# a trust file of accepted bundle and recorder keys, both halves
$ bitexact-verifier run-42.bundle.json --trust-file trust.json

# streaming JSONL bundles and DSSE / in-toto envelopes verify the same way
$ bitexact-verifier run-42.bundle.jsonl
$ bitexact-verifier run-42.dsse.json