#!/usr/bin/env python3
"""
verify_proofpack.py - standalone verifier for PriviNet Lumra ProofPacks.

Verifies a ProofPack with NO PriviNet servers, accounts, or trust involved:
  1. Recomputes SHA-256 over the canonical JSON of every hashed object in
     canonical_inputs and compares each to the digests in the proof record.
  2. Recomputes the composite event digest with its domain-separation
     context string and compares it to the stored event_hash.
  3. Verifies the Ed25519 signature over the event_hash with the public
     key embedded in the pack.

Usage:
    python verify_proofpack.py PROOFPACK-xxxxxxxx-YYYY-MM-DD.json

Dependencies: Python 3.9+ and ONE of:
    pip install cryptography      (preferred)
    pip install pynacl            (alternative)
Steps 1 and 2 run with the standard library alone; only step 3 (the
signature) needs a crypto library.

Exit code 0 means every check passed. Any failure exits 1 and says why.
Any change to a sealed value makes this script fail. Hashes are computed over
canonical content (sorted keys, fixed separators), so cosmetic JSON whitespace
is normalized away and is not part of the seal; every meaningful value is.

IMPORTANT, WHAT "VERIFIED" MEANS: this script proves the records in the
pack are internally consistent and were signed by whatever key is inside
the pack. That is integrity, not provenance. To know the pack was signed
by PriviNet (and not by a forger who put their own key in the file), the
signing key must match PriviNet's published key. This script pins the
known PriviNet software signing key below and warns loudly on a mismatch.
Independently confirm the key at https://privinet.net/keys.txt
"""

from __future__ import annotations

import base64
import hashlib
import json
import os
import re
import shutil
import subprocess
import sys
import tempfile
from datetime import datetime, timezone

VERIFIER_VERSION = "1.4.0"

# PriviNet's published signing keys. Ed25519 keys are 32-byte hex; the KMS
# HSM key is an ECDSA P-256 public key as a 65-byte uncompressed point (hex,
# leading 04). Confirm this list out-of-band at https://privinet.net/keys.txt
# before relying on a "signed by PriviNet" conclusion.
PRIVINET_PUBLIC_KEYS = {
    "04bc55de97e806fbeaf95298588a87cf7f1404640f7b9d359fbbc0f1b2bf408f8be837d1cfa641778a11fd9776be566a2c938b8891d29a7182c15500a05b2e6655": "kms-1944107aa98ec035 (production key, held in Google Cloud KMS HSM, non-extractable)",
    "33b8163a6debfb9fcf6664118c7c7a0a9e9ee9d0429ef97343a57f0727b0edd6": "sw-77396abcd4102123 (software key: primary before 2026-07-06, availability fallback after)",
}
# Keys that are no longer PriviNet's primary signing key. A pack they sign is
# still authentically PriviNet's, but the reader deserves to know the primary
# custody story (HSM) does not apply to THIS record.
PRIVINET_NON_PRIMARY_KEYS = {
    "33b8163a6debfb9fcf6664118c7c7a0a9e9ee9d0429ef97343a57f0727b0edd6": (
        "this record is signed by PriviNet's SOFTWARE key, not the HSM key. That is "
        "expected for records issued before 2026-07-06, and for later records only as "
        "an availability fallback (the record labels itself software_key). The "
        "software key is not protected by HSM custody; weigh it accordingly and "
        "confirm key status at privinet.net/keys.txt."
    ),
}
# Demo device keys (source-signing demonstration); recognized so the demo
# edge signature reads as a known device rather than an unknown one.
PRIVINET_DEVICE_KEYS = {
    "5cbf574f7a61a92010178d69ada7a33736a23f0f8e279cd360f67c89cf157242": "metalert-smartsole-002 (demo edge device)",
}
# PriviNet's attestation ROOT keys. Clients on dedicated per-tenant signing
# keys embed a root-signed attestation in each ProofPack; this verifier pins
# ONLY the root, then accepts the tenant key the attestation names. A forger
# cannot mint an attestation (the root is HSM-held), so an unrecognized key
# with no valid attestation still lands on "NOT CONFIRMED", never on green.
# Confirm out-of-band at https://privinet.net/keys.txt
PRIVINET_ATTESTATION_ROOTS = {
    "04f371e86f86234164195fca40b7764ea66ab83e09cf029fe0e112a72e20fb9592ac0f75c35affd69a08b3ed498e272909a1445a68937c502b3c7663693452a651": "kms-6097ab8f15998232 (lumra-attestation-root-1, Google Cloud KMS HSM, non-extractable)",
}
# Domain-separation prefix for attestation signatures: keeps the attestation's
# message space disjoint from record/manifest signatures, so a signature can
# never be replayed across roles.
ATTEST_CONTEXT = "PRIVINET-TENANT-KEY-ATTEST-V1:"


# ---------------------------------------------------------------------------
# RFC 3161 timestamp verification.
#
# The anchor carries a standard RFC 3161 token (DER, base64) over sha256 of the
# ASCII bytes of event_hash. Checking it properly needs two things this script
# cannot assume: an openssl binary, and a trusted root for the timestamp
# authority. When either is missing the answer is INDETERMINATE, never OK.
# "We could not check" is not "we checked and it passed".
#
# Trust material, in order: --tsa-ca <path>, $LUMRA_TSA_CA, or tsa-cacert.pem
# next to this script. For freetsa.org that is its published cacert.pem; for a
# TSA chaining to a public root, your system bundle works.
# ---------------------------------------------------------------------------
TSA_CA_PATH = None  # set from --tsa-ca / $LUMRA_TSA_CA at startup
EU_TRUST_LIST_PATH = None  # set only from --eu-trusted-list for this invocation

# Signing keys the READER supplied for THIS run with --trust-keys. Kept apart from
# PRIVINET_PUBLIC_KEYS on purpose and never merged into it: a package verified only
# against supplied trust is reported as exactly that, never as a PriviNet record.
# There is deliberately no environment variable for this. Trust that silently
# persists in a shell is how test trust leaks into a production check.
SUPPLIED_TRUST = {}


def load_supplied_trust(paths):
    """Load {"keys": [{"public_key_hex": ..., "label": ...}]} files."""
    import json as _json
    for path in paths:
        with open(path, encoding="utf-8") as handle:
            data = _json.load(handle)
        for entry in data.get("keys") or []:
            key = (entry.get("public_key_hex") or "").lower()
            if not key:
                continue
            if key in PRIVINET_PUBLIC_KEYS:
                continue  # already production trust; nothing to add
            SUPPLIED_TRUST[key] = entry.get("label") or "supplied key (no label)"
TSA_VERIFIED = "verified"
TSA_FAILED = "failed"
TSA_INDETERMINATE = "indeterminate"

EU_PROFILE_ID = "EU_EIDAS_QTST_V1"
EU_QUALIFIED = "QUALIFIED"
EU_NOT_QUALIFIED = "NOT_QUALIFIED"
EU_INDETERMINATE = "INDETERMINATE"


def resolve_tsa_ca(explicit=None):
    """Locate TSA trust material. Returns a list of paths (possibly empty).

    A pack may be anchored at two unrelated authorities, so --tsa-ca is
    repeatable and $LUMRA_TSA_CA accepts an os.pathsep-separated list.
    """
    paths = list(explicit or [])
    env = os.environ.get("LUMRA_TSA_CA")
    if env:
        paths += [x for x in env.split(os.pathsep) if x]
    # Documented default bundle, lowest precedence: anything the customer
    # supplies is tried first, so their trust material always wins.
    here = os.path.dirname(os.path.abspath(__file__))
    for cand in (os.path.join(here, "tsa-cacert.pem"),
                 os.path.join(here, "tsa-trust", "lumra-tsa-bundle.pem")):
        if os.path.isfile(cand) and cand not in paths:
            paths.append(cand)
    return paths


