#!/usr/bin/env python3
"""Check a pflow-ontology pack without trusting the server that sent it.

    python3 -I verify-pack.py pack.json

Recomputes, from the pack's own bytes, the three digests it claims, using only
the Python standard library. Exit status 0 means every digest matched.

  sha256          SHA-256 of the `graph` text (sorted N-Triples, UTF-8)
  content_sha256  SHA-256 over a length-prefixed framing of the graph AND the
                  certificate, so a certificate cannot be swapped for one that
                  is sound over different assertions
  asserted.sha256 SHA-256 of the certificate's asserted.tsv

Matching digests show the file is intact and its parts belong together. They do
not show the ontology is right; the certificate can be re-checked separately
with the Lean checker `oo-cert` (see the README of open-ontologies).
"""
import hashlib
import json
import struct
import sys


def le(n):
    return struct.pack("<Q", n)


def framed(s):
    b = s.encode("utf-8")
    return le(len(b)) + b


def content_sha256(graph, cert):
    h = hashlib.sha256()
    h.update(b"oo-pack-content/1\n")
    h.update(framed(graph))
    if cert is None:
        h.update(le(0))
    else:
        h.update(le(1))
        h.update(framed(cert["format"]))
        h.update(framed(cert.get("profile_claimed_by_sender") or ""))
        h.update(le(cert["asserted"]))
        h.update(le(cert["derivations"]))
        h.update(le(len(cert["files"])))
        for name in sorted(cert["files"]):  # byte order, as the engine's BTreeMap
            h.update(framed(name))
            h.update(framed(cert["files"][name]))
    return h.hexdigest()


def main(path):
    pack = json.load(open(path, encoding="utf-8"))
    m, graph = pack["manifest"], pack["graph"]
    cert = m.get("certificate")
    checks = [("sha256 (graph)", hashlib.sha256(graph.encode("utf-8")).hexdigest(), m["sha256"]),
              ("content_sha256 (graph + certificate)", content_sha256(graph, cert), m.get("content_sha256"))]
    if cert:
        files = cert["files"]
        want = hashlib.sha256(files["asserted.tsv"].encode("utf-8")).hexdigest()
        checks.append(("asserted.sha256 (asserted.tsv)", want, files["asserted.sha256"].strip()))
    bad = 0
    for name, got, claimed in checks:
        ok = got == claimed
        bad += not ok
        print(f"{'ok  ' if ok else 'FAIL'} {name}\n       {got}" + ("" if ok else f"\n       pack claims {claimed}"))
    print(f"{m['name']} {m['version']}: {m['triples']} triples")
    return 1 if bad else 0


if __name__ == "__main__":
    if len(sys.argv) != 2:
        sys.exit(__doc__)
    sys.exit(main(sys.argv[1]))
