#!/usr/bin/env python3 """ Standalone Counterfoil receipt verifier — the whole point of the receipt. Give this ONE file plus a receipt to anyone. It re-verifies the receipt fully offline, with no secret, no network, and no Matrix CR Studio code: a buyer or an auditor checks the evidence WITHOUT trusting the issuer. That is the difference between attestation and observability — you verify it yourself. Only dependency: `cryptography` (pip install cryptography). Everything else is the Python standard library. python verify_counterfoil.py receipt.json python verify_counterfoil.py receipt.json --expect-key Exit code 0 = PASS, 1 = FAIL. Checks (all offline): 1. integrity — SHA3-256 hash chain recomputes, links, and closes on seed_anchor 2. claim — the presented claim hashes to the sealed SEED payload 3. authenticity— Ed25519 seal verifies over the terminal record_hash Optional identity pinning binds the seal to a known key fingerprint. """ import hashlib import json import sys from typing import Any, Dict, List, Tuple try: from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey from cryptography.exceptions import InvalidSignature except Exception: # pragma: no cover sys.stderr.write("needs the 'cryptography' package: pip install cryptography\n") raise SystemExit(2) def _canon(obj: Any) -> bytes: return json.dumps(obj, sort_keys=True, separators=(",", ":"), default=str).encode() def _payload_hash(payload: Any) -> str: return hashlib.sha3_256(_canon(payload)).hexdigest() def _record_hash(r: Dict[str, Any]) -> str: material = f"{r['seq']}|{r['stage']}|{r['ts_ms']}|{r['payload_hash']}|{r['prev_hash']}" return hashlib.sha3_256(material.encode()).hexdigest() def _key_fingerprint(public_raw: bytes) -> str: return hashlib.sha3_256(public_raw).hexdigest()[:16] def _verify_chain(chain: List[Dict[str, Any]], seed_anchor: Any) -> bool: if not chain: return False prev = "" for r in chain: if r.get("record_hash") != _record_hash(r): return False if r.get("prev_hash") != prev: return False prev = r["record_hash"] for i in range(len(chain) - 1, 0, -1): if chain[i]["prev_hash"] != _record_hash(chain[i - 1]): return False if chain[0]["prev_hash"] != "": return False if seed_anchor is not None and chain[0]["payload_hash"] != seed_anchor: return False return True def verify(receipt: Dict[str, Any], expect_key: str = "") -> Tuple[bool, Dict[str, Any]]: reasons: List[str] = [] chain = receipt.get("chain") or [] integrity = _verify_chain(chain, receipt.get("seed_anchor")) if not integrity: reasons.append("integrity: hash chain failed (tamper, reorder, or bad closure)") claim_ok = bool(chain) and _payload_hash(receipt.get("claim", {})) == chain[0]["payload_hash"] if not claim_ok: reasons.append("claim: presented claim does not match the sealed seed payload_hash") seal = receipt.get("seal", {}) auth_ok = False fp = None if not chain: reasons.append("authenticity: no chain to seal") elif seal.get("alg") != "ed25519": reasons.append(f"authenticity: unsupported seal alg {seal.get('alg')!r}") else: try: pub_raw = bytes.fromhex(seal["public_key"]) fp = _key_fingerprint(pub_raw) terminal = chain[-1]["record_hash"] if seal.get("key_fingerprint") != fp: reasons.append("authenticity: key_fingerprint != public_key") elif seal.get("sealed_value") != terminal: reasons.append("authenticity: sealed_value != terminal record_hash") else: Ed25519PublicKey.from_public_bytes(pub_raw).verify( bytes.fromhex(seal["signature"]), terminal.encode()) auth_ok = True except InvalidSignature: reasons.append("authenticity: ed25519 signature invalid") except (KeyError, ValueError) as e: reasons.append(f"authenticity: malformed seal ({e})") identity_ok = True if expect_key: identity_ok = fp == expect_key if not identity_ok: reasons.append(f"identity: sealed key {fp} != expected {expect_key}") if receipt.get("hardware_seal") is not None: reasons.append("note: hardware_seal present — not checked by this offline core verifier") valid = integrity and claim_ok and auth_ok and identity_ok return valid, {"valid": valid, "integrity_ok": integrity, "claim_binding_ok": claim_ok, "authenticity_ok": auth_ok, "identity_ok": identity_ok, "key_fingerprint": fp, "reasons": reasons} def _main(argv: List[str]) -> int: args = [a for a in argv if not a.startswith("--")] expect = "" for i, a in enumerate(argv): if a == "--expect-key" and i + 1 < len(argv): expect = argv[i + 1] if not args: sys.stderr.write("usage: verify_counterfoil.py [--expect-key ]\n") return 2 receipt = json.loads(open(args[0], encoding="utf-8").read()) ok, verdict = verify(receipt, expect_key=expect) print(json.dumps(verdict, indent=2)) print("PASS" if ok else "FAIL", file=sys.stderr) return 0 if ok else 1 if __name__ == "__main__": raise SystemExit(_main(sys.argv[1:]))