#!/usr/bin/env python3 """Verify CAIN42_FINAL_PUBLIC_CLAIMS.json without trusting any CAIN-42 website. No CAIN imports; needs Python 3.8+ and `cryptography`. python3 verify_claims.py https://clawx.click/evidence/claims/CAIN42_FINAL_PUBLIC_CLAIMS.json \\ --pin # compare with the key published on the other two sites Checks: REGISTRY the registry digest re-hashes and is signed by the pinned evidence-root key (without --pin, the key is fetched from all three sites and must be identical) ARTIFACTS every artifact is fetched from the chosen site (or --base) and re-hashed: VALID / TAMPERED / UNREACHABLE, with what was expected and what was found STATUS a claim's status/level must be consistent (NOT_IMPLEMENTED => level 0 and no artifacts; level >= 3 => at least one artifact) A claim is VALID only if the registry is valid and all its artifacts match. """ from __future__ import annotations import base64 import hashlib import json import sys import urllib.request from typing import Any, Dict, List from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey SITES = {"cainstudio.online": "https://cainstudio.online/proof/bundle/", "mcpgate.online": "https://mcpgate.online/proof/bundle/", "clawx.click": "https://clawx.click/evidence/"} def fetch(url: str) -> bytes: if url.startswith("http"): with urllib.request.urlopen(url, timeout=30) as r: return r.read() with open(url, "rb") as f: return f.read() def canon(o) -> bytes: return json.dumps(o, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode() def verify(reg_src: str, pin: str = None, base: str = None) -> Dict[str, Any]: reg = json.loads(fetch(reg_src)) out = {"registry": {}, "claims": []} body = {k: v for k, v in reg.items() if k not in ("registry_digest", "signature_b64")} digest = hashlib.sha256(canon(body)).hexdigest() keys = {} if pin is None: for site, root in SITES.items(): try: keys[site] = json.loads(fetch(root + "claims/evidence-root.pub.json"))["public_key_b64"] except Exception as e: # noqa: BLE001 keys[site] = f"UNREACHABLE: {e}" pin = keys.get("clawx.click") reasons = [] if pin is None or (keys and len(set(keys.values())) != 1): reasons.append(f"evidence-root key differs between sites or is unreachable: {keys}") if reg.get("evidence_root_public_key_b64") != pin: reasons.append("registry names a different evidence-root key than the pinned one") if digest != reg.get("registry_digest"): reasons.append("registry content does not match its digest (edited after signing)") try: Ed25519PublicKey.from_public_bytes(base64.b64decode(pin)).verify(base64.b64decode(reg["signature_b64"]), digest.encode()) except Exception: reasons.append("signature does not verify against the pinned evidence-root key") out["registry"] = {"result": "VALID" if not reasons else "INVALID", "why": reasons, "digest": digest, "pinned_key": pin, "keys_seen": keys} base = base or reg_src.rsplit("/claims/", 1)[0] + "/" for c in reg.get("claims", []): rows, bad = [], [] for a in c["artifacts"]: url = base + a["bundle"] + "/" + a["file"] try: got = hashlib.sha256(fetch(url)).hexdigest() ok = got == a["sha256"] rows.append({"file": f"{a['bundle']}/{a['file']}", "result": "VALID" if ok else "TAMPERED", "expected": a["sha256"], "found": got}) if not ok: bad.append(f"{a['file']}: expected sha256 {a['sha256'][:16]}..., found {got[:16]}...") except Exception as e: # noqa: BLE001 rows.append({"file": f"{a['bundle']}/{a['file']}", "result": "UNREACHABLE", "error": str(e)[:120]}) bad.append(f"{a['file']}: unreachable") if c["status"] == "NOT_IMPLEMENTED" and (c["evidence_level"] != 0 or c["artifacts"]): bad.append("NOT_IMPLEMENTED claim carries evidence level or artifacts") if c["evidence_level"] >= 3 and not c["artifacts"]: bad.append(f"evidence level {c['evidence_level']} without artifacts") result = "VALID" if not bad and out["registry"]["result"] == "VALID" else ( "TAMPERED" if any("expected sha256" in b for b in bad) else "INVALID") out["claims"].append({"claim_id": c["claim_id"], "status": c["status"], "evidence_level": c["evidence_level"], "result": result, "why": bad, "artifacts": rows, "limits": c["limits"]}) out["final"] = "VALID" if out["registry"]["result"] == "VALID" and all(x["result"] == "VALID" for x in out["claims"]) \ else "INVALID" return out def main(argv: List[str]) -> int: if len(argv) < 2: print(__doc__) return 2 pin = argv[argv.index("--pin") + 1] if "--pin" in argv else None base = argv[argv.index("--base") + 1] if "--base" in argv else None r = verify(argv[1], pin, base) if "--json" in argv: print(json.dumps(r, indent=1)) return 0 if r["final"] == "VALID" else 1 print(f"REGISTRY: {r['registry']['result']} (digest {r['registry']['digest'][:16]}...)") for w in r["registry"]["why"]: print(f" - {w}") for c in r["claims"]: print(f"{c['result']:9s} {c['claim_id']:34s} {c['status']:16s} level {c['evidence_level']}") for w in c["why"]: print(f" - {w}") print(f"\nFINAL RESULT: {r['final']}") return 0 if r["final"] == "VALID" else 1 if __name__ == "__main__": sys.exit(main(sys.argv))