def verify_rfc3161_token(anchor, event_hash, ca_path):
    """Cryptographically verify one anchor's timestamp token.

    Returns (status, detail) where status is TSA_VERIFIED / TSA_FAILED /
    TSA_INDETERMINATE. Nothing from the pack is ever interpolated into a
    shell: openssl is invoked with an argument list (shell=False) and all
    attacker-controlled bytes are written to files in a temp directory.
    """
    b64 = anchor.get("token_der_base64")
    if not b64:
        return TSA_INDETERMINATE, "the anchor carries no token_der_base64, so there is nothing to check"
    try:
        der = base64.b64decode(b64, validate=True)
    except Exception:
        return TSA_FAILED, "token_der_base64 is not valid base64 (malformed token)"
    if not der:
        return TSA_FAILED, "the timestamp token is empty"

    if not re.fullmatch(r"[0-9a-fA-F]{64}", event_hash or ""):
        return TSA_INDETERMINATE, "event_hash is not a sha256 hex string; refusing to guess what was stamped"

    # What the authority actually signed is sha256 over the ASCII event_hash.
    expected_imprint = hashlib.sha256(event_hash.encode("ascii")).hexdigest()
    claimed = (anchor.get("imprint_sha256") or "").lower()
    if claimed and claimed != expected_imprint:
        return TSA_FAILED, (
            "the anchor timestamps a different fingerprint than this record: imprint "
            f"{claimed[:16]}... but this record hashes to {expected_imprint[:16]}..."
        )

    openssl = shutil.which("openssl")
    if not openssl:
        return TSA_INDETERMINATE, "openssl is not on PATH, so the token signature and chain were not checked"
    ca_list = [c for c in (ca_path if isinstance(ca_path, (list, tuple)) else [ca_path]) if c]
    if not ca_list:
        return TSA_INDETERMINATE, (
            "no TSA trust material supplied (--tsa-ca <cacert.pem> or $LUMRA_TSA_CA). "
            "A signature check without a trusted root proves nothing, so this is reported as unchecked"
        )
    missing = [c for c in ca_list if not os.path.isfile(c)]
    ca_list = [c for c in ca_list if os.path.isfile(c)]
    if not ca_list:
        return TSA_INDETERMINATE, f"TSA trust material not found at {', '.join(missing)}"

    with tempfile.TemporaryDirectory() as td:
        tok = os.path.join(td, "token.tsr")
        dat = os.path.join(td, "stamped.bin")
        with open(tok, "wb") as fh:
            fh.write(der)
        with open(dat, "wb") as fh:
            fh.write(event_hash.encode("ascii"))  # exact bytes, no trailing newline
        errors = []
        for ca in ca_list:
            try:
                proc = subprocess.run(
                    [openssl, "ts", "-verify", "-data", dat, "-in", tok, "-CAfile", ca],
                    capture_output=True, timeout=60,
                )
            except subprocess.TimeoutExpired:
                return TSA_INDETERMINATE, "openssl timed out verifying the token"
            except OSError as exc:
                return TSA_INDETERMINATE, f"could not run openssl: {exc}"
            out = (proc.stdout + proc.stderr).decode("utf-8", "replace").strip()
            if proc.returncode == 0 and "verification: ok" in out.lower():
                return TSA_VERIFIED, f"openssl reports Verification: OK against {os.path.basename(ca)}"
            errors.append(out)

    # Distinguish "we lack a trusted root for THIS authority" from "the token is
    # cryptographically wrong". A pack anchored at two unrelated authorities will
    # legitimately fail against the other one's root, and that is not tampering.
    # Timestamp tokens also outlive the TSA certificate that signed them, so an
    # expired signer is a trust-material problem, not a forgery.
    CHAIN_MATERIAL = (
        "unable to get local issuer certificate",
        "unable to get issuer certificate",
        "unable to verify the first certificate",
        "self signed certificate",
        "self-signed certificate",
        "certificate has expired",
    )
    joined = " ".join(errors).lower()
    if any(marker in joined for marker in CHAIN_MATERIAL):
        return TSA_INDETERMINATE, (
            "no supplied trust root matches this authority, so the chain could not be built "
            "(this is a trust-material gap, not evidence of tampering). Supply this authority's "
            "CA with --tsa-ca to check it"
        )
    last = errors[-1].splitlines()[-1][:180] if errors and errors[-1] else "no output"
    return TSA_FAILED, f"openssl rejected the token: {last}"


def verify_eu_request_binding(anchor, batch_root):
    """Check that the retained request is exactly tied to this batch root."""
    if anchor.get("profile_id") != EU_PROFILE_ID:
        return TSA_FAILED, "anchor is not labelled as the supported EU profile"
    if anchor.get("capture_point") != "batch_closure":
        return TSA_FAILED, "capture_point must be batch_closure for this profile"
    if not re.fullmatch(r"[0-9a-f]{64}", batch_root or ""):
        return TSA_FAILED, "batch root is not lowercase SHA-256 hex"

    bound = anchor.get("bound_input") or {}
    expected = hashlib.sha256(batch_root.encode("ascii")).hexdigest()
    if bound.get("kind") != "batch_merkle_root":
        return TSA_FAILED, "bound_input.kind is not batch_merkle_root"
    if bound.get("encoding") != "lowercase_sha256_hex_ascii":
        return TSA_FAILED, "bound_input encoding is not the supported canonical form"
    if (bound.get("value_sha256") or "").lower() != expected:
        return TSA_FAILED, "bound_input digest does not match this batch root"
    if (anchor.get("imprint_sha256") or "").lower() != expected:
        return TSA_FAILED, "claimed timestamp imprint does not match this batch root"

    b64 = anchor.get("request_der_base64")
    if not b64:
        return TSA_INDETERMINATE, "original RFC 3161 request DER was not retained"
    try:
        request_der = base64.b64decode(b64, validate=True)
    except Exception:
        return TSA_FAILED, "request_der_base64 is malformed"
    if not request_der:
        return TSA_FAILED, "retained RFC 3161 request is empty"
    claimed_request_hash = (anchor.get("request_der_sha256") or "").lower()
    actual_request_hash = hashlib.sha256(request_der).hexdigest()
    if claimed_request_hash != actual_request_hash:
        return TSA_FAILED, "retained request digest does not match its bytes"
    expected_request = _eu_timestamp_request(bytes.fromhex(expected))
    if request_der != expected_request:
        return TSA_FAILED, (
            "retained request is not the exact canonical RFC 3161 request for this batch root"
        )
    return TSA_VERIFIED, "retained RFC 3161 request binds the exact batch-root bytes"


