#!/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())