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_root — null 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 codes: 0 = valid (cryptographically sound and within the reliance window), 2 = cryptographically sound but expired / not-yet-valid, 1 = cryptographically or structurally invalid. (Argument/usage errors — including a malformed --now/EV_VERIFY_NOW timestamp — also exit 2, per the argparse convention.) The verdict splits cryptographicallyValid (clock-independent) from validity (valid | expired | not-yet-valid); pass --now <RFC3339> or set EV_VERIFY_NOW to override the clock.

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-65 → dilithium-py (pure-Python FIPS 204). ML_DSA_65.verify(pubkey, msg, sig) accepts @noble's signature and rejects a tampered message.
  • SLH-DSA-SHA2-128f → slh-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.

Metadata

Release files for evidencevault-verify 2.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for evidencevault-verify 2.0.1
File Size Uploaded
evidencevault_verify-2.0.1.tar.gz 42.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for evidencevault-verify 2.0.1
File Interpreter ABI Platform
evidencevault_verify-2.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 76.3 kB

Release files / evidencevault_verify-2.0.1.tar.gz

Download URL evidencevault_verify-2.0.1.tar.gz
Size 42.4 kB
Tags Source
SHA-256 checksum
How to use checksums
a98cdfca7b4b35e8d1602c5e416cbcf6360bb27d69ffd82e866c282cdcc9ef7f
BLAKE2b-256 checksum
How to use checksums
b3fb6714fc9605b68a343d04a5819df20ef6aa158af86c8ff500493588f99a09
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Release files / evidencevault_verify-2.0.1-py3-none-any.whl

Download URL evidencevault_verify-2.0.1-py3-none-any.whl
Size 34.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5753e51d7f0545f10eda7c1c0e4a2f86e8db6ae27d415cf2713fe8a68fa0a6ea
BLAKE2b-256 checksum
How to use checksums
5d276a544f10d97891f66cb3e90179a68284034abdd65aec8a4ee750862ad9dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Release history Release notifications | RSS feed

This release

2.0.1 This release

2 release files

2.0.0

2 release files

1.0.0

2 release 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