def _eu_der(tag, body):
    if len(body) < 0x80:
        return bytes([tag, len(body)]) + body
    length = len(body)
    encoded = length.to_bytes((length.bit_length() + 7) // 8, "big")
    return bytes([tag, 0x80 | len(encoded)]) + encoded + body


def _eu_timestamp_request(imprint):
    """Rebuild the deterministic certReq=true request emitted by Lumra."""
    sha256_oid = bytes.fromhex("0609608648016503040201")
    algorithm = _eu_der(0x30, sha256_oid + _eu_der(0x05, b""))
    message_imprint = _eu_der(0x30, algorithm + _eu_der(0x04, imprint))
    version = _eu_der(0x02, b"\x01")
    cert_req = _eu_der(0x01, b"\xff")
    return _eu_der(0x30, version + message_imprint + cert_req)


def _parse_iso_utc(value):
    if not value:
        return None
    try:
        parsed = datetime.fromisoformat(str(value).replace("Z", "+00:00"))
    except ValueError:
        return None
    if parsed.tzinfo is None:
        parsed = parsed.replace(tzinfo=timezone.utc)
    return parsed.astimezone(timezone.utc)


def _token_generation_time(anchor):
    """Extract genTime with OpenSSL. Returns (datetime|None, reason|None)."""
    openssl = shutil.which("openssl")
    if not openssl:
        return None, "openssl is not on PATH, so token generation time was not extracted"
    try:
        der = base64.b64decode(anchor.get("token_der_base64") or "", validate=True)
    except Exception:
        return None, "timestamp response is malformed"
    with tempfile.TemporaryDirectory() as td:
        path = os.path.join(td, "token.tsr")
        with open(path, "wb") as handle:
            handle.write(der)
        try:
            proc = subprocess.run(
                [openssl, "ts", "-reply", "-in", path, "-text"],
                capture_output=True,
                timeout=60,
            )
        except (OSError, subprocess.TimeoutExpired) as exc:
            return None, "could not inspect timestamp response: %s" % type(exc).__name__
        if proc.returncode != 0:
            return None, "openssl could not parse timestamp response"
        output = (proc.stdout + proc.stderr).decode("utf-8", "replace")
        match = re.search(r"^Time stamp:\s*(.+)$", output, re.MULTILINE)
        if not match:
            return None, "timestamp response did not expose a generation time"
        raw = match.group(1).strip()
        for fmt in ("%b %d %H:%M:%S %Y GMT", "%b %d %H:%M:%S.%f %Y GMT"):
            try:
                return datetime.strptime(raw, fmt).replace(tzinfo=timezone.utc), None
            except ValueError:
                continue
        return None, "unrecognized timestamp generation time: %s" % raw


def _token_certificate_fingerprints(anchor):
    """Return every certificate fingerprint embedded in the timestamp token."""
    openssl = shutil.which("openssl")
    if not openssl:
        return [], "openssl is not on PATH"
    try:
        from cryptography.hazmat.primitives import hashes
        from cryptography.hazmat.primitives.serialization import pkcs7
    except ImportError:
        return [], "cryptography is unavailable, so token certificates were not inspected"
    try:
        der = base64.b64decode(anchor.get("token_der_base64") or "", validate=True)
    except Exception:
        return [], "timestamp response is malformed"
    with tempfile.TemporaryDirectory() as td:
        response = os.path.join(td, "response.tsr")
        token = os.path.join(td, "token.p7")
        with open(response, "wb") as handle:
            handle.write(der)
        try:
            proc = subprocess.run(
                [openssl, "ts", "-reply", "-in", response, "-token_out", "-out", token],
                capture_output=True,
                timeout=60,
            )
        except (OSError, subprocess.TimeoutExpired) as exc:
            return [], "could not extract CMS token: %s" % type(exc).__name__
        if proc.returncode != 0:
            return [], "openssl could not extract the CMS timestamp token"
        try:
            with open(token, "rb") as handle:
                certs = pkcs7.load_der_pkcs7_certificates(handle.read())
        except Exception:
            return [], "embedded timestamp certificates could not be parsed"
    return sorted({cert.fingerprint(hashes.SHA256()).hex() for cert in certs}), None


def load_eu_trust_snapshot(path):
    if not path:
        return None, "no independently supplied Trusted List snapshot (--eu-trusted-list)"
    try:
        with open(path, encoding="utf-8") as handle:
            data = json.load(handle)
    except (OSError, ValueError) as exc:
        return None, "could not read Trusted List snapshot: %s" % type(exc).__name__
    if data.get("format") != "lumra-eu-trusted-list-snapshot-v1":
        return None, "Trusted List snapshot format is not recognized"
    return data, None


def evaluate_eu_qualification(anchor, token_info, snapshot):
    """Reject known-bad qualification cases; never manufacture QUALIFIED.

    This verifier does not yet validate EU LOTL/member-state TL XML signatures
    or historical revocation data itself. A matching normalized snapshot is
    therefore useful for deterministic negative checks but remains
    INDETERMINATE on the positive path.
    """
    if not snapshot:
        return EU_INDETERMINATE, "authenticated historical Trusted List data is missing"
    service = anchor.get("service") or {}
    provider = service.get("provider_legal_name")
    country = service.get("country_code")
    service_name = service.get("service_name")
    service_type = service.get("service_type_identifier")
    expected_cert = re.sub(
        r"[^0-9a-f]", "", (service.get("service_certificate_sha256") or "").lower()
    )
    entries = snapshot.get("services") or []
    provider_entries = [
        entry for entry in entries
        if entry.get("provider_legal_name") == provider
        and entry.get("country_code") == country
    ]
    if not provider_entries:
        return EU_NOT_QUALIFIED, "the named provider is absent from the supplied service data"
    matching = [
        entry for entry in provider_entries
        if entry.get("service_name") == service_name
        and entry.get("service_type_identifier") == service_type
        and re.sub(r"[^0-9a-f]", "", (
            entry.get("service_certificate_sha256") or ""
        ).lower()) == expected_cert
    ]
    if not matching:
        return EU_NOT_QUALIFIED, (
            "the exact service type and digital identity do not match a supplied Trusted List entry"
        )
    entry = matching[0]
    if service_type != "http://uri.etsi.org/TrstSvc/Svctype/TSA/QTST":
        return EU_NOT_QUALIFIED, "the matched service is not TSA/QTST"

    generated = token_info.get("generation_time")
    generated = _parse_iso_utc(generated) if isinstance(generated, str) else generated
    if generated is None:
        return EU_INDETERMINATE, "token generation time could not be established"

    embedded = {
        re.sub(r"[^0-9a-f]", "", value.lower())
        for value in token_info.get("certificate_sha256s") or []
    }
    if not embedded:
        return EU_INDETERMINATE, "timestamp signer certificates were not independently inspected"
    if expected_cert not in embedded:
        return EU_NOT_QUALIFIED, "the Trusted List service certificate is not embedded in this token"

    cert_from = _parse_iso_utc(entry.get("certificate_valid_from"))
    cert_to = _parse_iso_utc(entry.get("certificate_valid_to"))
    if not cert_from or not cert_to:
        return EU_INDETERMINATE, "service-certificate validity interval is missing"
    if generated < cert_from or generated > cert_to:
        return EU_NOT_QUALIFIED, "service certificate was not valid at token generation time"

    histories = entry.get("status_history") or []
    active = None
    for item in histories:
        start = _parse_iso_utc(item.get("from"))
        end = _parse_iso_utc(item.get("to"))
        if start and generated >= start and (end is None or generated < end):
            active = item
            break
    if not active:
        return EU_NOT_QUALIFIED, "no service-status interval covers token generation time"
    if str(active.get("status") or "").lower() != "granted":
        return EU_NOT_QUALIFIED, "the exact service was not granted at token generation time"

    revoked_at = _parse_iso_utc(entry.get("certificate_revoked_at"))
    if revoked_at and revoked_at <= generated:
        return EU_NOT_QUALIFIED, "service certificate was revoked by token generation time"
    if entry.get("revocation_status_at_issuance") != "good":
        return EU_INDETERMINATE, "revocation status at token issuance is missing or not good"

    # Intentionally no EU_QUALIFIED return. This JSON adapter is not a substitute
    # for authenticating LOTL/TL signatures and applying ETSI qualification rules.
    return EU_INDETERMINATE, (
        "service data matches, but this verifier does not yet authenticate EU LOTL/"
        "national TL signatures or independently validate the historical revocation evidence"
    )


def check_tenant_attestation(pack, record_pubkey_hex):
    """Validate the in-pack tenant-key attestation against the pinned roots.

    Only the copy INSIDE bundle counts: the manifest's bundle_sha256 seals it,
    so Step 5 already rejects a tampered or stripped attestation. Returns
    (label_string, None) on success, or (None, reason) on failure.
    """
    att = (pack.get("bundle") or {}).get("tenant_key_attestation")
    if not isinstance(att, dict):
        return None, "no tenant-key attestation inside the sealed bundle"
    body = att.get("body")
    if not isinstance(body, dict):
        return None, "attestation body missing"
    if body.get("type") != "privinet-tenant-key-attestation" or body.get("v") != 1:
        return None, "attestation type/version not recognized"
    if body.get("revoked") is not False:
        return None, "attestation is marked revoked"
    root_pub = att.get("root_public_key_hex") or ""
    if root_pub not in PRIVINET_ATTESTATION_ROOTS:
        return None, "attestation root key is not a pinned PriviNet attestation root"
    digest = sha256_hex(ATTEST_CONTEXT.encode("utf-8") + canonical_bytes(body))
    if not verify_signature(root_pub, digest.encode("utf-8"), att.get("signature_hex", "")):
        return None, "attestation signature did not validate against the pinned root"
    if body.get("tenant_public_key_hex") != record_pubkey_hex:
        return None, "attestation names a DIFFERENT key than the one that signed this record"
    signed_at = str((pack.get("bundle") or {}).get("proof_record", {}).get("signed_at") or "")
    not_before = str(body.get("not_before") or "")
    if signed_at and not_before and signed_at.rstrip("Z") < not_before.rstrip("Z"):
        return None, "record was signed before the attestation's validity window opened"
    label = "%s (key %s, attested by %s)" % (
        body.get("tenant_label", "?"),
        body.get("tenant_key_id", "?"),
        PRIVINET_ATTESTATION_ROOTS[root_pub],
    )
    return label, None


def canonical_bytes(obj) -> bytes:
    # Must match the producer exactly (see canonicalization_spec in the pack):
    # json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False), UTF-8.
    return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")


def sha256_hex(data: bytes) -> str:
    return hashlib.sha256(data).hexdigest()


def verify_signature(public_key_hex: str, message: bytes, signature_hex: str) -> bool:
    key_bytes = bytes.fromhex(public_key_hex)
    if len(key_bytes) == 65 and key_bytes[0] == 0x04:
        # ECDSA P-256 (uncompressed point), DER signature over SHA-256 of the
        # message. This is PriviNet's HSM-held Cloud KMS key format.
        try:
            from cryptography.exceptions import InvalidSignature
            from cryptography.hazmat.primitives import hashes
            from cryptography.hazmat.primitives.asymmetric import ec

            key = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256R1(), key_bytes)
            try:
                key.verify(bytes.fromhex(signature_hex), message, ec.ECDSA(hashes.SHA256()))
                return True
            except InvalidSignature:
                return False
        except ImportError:
            print("  ! ECDSA verification needs the 'cryptography' package:  pip install cryptography")
            sys.exit(2)
    try:
        from cryptography.exceptions import InvalidSignature
        from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey

        key = Ed25519PublicKey.from_public_bytes(key_bytes)
        try:
            key.verify(bytes.fromhex(signature_hex), message)
            return True
        except InvalidSignature:
            return False
    except ImportError:
        pass
    try:
        import nacl.exceptions
        import nacl.signing

        key = nacl.signing.VerifyKey(bytes.fromhex(public_key_hex))
        try:
            key.verify(message, bytes.fromhex(signature_hex))
            return True
        except nacl.exceptions.BadSignatureError:
            return False
    except ImportError:
        print("  ! No crypto library found. Install one:  pip install cryptography")
        sys.exit(2)


def main(path: str) -> int:
    with open(path, encoding="utf-8") as f:
        pack = json.load(f)
    # A multi-source Incident ProofPack wraps several complete single packs
    # plus a sealed assembly; everything else is a single pack.
    if isinstance(pack, dict) and "case_package_version" in pack:
        return verify_case_package(pack)
    if isinstance(pack, dict) and "incident_proofpack_version" in pack:
        return verify_incident(pack)
    return verify_pack(pack)


