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-2.0.0.tar.gz (28.7 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-2.0.0-py3-none-any.whl (25.7 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for evidencevault_verify-2.0.0.tar.gz
Algorithm Hash digest
SHA256 8169a765c38a47e31459d06eb94629665fd0b41cd7f9e9e822a28d9391c7dcf9
MD5 df3548526d5c76f7ee8ce51a169474aa
BLAKE2b-256 f00e5960e996b7c119c441ccdba23cfd4aef43ace3b72932931757880ed22f3c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for evidencevault_verify-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 007a6e6aeaed0b2de557a34b139e2eeaf11fda34abd9829e9422df7938125bb5
MD5 2518ca92662761fa75c3ede24e6d2993
BLAKE2b-256 a32d530645225603b15de1326841e79465eea15c217c49d291c10699d1846b2e

See more details on using hashes here.

Release history Release notifications | RSS feed

2.0.1

2 files

This release

2.0.0 This release

2 files

1.0.0

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