#!/usr/bin/env python3
"""Verify the IntelXview ACP public demonstration evidence pack.

Run it against the live files, with no account and no repository access:

    pip install cryptography
    python3 verify.py --url https://www.intelxview.com/evidence/acp-ed25519-pack

Or against local copies:

    python3 verify.py

WHAT IT CHECKS
  1. The key file's declared identity: key id, algorithm, scope and the
     not-production flag. Any unexpected value is a hard failure — this
     verifier is specific to the ev-demo-k1 demonstration pack.
  2. Every record's Ed25519 signature, against the published public key
  3. The hash chain, recomputed from the payloads
  4. The Ed25519 signed chain head, which detects truncation
  5. That the declared record counts match the records actually carried

This script performs POSITIVE verification only. The corrupted-pack cases —
modified records, broken chains, truncated tails, tampered counts,
substituted or re-signed keys — are exercised separately in the publisher's
CI, by a harness that builds genuinely corrupted packs and requires this
script to reject each one.

WHAT A PASS MEANS, AND WHAT IT DOES NOT
  A pass shows these records were signed by the holder of the ev-demo-k1
  private key and have not been altered since.

  It does NOT show that IntelXview Limited is that holder. The key is
  published by us, so matching it establishes key control, not legal identity.
  It does NOT prove custody of the production evidence key: this pack is
  signed with a DEMONSTRATION key whose only scope is public sample packs.
  And the export envelope is HMAC-SHA256 — symmetric — so it is not checked
  here and cannot be checked by you.
"""

import argparse
import hashlib
import json
import sys
import urllib.request

try:
    from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
    from cryptography.exceptions import InvalidSignature
except ImportError:  # pragma: no cover
    sys.exit("pip install cryptography")

GENESIS = "0" * 64
LEGACY_ENCODING_KEY_IDS = frozenset({"ev-k1"})

#: This verifier is for the public demonstration pack ONLY. If the key file
#: does not declare exactly this identity, the pack is not the one this
#: script exists to check, and the only safe verdict is failure. A note
#: would let a pack re-signed end-to-end under another key print
#: "all checks passed" against a key nobody vetted against the anchor.
EXPECTED_KEY_ID = "ev-demo-k1"
EXPECTED_ALG = "ed25519"
EXPECTED_SCOPE = "PUBLIC SAMPLE PACKS ONLY - never customer or production evidence"


def canonical(value: dict, signer_key_id: str) -> bytes:
    """The ACP canonical encoding, selected by the record's own key id.

    Canonicalisation is epoch-selected: only enumerated legacy key ids use
    ASCII escapes. Following a fixed rule instead of the record's
    `signer_key_id` will produce the wrong bytes for one epoch or the other.
    """
    if not signer_key_id:
        raise ValueError("signer_key_id is required to select an encoding epoch")
    return json.dumps(
        value,
        sort_keys=True,
        separators=(",", ":"),
        ensure_ascii=signer_key_id in LEGACY_ENCODING_KEY_IDS,
        allow_nan=False,
    ).encode("utf-8")


def payload_hash(parent_hash: str, payload: dict, signer_key_id: str) -> str:
    return hashlib.sha256(
        parent_hash.encode("utf-8") + canonical(payload, signer_key_id)
    ).hexdigest()


def load(source: str, name: str) -> dict:
    if source.startswith("http"):
        with urllib.request.urlopen(f"{source.rstrip('/')}/{name}") as r:
            return json.load(r)
    with open(name, "rb") as f:
        return json.load(f)


def check(label: str, ok: bool, detail: str = "") -> bool:
    print(f"  {'PASS' if ok else 'FAIL'}  {label}{(' — ' + detail) if detail else ''}")
    return ok


def verify_key_identity(bundle: dict, keyfile: dict) -> bool:
    """Hard-fail unless the pack declares exactly the demonstration identity.

    A wrong key id, algorithm, scope or production flag is not a warning: a
    pack re-signed end-to-end under another key is internally consistent and
    would pass every cryptographic check below.
    """
    ok = check("key id is the demonstration key",
               keyfile.get("key_id") == EXPECTED_KEY_ID,
               f"declared {keyfile.get('key_id')!r}")
    ok &= check("algorithm is ed25519", keyfile.get("alg") == EXPECTED_ALG,
                f"declared {keyfile.get('alg')!r}")
    ok &= check("scope is public sample packs only",
                keyfile.get("scope") == EXPECTED_SCOPE)
    ok &= check("key file declares not_production_key",
                keyfile.get("not_production_key") is True)
    signers = {row.get("signer_key_id") for row in bundle["rows"]}
    signers.add(bundle["head"].get("signer_key_id"))
    ok &= check("every record and the head declare the demonstration signer",
                signers == {EXPECTED_KEY_ID},
                f"found {sorted(str(s) for s in signers)}")
    return ok