def verify_pack(pack) -> int:
    ci = pack["canonical_inputs"]
    proof = pack["bundle"]["proof_record"]
    context = proof["hash_context"]
    failures = []
    warnings = []

    print(f"ProofPack {pack.get('incident_id', '?')}  (version {pack.get('proofpack_version', '?')})")
    print(f"Signing class: {pack.get('signing_class', proof.get('signer_note', 'n/a'))}")
    print()
    print("Step 0: check the printable bundle agrees with what is sealed")
    # The human-readable bundle is display only; only canonical_inputs is
    # signed. If a page shows one thing while the sealed data says another,
    # that is exactly the trick a forger would try. Catch it here.
    bundle_payload = pack.get("bundle", {}).get("event", {}).get("normalized_payload")
    if bundle_payload is not None and bundle_payload != ci.get("normalized_payload"):
        failures.append("bundle.event.normalized_payload disagrees with the sealed canonical_inputs")
        print("  ! the displayed record does NOT match the sealed record (see failures)")
    else:
        print("  - displayed record matches the sealed record")

    print("Step 1: recompute component digests from canonical_inputs")

    def check(name: str, obj, stored: str | None):
        if obj is None and stored is None:
            print(f"  - {name}: omitted (consistent)")
            return None
        if obj is None or stored is None:
            failures.append(f"{name}: present/omitted mismatch")
            print(f"  ! {name}: MISMATCH (present/omitted)")
            return None
        got = sha256_hex(canonical_bytes(obj))
        ok = got == stored
        print(f"  {'-' if ok else '!'} {name}: {'match' if ok else 'MISMATCH'}")
        if not ok:
            failures.append(f"{name}: {got} != {stored}")
        return got

    payload_hash = check("normalized_payload", ci.get("normalized_payload"), proof.get("normalized_payload_hash"))
    analysis_hash = check("analysis", ci.get("analysis"), proof.get("analysis_hash"))
    window_hash = check("evidence_window", ci.get("evidence_window"), proof.get("evidence_window_hash"))

    print("Step 2: recompute the composite event digest")
    composite_src = f"{context}|{payload_hash or proof.get('normalized_payload_hash')}|" \
                    f"{analysis_hash or proof.get('analysis_hash')}|" \
                    f"{(window_hash or proof.get('evidence_window_hash')) or ''}"
    composite = sha256_hex(composite_src.encode("utf-8"))
    if composite == proof["event_hash"]:
        print("  - composite event_hash: match")
    else:
        failures.append(f"composite: {composite} != {proof['event_hash']}")
        print("  ! composite event_hash: MISMATCH")

    print("Step 3: verify the record signature over event_hash")
    pubkey = proof["public_key_hex"]
    sig_ok = verify_signature(pubkey, proof["event_hash"].encode("ascii"), proof["signature"])
    print(f"  {'-' if sig_ok else '!'} signature: {'valid' if sig_ok else 'INVALID'}")
    if not sig_ok:
        failures.append("signature invalid")

    print("Step 4: is the signing key PriviNet's published key?")
    tenant_attested = False
    if pubkey in PRIVINET_PUBLIC_KEYS:
        print(f"  - key recognized: {PRIVINET_PUBLIC_KEYS[pubkey]}")
        if pubkey in PRIVINET_NON_PRIMARY_KEYS:
            print(f"  ? NOTE: {PRIVINET_NON_PRIMARY_KEYS[pubkey]}")
    else:
        # Not the shared production key: check for a dedicated tenant key,
        # established by a root-signed attestation inside the sealed bundle.
        att_label, att_reason = check_tenant_attestation(pack, pubkey)
        if att_label:
            tenant_attested = True
            print(f"  - key attested by PriviNet's published attestation root:")
            print(f"    {att_label}")
            print("    (dedicated tenant key, held in Google Cloud KMS; the attestation is")
            print("    sealed inside the bundle, so Step 5 rejects any tamper with it)")
        else:
            warnings.append(
                "the signing key is NOT in PriviNet's published key list. This pack may be "
                "self-consistent yet NOT signed by PriviNet. Confirm the key at "
                "https://privinet.net/keys.txt before relying on it."
            )
            print(f"  ? unrecognized signing key {pubkey[:16]}... (see warning below)")
            if att_reason and "no tenant-key attestation" not in att_reason:
                # An attestation was present but did not validate: say so plainly,
                # because a broken attestation is a stronger signal than none.
                warnings.append("a tenant-key attestation is present but failed: " + att_reason)
                print(f"  ? tenant-key attestation present but FAILED: {att_reason}")

    manifest = pack.get("manifest")
    msig = pack.get("manifest_signature")
    if manifest and msig:
        print("Step 5: manifest signature (binds the document's canonical content)")
        ci_hash = sha256_hex(canonical_bytes(ci))
        bundle_hash = sha256_hex(canonical_bytes(pack.get("bundle", {})))
        if manifest.get("canonical_inputs_sha256") != ci_hash:
            failures.append("manifest canonical_inputs_sha256 does not match the inputs")
            print("  ! manifest does not match canonical_inputs")
        elif manifest.get("bundle_sha256") != bundle_hash:
            failures.append("manifest bundle_sha256 does not match the display bundle (bundle was altered)")
            print("  ! manifest does not match the display bundle")
        else:
            manifest_digest = sha256_hex(canonical_bytes(manifest))
            mkey = msig.get("public_key_hex", "")
            m_ok = verify_signature(mkey, manifest_digest.encode("utf-8"), msig.get("signature", ""))
            if m_ok and mkey == pubkey:
                print("  - manifest signature valid; the sealed content (inputs, display bundle,")
                print("    signing metadata, anchor status) is bound and unaltered. Any change to a")
                print("    sealed value fails; cosmetic JSON whitespace is normalized before hashing.")
            elif m_ok and mkey != pubkey:
                failures.append("manifest is signed by a different key than the record")
                print("  ! manifest signed by a different key than the event record")
            else:
                failures.append("manifest signature invalid")
                print("  ! manifest signature INVALID")
    else:
        # Every pack since format 1.3 ships a manifest. A "1.3+" pack without
        # one is not an older format: it is a pack with its manifest stripped,
        # which is exactly how an attacker would smuggle doctored display text
        # past the inner (input-level) digests. Hard failure, not a warning.
        try:
            _ver = float(str(pack.get("proofpack_version", "0")))
        except ValueError:
            _ver = 0.0
        if _ver >= 1.3:
            failures.append(
                "this pack declares format %s, which always carries a signed manifest, "
                "but the manifest is missing. It was stripped; treat the displayed "
                "content as altered." % pack.get("proofpack_version")
            )
            print("Step 5: manifest signature (binds the document's canonical content)")
            print("  ! MISSING on a format that requires it: the manifest was stripped")
        else:
            warnings.append(
                "this pack has no manifest signature (older format). Only the inner event "
                "objects are bound; the displayed metadata is not independently protected."
            )

    eb = pack.get("bundle", {}).get("edge_binding") or {}
    claims_source = "source-signed" in (pack.get("signing_class", "").lower())
    if eb.get("device_signature") and eb.get("device_public_key_hex"):
        print("Step 5b: source (device) signature")
        dkey = eb["device_public_key_hex"]
        dpayload = eb.get("device_signed_payload")
        signed_digest = eb.get("signed_digest", "")
        recomputed = sha256_hex(canonical_bytes(dpayload)) if dpayload is not None else None
        # Tie the device-signed payload to the SEALED record, not merely to
        # itself: the proof record carries raw_payload_hash (the digest of the
        # payload as ingested), so a swapped-in device payload that self-hashes
        # correctly but differs from what was actually sealed is caught here.
        sealed_raw = pack.get("bundle", {}).get("proof_record", {}).get("raw_payload_hash")
        if recomputed != signed_digest:
            failures.append("device_signed_payload does not hash to the digest the device signed")
            print("  ! the device-signed payload does not match signed_digest")
        elif sealed_raw is not None and recomputed != sealed_raw:
            failures.append(
                "the device-signed payload is not the payload PriviNet sealed (raw_payload_hash mismatch)"
            )
            print("  ! the device-signed payload differs from the sealed payload")
        else:
            d_ok = verify_signature(dkey, signed_digest.encode("utf-8"), eb["device_signature"])
            if not d_ok:
                failures.append("device signature invalid")
                print("  ! device signature INVALID")
            elif dkey in PRIVINET_DEVICE_KEYS:
                print(f"  - device signature valid; key recognized: {PRIVINET_DEVICE_KEYS[dkey]}")
            else:
                print(f"  - device signature valid; device key {dkey[:16]}... (confirm at keys.txt)")
    elif claims_source:
        # The pack claims source-signing but carries no verifiable device
        # signature. Do not let that claim pass unchallenged.
        failures.append(
            "signing_class claims 'Source-signed' but the pack carries no device signature to verify"
        )
        print("Step 5b: source (device) signature")
        print("  ! claims Source-signed but no device signature is present to check")

    anchor = pack.get("timestamp_anchor") or {}
    a_status = anchor.get("status")
    # The manifest (if present) bound the anchor status + imprint, so a
    # stripped or swapped anchor is caught here rather than passing silently.
    m_anchor_status = manifest.get("timestamp_anchor_status") if manifest else None
    if manifest is not None and "timestamp_anchor_status" in manifest:
        if (a_status or None) != (m_anchor_status or None):
            failures.append(
                f"timestamp anchor was altered or removed: manifest bound status "
                f"'{m_anchor_status}' but the pack shows '{a_status}'"
            )
            print("  ! the timestamp anchor does not match what the manifest bound (stripped or swapped)")
        elif a_status == "anchored" and manifest.get("timestamp_anchor_imprint_sha256") != anchor.get("imprint_sha256"):
            failures.append("timestamp anchor imprint does not match the manifest")
            print("  ! the timestamp anchor imprint does not match the manifest")
    tsa_primary = None
    if a_status == "anchored":
        print(f"Step 6: independent timestamp anchor ({anchor.get('standard')}, {anchor.get('authority')})")
        print("  - present and bound into the manifest.")
        tsa_primary, tsa_detail = verify_rfc3161_token(anchor, proof.get("event_hash", ""), TSA_CA_PATH)
        if tsa_primary == TSA_VERIFIED:
            print("  - TIMESTAMP CRYPTOGRAPHICALLY VERIFIED: " + tsa_detail)
        elif tsa_primary == TSA_FAILED:
            failures.append("timestamp token failed cryptographic verification: " + tsa_detail)
            print("  ! TIMESTAMP VERIFICATION FAILED: " + tsa_detail)
        else:
            print("  ? timestamp present, full verification unavailable: " + tsa_detail)
    elif a_status in ("unavailable", "disabled"):
        print("Step 6: independent timestamp anchor")
        print(f"  ? none on this pack ({a_status}). Signing time is PriviNet's own clock; request a re-anchored pack for a third-party time.")

    # Dual anchoring: a SECOND authority's anchor may ride inside the sealed
    # bundle (Step 5's bundle_sha256 binds it, so stripping it fails above).
    # Only the sealed copy counts; a top-level copy is display convenience.
    sealed_second = (pack.get("bundle") or {}).get("timestamp_anchor_secondary") or {}
    top_second = pack.get("timestamp_anchor_secondary")
    if top_second is not None and top_second != (sealed_second or None):
        warnings.append(
            "a top-level timestamp_anchor_secondary does not match the sealed copy inside "
            "the bundle; trust only the sealed copy"
        )
        print("  ? top-level secondary anchor differs from the sealed copy (see warning below)")
    if sealed_second.get("status") == "anchored":
        print(f"Step 6b: second independent anchor ({sealed_second.get('standard')}, {sealed_second.get('authority')})")
        s2_status, s2_detail = verify_rfc3161_token(sealed_second, proof.get("event_hash", ""), TSA_CA_PATH)
        if s2_status == TSA_VERIFIED:
            print("  - TIMESTAMP CRYPTOGRAPHICALLY VERIFIED: " + s2_detail)
        elif s2_status == TSA_FAILED:
            failures.append("second timestamp token failed cryptographic verification: " + s2_detail)
            print("  ! TIMESTAMP VERIFICATION FAILED: " + s2_detail)
        else:
            print("  ? timestamp present, full verification unavailable: " + s2_detail)
        if a_status == "anchored" and sealed_second.get("imprint_sha256") != anchor.get("imprint_sha256"):
            failures.append(
                "the secondary anchor's imprint differs from the primary's: it timestamps a "
                "DIFFERENT fingerprint, not this record"
            )
            print("  ! secondary anchor covers a different fingerprint than the primary")
        else:
            print("  - present and SEALED INSIDE the bundle: two unrelated authorities attest")
            print("    the same fingerprint. Verify it exactly like the primary, per its own")
            print("    verify_instructions.")

    print()
    if failures:
        print("RESULT: VERIFICATION FAILED")
        for f_ in failures:
            print(f"  * {f_}")
        print("At least one record does not match what was sealed. Treat this pack as altered.")
        return 1
    if warnings:
        # Integrity is internally consistent, but the key is not PriviNet's,
        # so DO NOT lead with a green 'verified' line a careless reader could
        # screenshot as authentic.
        print("RESULT: NOT CONFIRMED AS A PRIVINET RECORD.")
        print("The records are internally consistent and unaltered, but:")
        for w in warnings:
            print(f"  ! {w}")
        print("Treat this as an unverified assertion until the signing key is confirmed.")
        return 3
    if tenant_attested:
        print("RESULT: INTEGRITY VERIFIED, AND SIGNED UNDER A PRIVINET-ATTESTED TENANT KEY.")
        print("Every digest and the manifest signature pass, and the signing key is a dedicated")
        print("tenant key whose identity is established by an attestation signed by PriviNet's")
        print("published attestation root (pinned in this script). The attestation rides inside")
        print("the sealed bundle, so altering or removing it fails verification like any other")
        print("sealed value. Any change to a sealed value fails; hashes are over canonical content.")
    else:
        print("RESULT: INTEGRITY VERIFIED, AND SIGNED BY PRIVINET.")
        print("Every digest, the manifest signature, and the published-key check pass, so the sealed")
        print("content (records, display bundle, signing metadata, anchor) is unaltered and signed by")
        print("PriviNet's published key. Any change to a sealed value fails; the hash is over canonical")
        print("content, so cosmetic JSON whitespace is normalized away and is not part of the seal.")
    if a_status == "anchored":
        if tsa_primary == TSA_VERIFIED:
            print("The independent timestamp token was cryptographically verified against the supplied")
            print("trust root: the authority attested this record's fingerprint at the time it states.")
        else:
            print("An independent timestamp anchor is present and manifest-bound, but it was NOT")
            print("cryptographically verified here (see Step 6). Supply --tsa-ca <cacert.pem> to check it.")
    print("No PriviNet system was contacted to reach this conclusion. Note: this proves the")
    print("record was not altered after signing; it does not prove when the underlying event occurred.")
    return 0


