Skip to main content

evidencevault-verify

Offline, dependency-light verification of EvidenceVault certificates — for third parties, auditors, and courts. pip install evidencevault-verify gives you a CLI and a Python library that verify a certificate's cryptographic and structural integrity using only its public artifacts, with zero network access.

This is a faithful Python port of the EvidenceVault TypeScript verifier (tools/cert-verify + packages/verify-core + packages/shared). It produces byte-identical canonicalization and hashes and the same pass/fail verdicts as the reference cert-verify tool — proven by a cross-validation test suite that diffs the two verifiers against real deployed certificates.

What it verifies

For each certificate, exactly the checks the TypeScript verifier reports:

Check Meaning
identityMatches The payload's cert_id / issuing_tenant_id match what you asked for
canonicalHashMatches RFC 8785 JCS canonicalization of the payload → SHA-256 → unpadded base64url equals signatures.canonical_payload_sha256
ed25519Valid Ed25519 signature over the canonical payload bytes (key resolved through the tenant registry, incl. time-bounded agent subkey certificates)
mlDsa65Valid ML-DSA-65 (FIPS 204) post-quantum co-signature
slhDsaSha2128fValid SLH-DSA-SHA2-128f (FIPS 205) post-quantum co-signature
merkleInclusionValid RFC 6962 inclusion proof (0x00 leaf / 0x01 node) binds the claimed leaf to source_evidence_merkle_rootnull when no proof is present
withinValidity issued_at <= now < expires_at
dualSignatureValid Licensee co-signature for dual-signed classes: licensee credential present, platform-certified, verified and in-window — null when neither present nor required, fail-closed when required but missing
predicatesValid Every declared predicate certificate exists, has status issued, and is within validity — fail-closed when predicates are declared but unresolved
otsAnchorPresent / otsProofFound An OpenTimestamps .ots proof is present and non-empty (Bitcoin confirmation is delegated to the ots CLI via --ots-cli)

A certificate is ok only when all mandatory checks pass and no optional check is false — the identical conjunction the TypeScript verifier uses.

Both post-quantum families are required (a break in one family cannot forge a certificate on its own), and both signatures were produced by @noble/post-quantum (FIPS 204 / FIPS 205 final). See Post-quantum interoperability below.

Install

pip install evidencevault-verify

Requires Python 3.10+. Dependencies (all pure-Python or pip-wheel, no native OQS build): cryptography (Ed25519), dilithium-py (ML-DSA-65), slh-dsa (SLH-DSA-SHA2-128f).

CLI

# Verify from a directory of public artifacts
evidencevault-verify --cert-id cert_01KY7HNX7T89K8HNNC17PBK3YD \
                     --tenant-id tnt_demo \
                     --dir ./bundle

# Verify from a self-contained bundle (base64-embedded files)
evidencevault-verify --cert-id <id> --tenant-id <tnt> --bundle ./bundle.json

# Machine-readable output
evidencevault-verify --cert-id <id> --tenant-id <tnt> --dir ./bundle --json

# Also run Bitcoin-tier OpenTimestamps confirmation (requires `ots` on PATH)
evidencevault-verify --cert-id <id> --tenant-id <tnt> --dir ./bundle --ots-cli

Output (byte-identical to the reference cert-verify):

✓ cert_01KY7HNX7T89K8HNNC17PBK3YD — {"tenantRegistryFound":true,...,"otsProofFound":true}

Exit code 0 when the certificate is valid, 1 when it is not, 2 on a usage error.

Library API

from evidencevault_verify import verify_certificate, DirSource

result = verify_certificate(DirSource("./bundle"), "cert_01KY7HNX...", "tnt_demo")

print(result.ok)                       # overall verdict
print(result.checks.ed25519_valid)     # individual checks (snake_case attributes)
print(result.checks.ml_dsa65_valid)
print(result.checks.slh_dsa_sha2_128f_valid)
print(result.checks.to_ts_dict())      # exact camelCase check map the CLI prints
for err in result.errors:
    print(err)

Convenience wrappers: verify_certificate_from_dir(base_dir, cert_id, tenant_id) and verify_certificate_from_bundle(bundle_path, cert_id, tenant_id). Pass now=<datetime> to any of them to verify against a fixed time instead of the current clock.

