Verify an AICVS certificate without AICVS.

One file. Python 3.8+. No network, no account, no vendor — not even a pip install. Download it, read it, keep it. It will still work if this company does not.

MIT LICENCE STANDARD LIBRARY ONLY OFFLINE ~490 LINES

Why this exists

A compliance certificate whose only proof is “the vendor's server says it's valid” is worth exactly as much as the vendor's continued existence. That is a poor property for evidence you may need to produce years from now, in front of a regulator, possibly after the company that issued it has gone.

So the check does not require us. Copy this file into your own repository, audit it, run it in ten years. It is short enough to read in full before trusting it, which is the point — we would rather you did not have to take our word for anything.

SHA-256 of aicvs_verify.py — 12bd42e6a6fa5062ba3638cad691202f65ddbd8b28c4dfefc17c77e7cd6a9510
Check it after downloading: python -c "import hashlib;print(hashlib.sha256(open('aicvs_verify.py','rb').read()).hexdigest())"

Using it

Every scan exposes a self-contained bundle, unauthenticated:

$ curl -O https://api.aicvs.io/api/v1/scans/<scan-id>/bundle.json
$ python aicvs_verify.py bundle.json --source your_file.py \
       --public-key <pinned-key> --require-signature

  AICVS certificate verification
  ----------------------------------------------------
  file        decide.py
  cert hash   c4584bfc8a55ddda2cfffc4c47318743...
  chain       AICVS-1.11.0

  [ok  ] source file matches certificate
  [ok  ] step 1_content_ingest ... step 6_cert_digest
  [ok  ] signature (signed_pinned)

  VERIFIED -- chain intact, signature is from AICVS.

Exit codes: 0 verified, 1 verification failed, 2 not a readable bundle — so it drops straight into CI.

The bundle carries the certificate, the evidence chain and the four fields that participate in the fingerprint. It deliberately contains no source code and no finding descriptions: verification should never require publishing the code being verified.

What it proves

1. Integrity

All six chain steps are recomputed from the declared inputs. Change a score, a status, a filename, an article mapping — or delete an inconvenient finding — and a hash stops matching.

2. Binding to real source

With --source, the file's SHA-256 is recomputed and compared to the digest sealed into step 1. This is what connects a document to a specific artefact rather than to a claim about one. One byte's difference fails the check.

3. Origin

Certificates carry an Ed25519 signature over the terminal digest. Checked against a pinned key, that establishes AICVS issued it — something a hash chain alone cannot show.

Pinning is the part that matters. A key that arrives inside the bundle proves only arithmetic — anyone can sign anything with a key they invented. So the verifier reports three states and never conflates them: signed_pinned (origin proven), signed_unpinned (maths only), unsigned (older certificates; integrity still verifies). Fetch the published keys once from /.well-known/aicvs-signing-keys.json and commit them into TRUSTED_KEYS in your copy.

What it does not prove

Stated plainly, because a verifier that overstates its own guarantee is worse than none:

  • Not that the findings are correct. This checks integrity and origin, not the quality of the analysis. A certificate can be intact, genuine, and describe a scan that missed something.
  • Not origin, if the bundle is unsigned or the key is not pinned. The verifier says so in those words rather than printing a tick.
  • Not that our signing key is uncompromised. A signature proves the holder of the private key produced it. Key custody is a separate problem, and no verifier can check it for you.
  • Not legal certification. AICVS produces readiness support based on available records. No tool, this one included, certifies EU AI Act compliance.

The evidence chain, specified

Published in full so a third party can implement their own checker without our cooperation. Six SHA-256 steps, each folding in the last. CV is the chain version (e.g. AICVS-1.11.0), recorded in the bundle so old certificates stay checkable after the format moves on.

#StepInput
1content ingestCV|1_content|{filename}|{sha256(file)}
2identity bindingCV|2_identity|{scan_id}|{h1}
3provenanceCV|3_provenance|{findings_fingerprint}|{h2}
4EU mappingCV|4_eu_mapping|{articles, comma-joined}|{h3}
5merkle sealCV|5_merkle|{h2}|{h4}
6cert digestCV|6_cert|{h5}|{score}|{status}|{filename}

findings_fingerprint is the SHA-256 of the findings reduced to rule_id, line, severity and score_impact, sorted by (rule_id, line) and serialised with sorted keys. Prose is excluded deliberately: reworded copy in a later release must not invalidate a certificate issued today, but a changed rule, line, severity or score must. Filename participates in steps 1 and 6, so identical code in two files yields two distinct certificates.

The signature covers, exactly:

aicvs-cert-v1|{chain_version}|{scan_id}|{cert_hash}

Domain-separated and context-bound on purpose. Without the prefix a signature could be replayed against another structure that happens to be a hex digest; without scan_id and chain_version a valid signature could be lifted onto a different scan sharing a cert hash. The verifier rebuilds this string itself, so if the two implementations ever diverge, verification fails rather than passing on trust.

Independence is a tested property

The verifier shares no code with the issuing backend. A checker that imports the thing it checks proves only that the code agrees with itself. The Ed25519 implementation is vendored from RFC 8032 rather than imported, because pip install cryptography is a bet on a package index still existing and still serving a build for your platform — precisely the assumption this tool exists to avoid.

The cost of that independence is drift, so our test suite runs this exact script as a subprocess against real scan output, and asserts it imports nothing from the backend. Tests cover a genuine certificate passing; source binding failing on a one-byte change; tampering with score, status, filename and findings each being caught; a forged bundle signed with an attacker's own key being rejected against the pinned key; and the vendored Ed25519 checked against the reference implementation rather than against itself.

Licence

MIT. Vendor it, fork it, ship it inside your own audit tooling. Evidence that outlives the vendor is the entire intent — see our continuity commitment for the rest of that answer.