def verify_records(bundle: dict, pub: Ed25519PublicKey) -> bool:
    ok = True
    for row in sorted(bundle["rows"], key=lambda r: r["chain_seq"]):
        try:
            pub.verify(bytes.fromhex(row["signature"]), row["payload_hash"].encode("utf-8"))
            sig_ok = True
        except InvalidSignature:
            sig_ok = False
        ok &= check(f"record {row['chain_seq']} {row['action']} signature", sig_ok)
    return ok


def verify_chain(bundle: dict) -> bool:
    ok = True
    parent = GENESIS
    for row in sorted(bundle["rows"], key=lambda r: r["chain_seq"]):
        expected = payload_hash(parent, row["payload"], row["signer_key_id"])
        ok &= check(
            f"record {row['chain_seq']} hash recomputes",
            expected == row["payload_hash"],
        )
        parent = row["payload_hash"]
    return ok


def verify_head(bundle: dict, pub: Ed25519PublicKey) -> bool:
    head = bundle["head"]
    body = {k: v for k, v in head.items() if k != "signature"}
    try:
        pub.verify(bytes.fromhex(head["signature"]), canonical(body, head["signer_key_id"]))
        sig_ok = True
    except InvalidSignature:
        sig_ok = False
    ok = check("signed chain head signature", sig_ok)

    rows = sorted(bundle["rows"], key=lambda r: r["chain_seq"])
    ok &= check("chain head row_count matches", head["row_count"] == len(rows),
                f"head says {head['row_count']}, pack carries {len(rows)}")
    ok &= check("chain head tip matches last record",
                head["tip_payload_hash"] == rows[-1]["payload_hash"]
                and head["tip_chain_seq"] == rows[-1]["chain_seq"])
    # The top-level row_count is OUTSIDE the signed head. Unchecked, editing
    # that one number would change what the pack claims while every
    # cryptographic check still passed.
    ok &= check("top-level row_count matches records carried",
                bundle["row_count"] == len(rows),
                f"bundle says {bundle['row_count']}, pack carries {len(rows)}")
    return ok


def verify_all(bundle: dict, keyfile: dict) -> bool:
    """Every positive check, as one verdict. This is what a variant must fail."""
    pub = Ed25519PublicKey.from_public_bytes(bytes.fromhex(keyfile["public"]))
    return (verify_key_identity(bundle, keyfile)
            and verify_records(bundle, pub)
            and verify_chain(bundle)
            and verify_head(bundle, pub))


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--url", default="", help="base URL of the published pack")
    args = ap.parse_args()
    src = args.url or "."

    bundle = load(src, "evidence-bundle.json")
    keyfile = load(src, "verification-key.json")

    print(f"pack tenant   : {bundle['tenant_id']}")
    print(f"records       : {bundle['row_count']}")
    print(f"signer key id : {keyfile['key_id']}  ({keyfile['alg']})")
    print(f"envelope      : {bundle['signature']['algorithm']}  <- NOT verifiable by you")
    print(f"completeness  : {bundle['completeness']['basis']}")
    print()

    pub = Ed25519PublicKey.from_public_bytes(bytes.fromhex(keyfile["public"]))

    spki = bytes.fromhex("302a300506032b6570032100" + keyfile["public"])
    print(f"SPKI SHA-256 fingerprint: {hashlib.sha256(spki).hexdigest()}")
    print("Compare it with the 'Demonstration keys' section of the trust anchor:")
    print("  https://github.com/intelxview/acp-trust-anchor\n")

    print("IDENTITY — pinned demonstration key"); ok = verify_key_identity(bundle, keyfile)
    print("POSITIVE — record signatures");        ok &= verify_records(bundle, pub)
    print("POSITIVE — hash chain");               ok &= verify_chain(bundle)
    print("POSITIVE — signed chain head");        ok &= verify_head(bundle, pub)

    print()
    print("RESULT:", "all checks passed" if ok else "FAILED")
    print()
    print("A pass shows these records were signed by the holder of the ev-demo-k1")
    print("private key and are unaltered. It does NOT establish IntelXview Limited's")
    print("legal identity, and it does NOT demonstrate production-key custody: this")
    print("is a demonstration key, scoped to public sample packs only.")
    return 0 if ok else 1


if __name__ == "__main__":
    raise SystemExit(main())
