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 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-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.1.tar.gz (42.4 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.1-py3-none-any.whl (34.0 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for evidencevault_verify-2.0.1.tar.gz
Algorithm Hash digest
SHA256 a98cdfca7b4b35e8d1602c5e416cbcf6360bb27d69ffd82e866c282cdcc9ef7f
MD5 29093d03e5f38668f8c19cd81f5e9672
BLAKE2b-256 b3fb6714fc9605b68a343d04a5819df20ef6aa158af86c8ff500493588f99a09

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for evidencevault_verify-2.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5753e51d7f0545f10eda7c1c0e4a2f86e8db6ae27d415cf2713fe8a68fa0a6ea
MD5 cdf8392f9bd9c1f40459e6b021eddd3b
BLAKE2b-256 5d276a544f10d97891f66cb3e90179a68284034abdd65aec8a4ee750862ad9dc

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

2.0.1 This release

2 files

2.0.0

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