verify_certificate accepts any object with a read(relative_path) -> bytes | None method (the ArtifactSource protocol), so you can back it with a directory (DirSource), an in-memory bundle (BundleSource), or your own storage.

Artifact layout

Identical to the reference cert-verify. In --dir mode the base directory contains:

public/tenants/<tenant_id>.json              # tenant public key registry (trust root)
public/certificates/<cert_id>.json           # existence records (for predicate resolution)
certificates/<cert_id>/payload.json          # the signed certificate payload
certificates/<cert_id>/signatures.json       # Ed25519 + hybrid PQC signature block
certificates/<cert_id>/merkle-proof.json     # optional RFC 6962 inclusion proof
certificates/<cert_id>/cert.ots              # optional OpenTimestamps proof

In --bundle mode a single JSON file carries every file above as base64-encoded bytes. Both modes contact no network and read no EvidenceVault infrastructure.

Post-quantum interoperability

The load-bearing risk in a Python port is whether a Python PQC library can verify signatures produced by @noble/post-quantum. It can — proven against the real signatures on the shipped campaign certificates:

  • ML-DSA-65dilithium-py (pure-Python FIPS 204). ML_DSA_65.verify(pubkey, msg, sig) accepts @noble's signature and rejects a tampered message.
  • SLH-DSA-SHA2-128fslh-dsa (pure-Python FIPS 205). Verified with the pure variant and an empty context string (PublicKey.from_digest(pubkey, slhdsa.sha2_128f).verify_pure(msg, sig)), which is what @noble's slh_dsa_sha2_128f.sign produces. Note: the pre-hashed verify variant does not accept these signatures — pure is required.

Both libraries are pure-Python (no WASM, no native liboqs build), which keeps the verifier portable and easy to audit. The interop is asserted for every campaign certificate in tests/test_pqc_interop.py.

Note on quantcrypt and liboqs: quantcrypt was evaluated first but on some platforms it has no pre-built wheel and its source build JIT-downloads PQClean from GitHub at runtime — unsuitable for an offline court tool. liboqs/oqs requires a CMake/C build of liboqs. The pure-Python dilithium-py + slh-dsa pair verifies the same @noble signatures with none of that friction, so it is the chosen dependency set.

Cross-validation (the correctness proof)

tests/test_cross_validation.py runs the Python verifier and the reference TypeScript cert-verify against the same real certificates and asserts their check maps are equal, field-for-field. Where a Node toolchain is present the TS verifier is run live and diffed; the pure-Python assertions (all-green verdict, canonical-hash parity, PQC interop, fail-closed tampering) run unconditionally.

pip install -e ".[test]"
pytest

For the operator: publishing to PyPI

The package is ready to publish. This repository does not publish it (that needs the operator's PyPI credentials). To publish:

cd tools/evidencevault-verify-py
python -m build            # produces dist/*.tar.gz and dist/*.whl
twine upload dist/*        # requires your PyPI API token

Run twine check dist/* first to validate the metadata.

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

evidencevault_verify-1.0.0.tar.gz (26.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

evidencevault_verify-1.0.0-py3-none-any.whl (23.5 kB view details)

Uploaded Python 3

File details

Details for the file evidencevault_verify-1.0.0.tar.gz.

File metadata

  • Download URL: evidencevault_verify-1.0.0.tar.gz
  • Upload date:
  • Size: 26.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for evidencevault_verify-1.0.0.tar.gz
Algorithm Hash digest
SHA256 0ed89b468d0c433868cb627f46dfb31705b0878896ff37da73868c6ec86983e6
MD5 96cc92657d1f2af76b79cb6f3fb7d9f4
BLAKE2b-256 025515cf71bdbbe5b737f7c682bb94285f5cf90b4be1bb3e03dbbc4eb73d73cf

See more details on using hashes here.

File details

Details for the file evidencevault_verify-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for evidencevault_verify-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7639eed2222fe758d963e78545cbbd557c07eb846387430b96c53f0ba93eab0f
MD5 b7aed67b05523898f2a7244a98450446
BLAKE2b-256 291718a930ed12f85e85dcc64b3a99aecdcbe2c4fdd32f6d535f002e2cfceaca

See more details on using hashes here.

Release history Release notifications | RSS feed

2.0.1

2 files

2.0.0

2 files

This release

1.0.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page