#!/usr/bin/env python3 """certiv-kernel — the file that DEFINES receipt validity, small enough to read. Verifies a receipt.certiv with NOTHING but the Python standard library: python3 certiv-kernel.py structural + hash verification (safe) python3 certiv-kernel.py --execute ALSO RUNS the receipt's commands !! SECURITY !! --execute runs author-declared shell commands on THIS machine with no isolation. NEVER run --execute on a receipt you do not trust. Use a disposable VM, or let a certiv verification node run it in its sandbox (network-isolated, non-root, read-only inputs). Without --execute the kernel only reads and hashes files under ; it still refuses any path that escapes (absolute, .., symlink, or non-regular file). This is a deliberately independent implementation of the receipt rules (04_RECEIPTS): it shares no code with the certiv tool, so agreement between the two is evidence, not tautology. If this file and the big tool ever disagree, believe this file, then file a bug. Exit code 0 = every check passed. Anything else = the receipt names what broke. """ import hashlib import json import os import subprocess import sys from pathlib import Path from stat import S_ISREG def safe_path(base, rel, must_be_file=True): """Resolve rel under base, refusing escapes and non-regular files. Returns the path, or None if unsafe/missing (caller fails closed).""" base_real = Path(base).resolve() p = Path(rel) if p.is_absolute() or ".." in p.parts or any(x == "" for x in p.parts): return None full = (base_real / p).resolve() if full != base_real and base_real not in full.parents: return None if must_be_file: if full.is_symlink() or not full.exists(): return None st = os.stat(full, follow_symlinks=False) if not S_ISREG(st.st_mode) or not os.access(full, os.R_OK): return None return full SCHEMA = "certiv_receipt_v1" NODE_ONLY = ("verification_result", "verified_at", "node_id") ENV = {"PATH": "/usr/local/bin:/usr/bin:/bin", "HOME": "/tmp", "LANG": "C.UTF-8", "LC_ALL": "C.UTF-8", "TZ": "UTC", "PYTHONHASHSEED": "0", "SOURCE_DATE_EPOCH": "0"} def sha_text(text): return hashlib.sha256(text.encode("utf-8")).hexdigest() def sha_file(path): h = hashlib.sha256() with open(path, "rb") as f: for chunk in iter(lambda: f.read(1 << 20), b""): h.update(chunk) return h.hexdigest() def sha_body(payload, field): body = {k: v for k, v in payload.items() if k != field} return sha_text(json.dumps(body, sort_keys=True, separators=(",", ":"))) def merkle(hashes): level = sorted(hashes) if not level: return sha_text("") while len(level) > 1: nxt = [sha_text(level[i] + level[i + 1]) for i in range(0, len(level) - 1, 2)] if len(level) % 2 == 1: nxt.append(level[-1]) level = nxt return level[0] def run(command, cwd): proc = subprocess.run(["/bin/sh", "-c", command], cwd=str(cwd), env=dict(ENV), capture_output=True, timeout=600) return proc.returncode, proc.stdout def check_execution(claim, cwd, problems): cid = claim.get("claim_id") if claim.get("kind") == "certificate": cert = claim.get("certificate", {}) cpath = safe_path(cwd, cert.get("path", "")) if cpath is None: problems.append("unsafe_certificate:%s" % cid) return if sha_file(cpath) != cert.get("sha256"): problems.append("certificate_hash_drift:%s" % cid) return code, _ = run(cert.get("checker", "false"), cwd) if code != 0: problems.append("checker_failed:%s" % cid) return code, stdout = run(claim.get("command", "false"), cwd) if code != 0: problems.append("exit_%s:%s" % (code, cid)) return expect = claim.get("expect", {}) if "value" in expect: observed = stdout.decode("utf-8", "replace").strip() declared = str(expect["value"]).strip() if "tolerance" in expect: ok = abs(float(observed) - float(declared)) <= float(expect["tolerance"]) else: ok = observed == declared if not ok: problems.append("value_mismatch:%s:got_%s" % (cid, observed[:40])) elif "stdout_sha256" in expect: if hashlib.sha256(stdout).hexdigest() != expect["stdout_sha256"]: problems.append("stdout_hash_mismatch:%s" % cid) else: out = safe_path(cwd, expect.get("output_file", "")) if out is None or sha_file(out) != expect.get("output_sha256"): problems.append("output_hash_mismatch:%s" % cid) def verify(directory, execute=False): cwd = Path(directory) problems = [] path = cwd / "receipt.certiv" if not path.is_file(): return ["receipt_missing"] r = json.loads(path.read_text(encoding="utf-8")) if r.get("schema") != SCHEMA: problems.append("schema_invalid") for field in NODE_ONLY: if field in r: problems.append("author_receipt_carries_node_field:%s" % field) if sha_body(r, "receipt_sha256") != r.get("receipt_sha256"): problems.append("receipt_sha256_mismatch") claims = r.get("claims", []) shas = [] for c in claims: cid = c.get("claim_id", "?") declared = c.get("claim_sha256", "") shas.append(declared) if sha_body(c, "claim_sha256") != declared: problems.append("claim_sha256_mismatch:%s" % cid) for field in NODE_ONLY: if field in c: problems.append("claim_carries_node_field:%s" % cid) for pin in c.get("inputs", []): p = safe_path(cwd, pin["path"]) if p is None: problems.append("unsafe_input:%s:%s" % (cid, pin["path"])) elif sha_file(p) != pin["sha256"]: problems.append("input_hash_drift:%s:%s" % (cid, pin["path"])) if merkle(shas) != r.get("claims_merkle_root"): problems.append("claims_merkle_root_mismatch") cov = r.get("coverage", {}) verified = [c for c in claims if c.get("weight") == "load_bearing" and c.get("status") == "verified_local"] total = [c for c in claims if c.get("weight") == "load_bearing"] if cov.get("load_bearing_verified_local") != len(verified) or \ cov.get("load_bearing_total") != len(total): problems.append("coverage_misstated") if execute and not problems: for c in claims: if c.get("status") == "verified_local": check_execution(c, cwd, problems) return problems def main(): args = [a for a in sys.argv[1:] if not a.startswith("--")] execute = "--execute" in sys.argv directory = args[0] if args else "." if execute: sys.stderr.write( "WARNING: --execute runs the receipt's shell commands on this machine " "with no isolation. Only do this for receipts you trust.\n") problems = verify(directory, execute=execute) mode = "hashes+structure+execution" if execute else "hashes+structure" if problems: for p in problems: print("KERNEL FAIL %s" % p) print("kernel verdict: NOT CONFIRMED (%s)" % mode) return 1 print("kernel verdict: receipt CONFIRMED (%s)" % mode) return 0 if __name__ == "__main__": sys.exit(main())