def verify_incident(doc) -> int:
    """Verify a multi-source Incident ProofPack: every embedded source pack
    individually (same checks as a single pack), then the SEALED ASSEMBLY:
    the incident_manifest that binds which packs belong together, in what
    order, with what fused readout. Without the assembly seal, four valid
    packs prove four valid records; the seal is what proves they are ONE
    incident as exported."""
    sources = doc.get("sources") or []
    print(f"Incident ProofPack '{doc.get('incident_label', '?')}'  (version {doc.get('incident_proofpack_version', '?')})")
    print(f"Partner: {doc.get('partner_name', 'n/a')}  |  source records: {len(sources)}")
    src_results = []
    for i, s in enumerate(sources, 1):
        label = s.get("vendor") or s.get("key_id") or f"source {i}"
        print()
        print("=" * 74)
        print(f"SOURCE {i}/{len(sources)}: {label}")
        print("=" * 74)
        pack = s.get("proofpack")
        if not isinstance(pack, dict):
            print("  ! no embedded ProofPack for this source")
            src_results.append(1)
            continue
        src_results.append(verify_pack(pack))

    print()
    print("=" * 74)
    print("INCIDENT ASSEMBLY: do these packs, in this order, belong together?")
    print("=" * 74)
    failures = []
    warnings = []
    man = doc.get("incident_manifest")
    msig = doc.get("incident_manifest_signature")

    def _ver_num(v):
        try:
            return float(str(v))
        except ValueError:
            return 0.0

    if not (man and msig):
        if _ver_num(doc.get("incident_proofpack_version", "0")) >= 1.1:
            failures.append(
                "this incident declares format %s, which always carries a sealed assembly "
                "manifest, but it is missing. It was stripped; the grouping cannot be trusted."
                % doc.get("incident_proofpack_version")
            )
            print("  ! sealed assembly MISSING on a format that requires it (stripped)")
        else:
            warnings.append(
                "This incident file predates assembly sealing (format %s). Each source pack "
                "verifies on its own above, but WHICH packs belong together, their order, the "
                "fused readout, and the assessment are not cryptographically bound in this "
                "file. Request a re-exported incident for a sealed assembly."
                % doc.get("incident_proofpack_version", "1.0")
            )
            print("  ? no sealed assembly on this older-format incident (see result below)")
    else:
        entries = man.get("sources") or []
        if len(entries) != len(sources) or man.get("sources_count") != len(sources):
            failures.append("the sealed assembly's source count does not match the packs present")
            print("  ! source count mismatch between the sealed assembly and the document")
        else:
            all_match = True
            for i, (s, e) in enumerate(zip(sources, entries), 1):
                got = sha256_hex(canonical_bytes((s.get("proofpack") or {}).get("manifest") or {}))
                if got != e.get("pack_manifest_sha256") or s.get("event_hash") != e.get("event_hash"):
                    all_match = False
                    failures.append(
                        f"source {i} does not match the sealed assembly (swapped, altered, or reordered)"
                    )
                    print(f"  ! source {i}: does NOT match the sealed assembly")
            if all_match:
                print(f"  - all {len(sources)} source packs match the sealed assembly, in order")
        if sha256_hex(canonical_bytes(doc.get("incident") or {})) != man.get("incident_sha256"):
            failures.append("the fused incident readout does not match the sealed assembly")
            print("  ! fused incident readout was altered")
        else:
            print("  - fused incident readout matches the sealed assembly")
        if sha256_hex(canonical_bytes(doc.get("evidence_trust_assessment") or {})) != man.get(
            "evidence_trust_assessment_sha256"
        ):
            failures.append("the incident-level Evidence Trust Assessment does not match the sealed assembly")
            print("  ! incident Evidence Trust Assessment was altered")
        if sha256_hex(canonical_bytes(doc.get("neutrality_note") or "")) != man.get("neutrality_note_sha256"):
            failures.append("the neutrality note does not match the sealed assembly")
            print("  ! neutrality note was altered")
        man_digest = sha256_hex(canonical_bytes(man))
        mkey = msig.get("public_key_hex", "")
        if not verify_signature(mkey, man_digest.encode("utf-8"), msig.get("signature", "")):
            failures.append("incident assembly signature invalid")
            print("  ! incident assembly signature INVALID")
        elif mkey in PRIVINET_PUBLIC_KEYS:
            print(f"  - assembly signature valid; key recognized: {PRIVINET_PUBLIC_KEYS[mkey]}")
        else:
            warnings.append(
                "the incident assembly is signed by a key NOT in PriviNet's published list. "
                "Confirm the key at https://privinet.net/keys.txt before relying on the grouping."
            )
            print(f"  ? assembly signed by unrecognized key {mkey[:16]}... (see warning below)")

    print()
    n_ok = sum(1 for r in src_results if r == 0)
    n_warn = sum(1 for r in src_results if r == 3)
    n_fail = sum(1 for r in src_results if r not in (0, 3))
    if failures or n_fail:
        print("RESULT: INCIDENT VERIFICATION FAILED")
        for f_ in failures:
            print(f"  * {f_}")
        if n_fail:
            print(f"  * {n_fail} source pack(s) failed verification (see above)")
        print("At least one sealed value does not match. Treat this incident document as altered.")
        return 1
    if warnings or n_warn:
        print("RESULT: NOT FULLY CONFIRMED")
        print(f"Source packs verified: {n_ok}/{len(sources)}"
              + (f" ({n_warn} not confirmed as PriviNet packs)" if n_warn else ""))
        for w in warnings:
            print(f"  ! {w}")
        return 3
    print(f"RESULT: INCIDENT VERIFIED. {n_ok}/{len(sources)} SOURCE PACKS AND THE SEALED ASSEMBLY ALL PASS.")
    print("Every source pack verifies on its own, and the sealed assembly proves these exact")
    print("packs, in this exact order, with this exact fused readout and assessment, were")
    print("bound together and signed by PriviNet's published key. No PriviNet system was contacted.")
    return 0



# ---------------------------------------------------------------------------
# Merkle inclusion, for receipt-time batch commitments
# ---------------------------------------------------------------------------

LEAF_PREFIX = b"\x00"
NODE_PREFIX = b"\x01"
LEAF_SCHEMA = "lumra.batch.leaf.v1"


def _batch_leaf_hash(event_id, event_hash, received_at_iso, environment,
                     schema_version="1"):
    """Recompute a committed leaf from the RECORD's own fields.

    Recomputing rather than trusting a supplied leaf hash is what makes the
    0x00/0x01 domain separation defend anything: because the leaf is derived
    here with the leaf prefix, an interior node (built with the node prefix)
    can never be substituted to fake an inclusion proof.
    """
    parts = [LEAF_SCHEMA, schema_version, environment, event_id,
             event_hash or "", received_at_iso]
    payload = "\x1f".join(parts).encode("utf-8")
    return hashlib.sha256(LEAF_PREFIX + payload).hexdigest()


def _fold_inclusion_path(leaf, path, root):
    current = leaf
    for step in path or []:
        sibling = step.get("hash")
        position = step.get("position")
        if not sibling or position not in ("left", "right"):
            return False
        try:
            if position == "right":
                current = hashlib.sha256(
                    NODE_PREFIX + bytes.fromhex(current) + bytes.fromhex(sibling)
                ).hexdigest()
            else:
                current = hashlib.sha256(
                    NODE_PREFIX + bytes.fromhex(sibling) + bytes.fromhex(current)
                ).hexdigest()
        except ValueError:
            return False
    return current == root


def _eu_operational_state(observations):
    batches = {}
    no_batch = 0
    delayed = 0
    for observation in observations:
        receipt = observation.get("receipt_anchor") or {}
        batch_id = receipt.get("batch_id")
        if batch_id:
            batches.setdefault(batch_id, receipt)
        else:
            no_batch += 1
        if receipt.get("committed_late") or receipt.get("status") == "overdue":
            delayed += 1
    counts = {
        "anchored_with_profile": 0,
        "anchored_without_profile": 0,
        "pending": 0,
        "failed": 0,
        "overdue": 0,
        "not_committed": no_batch,
    }
    for receipt in batches.values():
        status = receipt.get("status")
        anchor = receipt.get("anchor") or {}
        if status == "anchored":
            key = (
                "anchored_with_profile"
                if anchor.get("profile_id") == EU_PROFILE_ID
                else "anchored_without_profile"
            )
            counts[key] += 1
        elif status in counts:
            counts[status] += 1
        else:
            counts["not_committed"] += 1
    return {
        "unique_batches": len(batches),
        "counts": counts,
        "delayed_observations": delayed,
    }, batches


def verify_eu_profile(bundle, profile):
    """Verify the optional EU profile and return failures/incomplete/uncertainty."""
    failures = []
    incomplete = []
    uncertainty = []
    observations = bundle.get("observations") or []

    print()
    print("Step 4b: optional EU qualified-timestamp profile")
    print("  Profile status is evaluated separately from RFC 3161 token validity.")
    if profile.get("profile_id") != EU_PROFILE_ID:
        failures.append("unsupported or missing EU profile identifier")
        print("  FAIL: unsupported or missing profile identifier.")
        return failures, incomplete, uncertainty
    if profile.get("qualification_status") != EU_INDETERMINATE:
        failures.append(
            "this verifier version permits only INDETERMINATE qualification; package claimed %s"
            % profile.get("qualification_status")
        )
        print("  FAIL: package overstates qualification as %s." % profile.get("qualification_status"))
    if profile.get("readiness") != "IN_DEVELOPMENT_EXTERNAL_INTEGRATION_UNVERIFIED":
        failures.append("EU profile readiness label is missing or overstated")

    recomputed, batches = _eu_operational_state(observations)
    claimed = profile.get("operational_state") or {}
    if claimed != recomputed:
        failures.append("EU operational-state summary does not match sealed observations")
        print("  FAIL: profile backlog/delay counts do not match the evidence.")
    else:
        print("  OK: pending, failed, overdue and delayed counts recompute from the package.")
    if b"All relevant events were captured" in canonical_bytes(profile):
        failures.append("EU profile contains an unsupported completeness claim")

    snapshot, snapshot_error = load_eu_trust_snapshot(EU_TRUST_LIST_PATH)
    profile_roots = []
    for receipt in batches.values():
        if receipt.get("status") != "anchored" or not receipt.get("merkle_root"):
            continue
        anchor = receipt.get("anchor") or {}
        if anchor.get("profile_id") != EU_PROFILE_ID:
            continue
        profile_roots.append((receipt.get("merkle_root"), anchor))

    if not profile_roots:
        incomplete.append(
            "EU profile requested but no batch carries an EU_EIDAS_QTST_V1 timestamp"
        )
        print("  INDETERMINATE: no EU-profile timestamp is present.")
    for root, anchor in profile_roots:
        short = str(root)[:16]
        binding_status, binding_detail = verify_eu_request_binding(anchor, root)
        if binding_status == TSA_FAILED:
            failures.append("root %s... request binding: %s" % (short, binding_detail))
            print("  FAIL: root %s... %s" % (short, binding_detail))
            continue
        if binding_status == TSA_INDETERMINATE:
            incomplete.append("root %s... request binding: %s" % (short, binding_detail))
            print("  INDETERMINATE: root %s... %s" % (short, binding_detail))
        else:
            print("  OK: root %s... %s" % (short, binding_detail))

        token_status, token_detail = verify_rfc3161_token(anchor, root, TSA_CA_PATH)
        if token_status == TSA_FAILED:
            failures.append("root %s... timestamp token: %s" % (short, token_detail))
            print("  FAIL: root %s... %s" % (short, token_detail))
            continue
        if token_status == TSA_INDETERMINATE:
            incomplete.append("root %s... timestamp token: %s" % (short, token_detail))
            print("  INDETERMINATE: root %s... %s" % (short, token_detail))
        else:
            print("  OK: root %s... %s" % (short, token_detail))

        generated, generated_error = _token_generation_time(anchor)
        fingerprints, fingerprint_error = _token_certificate_fingerprints(anchor)
        token_info = {
            "generation_time": generated,
            "certificate_sha256s": fingerprints,
        }
        if generated_error:
            incomplete.append("root %s... %s" % (short, generated_error))
        if fingerprint_error:
            incomplete.append("root %s... %s" % (short, fingerprint_error))
        qstatus, qdetail = evaluate_eu_qualification(anchor, token_info, snapshot)
        if snapshot_error and qstatus == EU_INDETERMINATE:
            qdetail = snapshot_error
        package_qstatus = (anchor.get("qualification") or {}).get("status")
        if package_qstatus != EU_INDETERMINATE:
            failures.append(
                "root %s... anchor claimed qualification status %s"
                % (short, package_qstatus)
            )
        if qstatus == EU_NOT_QUALIFIED:
            failures.append("root %s... NOT QUALIFIED: %s" % (short, qdetail))
            print("  NOT QUALIFIED: root %s... %s" % (short, qdetail))
        elif qstatus == EU_QUALIFIED:
            # Defensive dead-end: evaluate_eu_qualification intentionally has
            # no positive path until authenticated LOTL/TL validation exists.
            failures.append("unsupported QUALIFIED result produced")
            print("  FAIL: unsupported QUALIFIED result.")
        else:
            incomplete.append("root %s... qualification: %s" % (short, qdetail))
            print("  INDETERMINATE qualification: root %s... %s" % (short, qdetail))

    uncertainty.extend([
        "A timestamp can establish that a digest existed by its token time; it does not prove device observation or actual receipt time.",
        "Coverage remains limited to the supplied roster and expectations; cryptography cannot prove all relevant events were captured.",
        "EU qualification does not imply Swiss qualification or guarantee admission in a legal proceeding.",
    ])
    return failures, incomplete, uncertainty


def verify_case_package(doc) -> int:
    """Verify a Case Evidence Package.

    Findings are reported in separate groups, because collapsing them misleads.
    Integrity, source authentication, timestamp validity, coverage and remaining
    uncertainty answer different questions.

    A check that could not be COMPLETED (no OpenSSL, no trust material) is never
    reported as a pass and never as a failure. It is reported as incomplete.
    """
    integrity = []
    timestamp_findings = []
    coverage_findings = []
    incomplete = []
    uncertainty = []
    # "Self-consistent but not attributable to PriviNet" is its own outcome. It is
    # not an integrity failure (the maths checks out) and it is certainly not a
    # pass, so it gets its own bucket and its own exit code.
    not_confirmed = []
    used_supplied_trust = []

    print("Lumra standalone verifier %s" % VERIFIER_VERSION)
    print("Case Evidence Package  (version %s)" % doc.get("case_package_version", "?"))
    print("Case reference: %s" % doc.get("case_ref", "?"))
    bundle = doc.get("bundle") or {}
    env = (bundle.get("environment") or {}).get("name", "unknown")
    is_prod = (bundle.get("environment") or {}).get("is_production")
    print("Environment:    %s%s" % (env, "" if is_prod else "   [NOT production evidence]"))
    fixture = (bundle.get("fixture_notice") or {})
    if fixture:
        print()
        print("  *** CONSTRUCTED FIXTURE, NOT A LIVE RECORDING ***")
        for line in fixture.get("notes") or []:
            print("  %s" % line)
    print()

    # --- 1. integrity: the manifest binds the whole document -----------------
    print("Step 1: sealed content matches the signed manifest")
    manifest = doc.get("case_package_manifest")
    sig = doc.get("case_package_manifest_signature")
    if not manifest or not sig:
        integrity.append("no case_package_manifest or signature: document is NOT sealed")
        print("  FAIL: the document carries no manifest signature.")
    else:
        recomputed_bundle = sha256_hex(canonical_bytes(bundle))
        if recomputed_bundle != manifest.get("bundle_sha256"):
            integrity.append("bundle digest does not match the manifest")
            print("  FAIL: the bundle has changed since it was sealed.")
        else:
            print("  OK: bundle digest matches the manifest.")

        recomputed_manifest = sha256_hex(canonical_bytes(manifest))
        manifest_key = sig.get("public_key_hex", "") or ""
        if recomputed_manifest != sig.get("manifest_digest"):
            integrity.append("manifest digest mismatch")
            print("  FAIL: the manifest has been altered.")
        elif verify_signature(manifest_key,
                              # The signed message is the hex digest encoded as
                              # UTF-8, matching how every other manifest in this
                              # verifier is checked. Passing raw bytes here
                              # silently fails every signature.
                              recomputed_manifest.encode("utf-8"),
                              sig.get("signature", "")):
            print("  OK: manifest signature is internally consistent.")
        else:
            integrity.append("manifest signature invalid")
            print("  FAIL: manifest signature does not verify.")

        # ---- the check whose absence made every other check pointless ----
        # A signature that validates against a key carried inside the same
        # document proves only that the document is self-consistent. An attacker
        # who rewrites the content, generates a key and re-signs produces a
        # perfectly self-consistent document. The key must be one WE recognise,
        # independently of the package.
        print()
        print("Step 1b: is the signing key PriviNet's, independently of this file?")
        if manifest_key in PRIVINET_PUBLIC_KEYS:
            print("  OK: key recognized: %s" % PRIVINET_PUBLIC_KEYS[manifest_key])
            if manifest_key in PRIVINET_NON_PRIMARY_KEYS:
                print("  NOTE: %s" % PRIVINET_NON_PRIMARY_KEYS[manifest_key])
        elif manifest_key.lower() in SUPPLIED_TRUST:
            used_supplied_trust.append((manifest_key, SUPPLIED_TRUST[manifest_key.lower()]))
            print("  OK UNDER SUPPLIED TRUST: %s" % SUPPLIED_TRUST[manifest_key.lower()])
            print("    This key is NOT one of PriviNet's production keys. It was")
            print("    supplied for this run with --trust-keys, and the result below")
            print("    holds only as far as you trust whoever gave you that file.")
        else:
            att_label, att_reason = check_tenant_attestation(doc, manifest_key)
            if att_label:
                print("  OK: key attested by PriviNet's published attestation root:")
                print("    %s" % att_label)
            else:
                not_confirmed.append(
                    "the signing key is NOT in this verifier's pinned PriviNet key "
                    "list and carries no valid attestation. This document may be "
                    "perfectly self-consistent and still NOT be a PriviNet record: "
                    "anyone can generate a key and sign their own content with it."
                )
                print("  NOT CONFIRMED: unrecognized signing key %s..." % manifest_key[:16])
                print("    The seal is self-consistent but proves only that whoever")
                print("    holds THIS key signed THIS content. It does not establish")
                print("    that PriviNet produced it.")
                if att_reason and "no tenant-key attestation" not in att_reason:
                    not_confirmed.append(
                        "a tenant-key attestation is present but failed: " + att_reason
                    )
                    print("    tenant-key attestation present but FAILED: %s" % att_reason)

    # --- 2. source authentication -------------------------------------------
    print()
    print("Step 2: recompute and verify EACH observation")
    observations = bundle.get("observations") or []
    if not observations:
        print("  (no observations in this package)")
    else:
        verified = 0
        unverifiable = 0
        for obs in observations:
            oid = str(obs.get("event_id", "?"))[:8]
            pr = obs.get("proof")
            if not pr:
                # Previously this step counted a producer-supplied label and
                # called it source authentication. A package with no proof
                # material cannot be verified and must say so.
                unverifiable += 1
                not_confirmed.append(
                    "observation %s carries no proof material, so its content "
                    "cannot be independently verified" % oid
                )
                continue

            ci = pr.get("canonical_inputs") or {}
            cd = pr.get("component_digests") or {}
            local_fail = []

            # 1. recompute each component digest from the exported inputs
            for name, key in (("normalized_payload", "normalized_payload_hash"),
                              ("analysis", "analysis_hash"),
                              ("evidence_window", "evidence_window_hash")):
                obj, stored = ci.get(name), cd.get(key)
                if obj is None and stored is None:
                    continue
                if obj is None or stored is None:
                    local_fail.append("%s present/omitted mismatch" % name)
                    continue
                if sha256_hex(canonical_bytes(obj)) != stored:
                    local_fail.append("%s digest mismatch" % name)

            # 2. recompute the composite event hash
            composite = "|".join([
                pr.get("hash_context") or "",
                cd.get("normalized_payload_hash") or "",
                cd.get("analysis_hash") or "",
                cd.get("evidence_window_hash") or "",
            ]).encode("utf-8")
            if sha256_hex(composite) != pr.get("event_hash"):
                local_fail.append("event_hash does not match its components")

            # 2b. the DISPLAYED payload must agree with the sealed input.
            # Without this, the human-readable observation could be doctored
            # while the signed canonical input stayed intact: a reader sees one
            # reading and the maths verifies a different one. This is the same
            # display-vs-sealed defence Step 0 gives the per-event pack.
            displayed = obs.get("payload")
            sealed_np = ci.get("normalized_payload") or {}
            sealed_inner = sealed_np.get("payload") if isinstance(sealed_np, dict) else None
            if displayed is not None and sealed_inner is not None:
                if canonical_bytes(displayed) != canonical_bytes(sealed_inner):
                    local_fail.append(
                        "displayed payload does not match the sealed canonical input"
                    )

            # 3. verify the signature over the event hash
            okey = pr.get("public_key_hex") or ""
            if not verify_signature(okey, (pr.get("event_hash") or "").encode("ascii"),
                                    pr.get("signature") or ""):
                local_fail.append("record signature invalid")

            # 4. pin the record's signing key too
            if okey not in PRIVINET_PUBLIC_KEYS and okey.lower() not in SUPPLIED_TRUST:
                att_label, _ = check_tenant_attestation(doc, okey)
                if not att_label:
                    not_confirmed.append(
                        "observation %s is signed by a key this verifier does not "
                        "recognise" % oid
                    )

            if local_fail:
                integrity.append("observation %s: %s" % (oid, "; ".join(local_fail)))
            else:
                verified += 1

        print("  observations fully recomputed and signature-verified: %d/%d"
              % (verified, len(observations)))
        if unverifiable:
            print("  observations with NO proof material: %d" % unverifiable)
        classes = {}
        for obs in observations:
            cls = obs.get("signing_class") or "unknown"
            classes[cls] = classes.get(cls, 0) + 1
        for cls in sorted(classes):
            print("    %d labelled: %s" % (classes[cls], cls))
        print("  NOTE: a signing class describes who signed the record, not whether")
        print("        the sensor reported the truth.")

    # --- 3. receipt-time commitments ----------------------------------------
    print()
    print("Step 3: receipt-time batch commitments")
    print("  (four different times appear below and are NOT interchangeable:")
    print("   source-reported = the device's claim; asserted receipt = Lumra's claim;")
    print("   batch closure = Lumra's clock; external timestamp = the only")
    print("   independently attested time)")
    anchored = pending = not_committed = failed = 0
    for obs in observations:
        ra = obs.get("receipt_anchor") or {}
        status = ra.get("status")
        if status == "anchored":
            leaf = _batch_leaf_hash(
                obs.get("event_id"),
                ra.get("committed_event_hash"),
                # Lumra's ASSERTED receipt time. Committing it freezes the claim;
                # it does not corroborate it.
                ra.get("committed_lumra_asserted_receipt_time")
                or ra.get("committed_receipt_time"),  # older packages
                ra.get("environment", ""),
                ra.get("schema_version", "1"),
            )
            if _fold_inclusion_path(leaf, ra.get("inclusion_path"), ra.get("merkle_root")):
                anchored += 1
            else:
                failed += 1
                integrity.append(
                    "inclusion proof does not verify for event %s" % obs.get("event_id")
                )
        elif status in ("pending", "overdue"):
            pending += 1
        elif status == "failed":
            failed += 1
        else:
            not_committed += 1
    print("  inclusion proofs verified: %d" % anchored)
    late = [o for o in observations
            if (o.get("receipt_anchor") or {}).get("committed_late")]
    if late:
        print("  committed LATE: %d record(s) missed their batch at ingest and were" % len(late))
        print("    committed afterwards by the recovery sweep. Each still commits its")
        print("    ORIGINAL asserted receipt time, but its upper bound is looser: the")
        print("    authority's timestamp comes later than it would have on time.")
    closures = sorted({
        (o.get("receipt_anchor") or {}).get("batch_closed_at_lumra_clock")
        for o in observations
        if (o.get("receipt_anchor") or {}).get("batch_closed_at_lumra_clock")
    })
    for c in closures:
        print("  batch closed at %s (Lumra's clock; NOT covered by the" % c)
        print("    commitment, so the authority attests nothing about it)")
    if pending:
        print("  pending (committed, not yet timestamped): %d" % pending)
    if not_committed:
        print("  not committed to a batch: %d" % not_committed)
    if failed:
        print("  FAILED inclusion proofs: %d" % failed)
    if anchored:
        print()
        print("  WHAT A VERIFIED INCLUSION PROOF MEANS, precisely:")
        print("    It shows this record, INCLUDING LUMRA'S ASSERTED RECEIPT TIME, was")
        print("    in a batch whose root the authority signed. That is an UPPER BOUND:")
        print("    the asserted value existed by the authority's timestamp and cannot")
        print("    have been added or edited after it.")
        print("  WHAT IT DOES NOT MEAN:")
        print("    It does NOT independently confirm the record arrived at that")
        print("    asserted time. Lumra writes the receipt time before sealing, so a")
        print("    wrong one would be anchored just as faithfully. The anchor freezes")
        print("    the claim; it does not corroborate it.")
        print("    It does NOT show when the physical event occurred.")

    # --- 4. timestamp tokens over the batch roots ---------------------------
    print()
    print("Step 4: independent timestamps over batch roots")
    roots_seen = {}
    for obs in observations:
        ra = obs.get("receipt_anchor") or {}
        if ra.get("status") == "anchored" and ra.get("merkle_root"):
            roots_seen[ra["merkle_root"]] = ra.get("anchor")
    if not roots_seen:
        print("  (no anchored batch roots in this package)")
    else:
        for root in roots_seen:
            status, detail = verify_rfc3161_token(roots_seen[root] or {}, root, TSA_CA_PATH)
            if status == TSA_VERIFIED:
                # Name the root that actually validated. Customer-supplied trust
                # material is tried first but the documented bundle is tried
                # after it, so "verified" without naming the root lets someone
                # believe THEIR root vouched for this when the bundled one did.
                print("  OK: root %s... %s" % (root[:16], detail))
                if "lumra-tsa-bundle" in detail:
                    print("      NOTE: validated against the trust bundle SHIPPED WITH THIS")
                    print("      VERIFIER, not against a root you supplied. Supply the")
                    print("      authority's own CA with --tsa-ca to check it independently.")
            elif status == TSA_INDETERMINATE:
                incomplete.append("timestamp for root %s...: %s" % (root[:16], detail))
                print("  INCOMPLETE: %s" % detail)
                anchor_obj = roots_seen[root] or {}
                if anchor_obj.get("stub"):
                    print("  THIS IS A STUB ANCHOR, not a real authority signature.")
                    print("  The package is a constructed fixture. It establishes nothing")
                    print("  about time and must not be cited as evidence of live behaviour.")
            else:
                timestamp_findings.append("root %s...: %s" % (root[:16], detail))
                print("  FAIL: %s" % detail)

    eu_profile = bundle.get("eu_evidence_profile")
    if eu_profile is not None:
        eu_failures, eu_incomplete, eu_uncertainty = verify_eu_profile(
            bundle, eu_profile
        )
        timestamp_findings.extend(eu_failures)
        incomplete.extend(eu_incomplete)
        uncertainty.extend(eu_uncertainty)

    # --- 5. coverage ---------------------------------------------------------
    print()
    print("Step 5: coverage assessment")
    cov = bundle.get("coverage_assessment") or {}
    if not cov:
        coverage_findings.append("no coverage assessment present")
        print("  MISSING: this package makes no coverage statement at all.")
    elif not cov.get("assessable"):
        print("  NOT ASSESSABLE: %s" % cov.get("explanation", "no expectations supplied"))
        print("  Completeness is UNKNOWN. That is not the same as complete.")
    else:
        summary = cov.get("summary") or {}
        print("  sources expected:            %s" % summary.get("sources_expected"))
        print("  sources with NO data:        %s" % summary.get("sources_with_no_data"))
        print("  sources with gaps:           %s" % summary.get("sources_with_gaps"))
        print("  no defensible denominator:   %s" % summary.get("sources_without_defensible_denominator"))
        print("  observations received:       %s" % summary.get("observations_received"))
        print("  late arrivals:               %s" % summary.get("late_arrivals"))
        print("  identity conflicts:          %s" % summary.get("identity_conflicts"))
        for profile in cov.get("expectation_profiles") or []:
            print("  expectations v%s supplied by %s at %s" % (
                profile.get("version"), profile.get("supplied_by"),
                profile.get("supplied_at")))
        stmt = cov.get("statement", "")
        if "All relevant events were captured" in stmt:
            coverage_findings.append("package makes an unsupportable completeness claim")
            print("  FAIL: this package claims all relevant events were captured.")
        else:
            print("  %s" % stmt)

    # --- 6. amendments -------------------------------------------------------
    print()
    print("Step 6: amendment chain")
    supersedes = bundle.get("supersedes")
    chain = bundle.get("amendment_chain") or []
    if supersedes:
        sd = str(supersedes.get("package_sha256") or "").lower()
        if len(sd) != 64 or any(c not in "0123456789abcdef" for c in sd):
            integrity.append("supersedes reference is not a valid SHA-256 digest")
            print("  FAIL: this package claims to supersede something that cannot "
                  "be a package digest.")
        print("  This package SUPERSEDES %s..." % (sd[:16] or "?"))
        print("  Reason: %s" % supersedes.get("reason"))
        print("  The superseded package was not altered and remains valid as of its")
        print("  own export time.")
    if chain:
        print("  %d recorded amendment(s) for this case." % len(chain))
        # Validate rather than merely display. An unchecked chain is decoration.
        seqs = [a.get("sequence") for a in chain]
        if seqs != sorted(seqs) or len(set(seqs)) != len(seqs):
            integrity.append("amendment sequence is not strictly ordered: %s" % seqs)
            print("  FAIL: amendment sequence numbers are out of order or repeated.")
        elif seqs and seqs != list(range(1, len(seqs) + 1)):
            integrity.append("amendment sequence has gaps: %s" % seqs)
            print("  FAIL: amendment sequence has gaps, so an entry is missing.")
        for a in chain:
            d = (a.get("supersedes_package_sha256") or "").lower()
            if len(d) != 64 or any(c not in "0123456789abcdef" for c in d):
                integrity.append(
                    "amendment %s does not reference a valid SHA-256 digest"
                    % a.get("sequence")
                )
                print("  FAIL: amendment %s references something that cannot be a "
                      "package digest." % a.get("sequence"))
        print("  Note: the chain records BACKWARD links only. A superseding")
        print("  package's own digest covers the chain, so it cannot contain its")
        print("  own digest; the forward link is deliberately absent, not missing.")
    if not supersedes and not chain:
        print("  No amendments. This is the original assessment for this case.")

    # --- verdict -------------------------------------------------------------
    print()
    if integrity or timestamp_findings or coverage_findings:
        print("RESULT: CASE PACKAGE FAILED VERIFICATION.")
        for f in integrity:
            print("  [integrity] %s" % f)
        for f in timestamp_findings:
            print("  [timestamp] %s" % f)
        for f in coverage_findings:
            print("  [coverage]  %s" % f)
        for f in not_confirmed:
            print("  [attribution] %s" % f)
        for f in incomplete:
            print("  [incomplete, not a failure] %s" % f)
        for f in uncertainty:
            print("  [uncertainty] %s" % f)
        return 1

    if not_confirmed:
        print("RESULT: NOT CONFIRMED AS A PRIVINET RECORD.")
        print("The document may be internally consistent, but it is not attributable")
        print("to PriviNet by anything this verifier can check independently.")
        for f in not_confirmed:
            print("  [attribution] %s" % f)
        for f in incomplete:
            print("  [incomplete]  %s" % f)
        for f in uncertainty:
            print("  [uncertainty] %s" % f)
        print()
        print("Do NOT rely on this as a PriviNet record. A self-consistent document")
        print("signed by an unrecognized key is exactly what a forgery looks like.")
        return 3

    if incomplete:
        print("RESULT: INTEGRITY VERIFIED; SOME CHECKS COULD NOT BE COMPLETED.")
        for f in incomplete:
            print("  [incomplete] %s" % f)
        for f in uncertainty:
            print("  [uncertainty] %s" % f)
        print("An incomplete check is NOT a pass. Supply --tsa-ca or install OpenSSL")
        print("to complete timestamp verification.")
        return 3

    if used_supplied_trust:
        key, label = used_supplied_trust[0]
        print("RESULT: CASE PACKAGE VERIFIED UNDER SUPPLIED TRUST.")
        print("Signed by: %s" % label)
        print("Key:       %s" % key)
        print("Every check passed against trust material YOU supplied with --trust-keys.")
        print("This is NOT confirmation that PriviNet produced this package: that key")
        print("is not one of PriviNet's production keys. Run again without --trust-keys")
        print("to see how it verifies against production trust alone.")
        print()
        print("What this does NOT establish:")
        print("  - that the records arrived when Lumra asserts they did.")
        print("  - that a sensor reported the truth.")
        print("  - that the supplied roster was complete.")
        print("  - when any physical event actually occurred.")
        for f in uncertainty:
            print("  - %s" % f)
        return 0

    print("RESULT: CASE PACKAGE VERIFIED.")
    print("The sealed content is unaltered and signed, each anchored record's inclusion")
    print("proof checks out, and the coverage statement is scoped to the roster that was")
    print("supplied. No PriviNet system was contacted to reach this conclusion.")
    print()
    print("What this does NOT establish:")
    print("  - that the records arrived when Lumra asserts they did. The external")
    print("    timestamp bounds when that ASSERTION existed, not whether it is true.")
    print("  - that a sensor reported the truth.")
    print("  - that the supplied roster was complete.")
    print("  - when any physical event actually occurred.")
    for f in uncertainty:
        print("  - %s" % f)
    return 0


if __name__ == "__main__":
    args = sys.argv[1:]
    if args == ["--version"]:
        print("lumra-verifier %s" % VERIFIER_VERSION)
        sys.exit(0)
    ca = []
    while "--tsa-ca" in args:
        i = args.index("--tsa-ca")
        if i + 1 >= len(args):
            print("--tsa-ca needs a path to the timestamp authority's CA certificate")
            sys.exit(2)
        ca.append(args[i + 1])
        del args[i:i + 2]
    trust_files = []
    while "--trust-keys" in args:
        i = args.index("--trust-keys")
        if i + 1 >= len(args):
            print("--trust-keys needs a path to a trust file")
            sys.exit(2)
        trust_files.append(args[i + 1])
        del args[i:i + 2]
    eu_trust_files = []
    while "--eu-trusted-list" in args:
        i = args.index("--eu-trusted-list")
        if i + 1 >= len(args):
            print("--eu-trusted-list needs a path to an independently supplied snapshot")
            sys.exit(2)
        eu_trust_files.append(args[i + 1])
        del args[i:i + 2]
    if len(eu_trust_files) > 1:
        print("--eu-trusted-list may be supplied once")
        sys.exit(2)
    if len(args) != 1:
        print(__doc__)
        print("usage: verify_proofpack.py <pack.json> [--tsa-ca <cacert.pem>] "
              "[--trust-keys <trust.json>] "
              "[--eu-trusted-list <snapshot.json>]")
        sys.exit(2)
    TSA_CA_PATH = resolve_tsa_ca(ca)
    EU_TRUST_LIST_PATH = eu_trust_files[0] if eu_trust_files else None
    load_supplied_trust(trust_files)
    sys.exit(main(args[0]))
