#!/usr/bin/env python3 """Second verifier for claim-conformance/ — typed claim receipts, the soft binding class, and recovery under the ancestor rule (extensions/jwp-receipts.md §2.4.3-§2.4.12, §2.6, §3.1-§3.2), and the arithmetic of a measurement report (energy-measurement.md §3.1, §6.2), computed with Python's integers. Written from the extension text. It shares no code with wai-rs (`crate::claim_receipt`), which generates the corpus: the canonical form is Python's JSON serialiser with members ordered by UTF-16 code units, BLAKE3 is written from its specification (`_blake3.py`), Ed25519 is WAI's strict rule written from RFC 8032 (`_ed25519.py`, SPEC §7 *Signatures*; the verifiers of the other corpora call it too), and OER receipts named by an `energy` claim are read, verified and their figures read by the OER reference verifier (`../oer-conformance/oer2_verify.py`), under oer-receipts' own rules. python3 claim_verify.py [vectors.json] verify every vector and the canonical fixture python3 claim_verify.py fixture print the fixture's canonical bytes, signature and link hash python3 claim_verify.py brackets print the figure and least declaration of each bracket wai-rs pins python3 claim_verify.py verdicts print `nameverdict` for every vector of a corpus-format file, ignoring any declared outcome (the input to differential.py) Exit status 0 only if every vector reports its declared outcome, and the fixture and the bracket arithmetic this script computes equal the literals pinned in wai-rs. A corpus-format file is read as the OER reference reads a vector document (`OER.load`, oer-receipts §4 "The document"), since it holds OER receipts: a file that reading refuses has no verdicts, and `verdicts` exits 1 with no output. No integer in a claim is converted from more than twenty digits, so no verdict depends on an interpreter's limit on integer digits. """ import hashlib, json, os, re, struct, sys, unicodedata from decimal import localcontext HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.join(HERE, "..", "oer-conformance")) import oer2_verify as OER # noqa: E402 (the OER reference verifier: grades + receipt checks) sys.path.insert(0, os.path.join(HERE, "..", "staged-conformance")) import staged_verify as SV # noqa: E402 (the strict JSON and manifest reader http_verify.py uses too) sys.path.insert(0, os.path.join(HERE, "..", "staged-measure-conformance")) import measure_verify as MV # noqa: E402 (staged-measurement §3's counted bytes, written from that text) import int_payload_verify as IPV # noqa: E402 (the integer payload parsers; measure_verify puts them on the path) sys.path.insert(0, HERE) from cryptography.hazmat.primitives import serialization # noqa: E402 from cryptography.hazmat.primitives.asymmetric import ed25519 # noqa: E402 # ---------------------------------------------------------------- BLAKE3 (hash mode) from _blake3 import blake3 # noqa: E402 (written from its specification; shared with staged_verify.py) # ------------------------------------------------ Ed25519 (RFC 8032), §3.1 step 3's rule # # §3.1 step 3 is WAI's one rule for every signature (SPEC §7 *Signatures*): the key is the # canonical encoding of a point of large order, S < L, R is not of small order, and the # cofactorless equation holds with R compared byte for byte. `_ed25519.py` implements it # from RFC 8032's definitions; the `cryptography` package alone would accept keys that are # not canonical encodings and keys and R of small order. import _ed25519 as ED # noqa: E402 def check_signature(pk: bytes, sig: bytes, msg: bytes): """§3.1 step 3, in its order: the key (`bad_signer_key`), then S < L, R, and the equation (`signature_verify_failed`).""" why = ED.verify(pk, msg, sig) need(why != "bad_key", 3, "bad_signer_key") need(why is None, 3, "signature_verify_failed") # ------------------------------------------------------------- canonical form (RFC 8785) MAX_INT = 2 ** 53 - 1 def jcs(v): if v is None: return "null" if v is True or v is False: return "true" if v else "false" if isinstance(v, int): if not 0 <= v <= MAX_INT: raise ValueError("integer outside the canonical subset") return str(v) if isinstance(v, str): return json.dumps(v, ensure_ascii=False) if isinstance(v, list): return "[" + ",".join(jcs(x) for x in v) + "]" if isinstance(v, dict): keys = sorted(v, key=lambda k: k.encode("utf-16-be")) return "{" + ",".join(json.dumps(k, ensure_ascii=False) + ":" + jcs(v[k]) for k in keys) + "}" raise ValueError("value outside the canonical subset") def link_hash(claim: dict) -> str: return blake3(b"moq-jwp:claim-link\x01" + jcs(claim).encode()).hex() def signing_input(claim: dict) -> bytes: return b"moq-jwp:claim\x01" + jcs({k: v for k, v in claim.items() if k != "sig"}).encode() MAX_DEPTH = 32 # §3.1 step 1: arrays and objects nested deeper, the receipt being level 1 class OutsideSubset: """A JSON number that is not an unsigned integer written without a sign, fraction or exponent: whatever its value, not one a claim may carry (`field_type`, never `malformed`). Kept as a marker rather than a float so that no reading depends on how a float is rounded, or on whether it overflows.""" OUTSIDE_SUBSET = OutsideSubset() def _scalar_text(s): """Whether `s` is Unicode text: no surrogate code point, which is what an escape that leaves a surrogate unpaired decodes to here.""" return not any("\ud800" <= ch <= "\udfff" for ch in s) def _check_tree(v, level): if isinstance(v, str): if not _scalar_text(v): raise ValueError("an escape that leaves a surrogate unpaired") elif isinstance(v, (list, dict)): if level + 1 > MAX_DEPTH: raise ValueError("nested too deeply") for x in (v.values() if isinstance(v, dict) else v): _check_tree(x, level + 1) def parse_strict(text): """§3.1 step 1's reading of a JSON text; ValueError is `malformed`. Refuses what RFC 8259 does, a duplicate member name, a string or member name holding an unpaired surrogate, and nesting deeper than MAX_DEPTH. Never refuses a number: one outside the claim subset is OUTSIDE_SUBSET.""" def pairs(ps): d = {} for k, v in ps: if not _scalar_text(k): raise ValueError("an escape that leaves a surrogate unpaired") if k in d: raise ValueError("duplicate member " + k) d[k] = v return d def no_constant(c): raise ValueError("not JSON: " + c) def integer(s): # A plain unsigned integer beyond 20 digits exceeds every u64, as wai-rs reads it. return OUTSIDE_SUBSET if s.startswith("-") or len(s) > 20 else int(s) if not isinstance(text, str): raise TypeError("a claim is given as its wire form") try: v = json.loads(text, object_pairs_hook=pairs, parse_constant=no_constant, parse_float=lambda s: OUTSIDE_SUBSET, parse_int=integer) except RecursionError: raise ValueError("nested too deeply") _check_tree(v, 0) return v # -------------------------------------------------------------------- the extension ENVELOPE = ["kind", "class", "subject_type", "subject", "seq", "issued_at", "body", "signer_pubkey", "signer_id", "parent_receipt_hash", "sig"] ADMITS = {"energy": {"object"}, "lawful-basis": {"object", "receipt"}, "log-inclusion": {"receipt"}, "soft-binding": {"object"}, "binding-recovery": {"object"}, "measurement": {"object", "receipt"}, "operating-point": {"object", "receipt"}, "stage": {"object"}, "delivery-gap": {"object"}, "reconstruct-session": {"object"}} FIGURE = ["figure_profile", "figure_index", "group", "joules_micro", "energy_provenance", "energy_uncertainty", "energy_counter_filtering", "energy_calibration_ref"] BODY = { "energy": ["joules_micro", "energy_provenance", "energy_uncertainty", "energy_counter_filtering", "energy_calibration_ref", "coverage", "grade", "method", "work_units", "work_unit", "oer_receipt"], "log-inclusion": ["log", "profile", "entry", "log_receipt"], "soft-binding": ["alg", "registry", "binding_type", "value", "resolution_endpoint"], "binding-recovery": ["alg", "binding_type", "value", "declaration", "resolution_endpoint", "observed", "attests"], "measurement": FIGURE + ["bracket", "build", "power_reader"], "operating-point": FIGURE + ["vocabulary", "op_requested", "op_committed", "op_delivered", "prediction"], "stage": ["vocabulary", "op_requested", "op_committed", "op_delivered", "presented", "units_by_stage", "tier", "stage_hash", "objects", "conflicts", "deadline_misses", "bytes_envelopes"], "delivery-gap": ["track_alias", "group_id", "object_id", "reason", "observed_at"], "reconstruct-session": ["session_nonce", "media", "host", "decoders", "units", "leaf_count", "output_root", "primary_units", "fallback_units", "switches", "switches_total", "deadline_us", "missed", "missed_units", "stage_sets", "framing", "counted_primary", "counted_fallback", "counted_discarded", "uncounted", "transferred", "receipt_bytes", "classical_declared", "classical_source", "energy"], } # §2.4.10: one reported layer object, its units, the enumerations this extension defines # for it, the tiers of staged-delivery §5, and the reasons of staged-delivery §4.2. STAGE_OBJECT = ["layer", "object", "units", "bytes", "status", "evidence", "reason"] STAGE_UNITS = ["from", "count"] STAGE_STATUS = ("applied", "partial", "late", "dropped", "refused") STAGE_EVIDENCE = ("delivered-verified", "inclusion-verified", "none") STAGE_TIERS = ("decode-equivalence", "entropy-consistency", "none") STAGE_REASONS = ("refines-malformed", "capability-not-refinement", "base-absent", "base-via-fallback", "base-not-admitted", "refinement-unattested", "payload-malformed", "cost-over-limit", "chain-over-limit", "layer-conflict", "prev-mismatch", "layer-gap", "geometry-mismatch", "deadline-missed") STAGE_VOCABULARY = "urn:wai:staged-delivery:1" GAP_REASONS = ("delivery-timeout", "discretion", "not-received") # §2.4.11 BRACKET = ["idle", "busy", "window_us", "completions", "in_flight", "opening"] SUMMARY = ["n", "mean_uw", "sd_uw", "live_intervals", "longest_live_run"] BUILD = ["profile", "toolchain", "target"] PREDICTION = ["predictor", "descriptor_hash", "predicted_joules_micro"] # energy-measurement §6.1: profile -> (subject_type, carries several figures, an Ed25519 # key signs the figure, names a group). PROFILES = {"moq-jwp:object": ("object", True, True, True), "moq-jwp:claim-link": ("receipt", False, True, False), "wai:energy-binding-id": ("receipt", False, False, False)} for _n in ["video-receipt-id", "video-manifest-receipt-id", "video-ssai-receipt-id", "video-keys-receipt-id", "video-package-receipt-id", "video-channel-receipt-id", "world-receipt-id", "film-receipt-id", "haptic-receipt-id", "score-receipt-id", "splat4d-receipt-id", "audio-receipt-id", "ternary-receipt-id", "quantum-receipt-id", "quantum-calibration-id", "quantum-job-id", "quantum-mitigate-id", "quantum-decode-id", "quantum-compile-id", "quantum-rearrange-id", "phasor-receipt-id"]: PROFILES["wai:" + _n] = ("receipt", False, True, False) for _n in ["asset-receipt-id", "quantum-qbom-id"]: PROFILES["wai:" + _n] = ("receipt", True, True, False) DECLARATION = ["record", "vocabulary", "fragment", "purposes"] PURPOSE = ["purpose", "basis", "data"] DECISION = ["record", "vocabulary", "declaration", "purpose", "outcome"] UNCERTAINTY = ["relative_ppm", "window_us"] # energy-measurement.md §2's acquisition classes, each with the strongest oer-receipts.md # §2 grade it supports. Grades and their ranking come from the OER reference verifier. STRONGEST = {"CalibratedInstrument": "measured", "OnChipCounter": "measured", "HwShunt": "measured", "ModelBased": "derived", "Estimator": "projected"} FILTERING = {"On", "Off", "Unknown"} # energy-measurement.md §2.4 RESOLUTION_FLOOR = 288676 # energy-measurement.md §2.3: joules_micro x relative_ppm, at least # Unicode's White_Space property: what a calibration reference must hold something besides # (energy-measurement.md §2.5, "non-empty"). Written out, because str.isspace() also # counts U+001C to U+001F, which are not White_Space. WHITE_SPACE = ("\t\n\x0b\x0c\r \x85\xa0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007" "\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000") COVERAGE = {"Whole", "Partial"} GRADES = OER.GRADES # §2.4.12: a session's host, decoder entries, switches, missed presentations, stage sets # and energy, the enumerations it defines, and delivery-telemetry.md §5's switch reasons. SESSION_HOST = ["profile", "target", "build", "threads"] SESSION_DECODER = ["capability", "rendition", "object", "first_unit", "path", "decoder", "variant", "pin", "tier", "module_status", "leaves"] SESSION_SWITCH = ["unit", "from", "to", "reason"] SESSION_MISSED = ["unit", "layers", "late_us"] SESSION_STAGE_SET = ["first_unit", "claim"] SESSION_ENERGY = ["joules_micro", "energy_provenance", "energy_uncertainty", "energy_counter_filtering", "energy_calibration_ref"] HOST_PROFILES = ("native-ffi", "browser-baseline", "browser-gpu", "edge-wasi") PATHS = ("primary", "fallback") FRAMINGS = ("registered",) CLASSICAL_SOURCES = ("wai2-table",) MODULE_STATUSES = ("pinned", "fetched") TIER_RANK = {"none": 0, "entropy-consistency": 1, "decode-equivalence": 2} # SPEC §3.1 SWITCH_REASONS = ("pin-malformed", "form-not-registered", "form-unreadable", "form-required", "prior-revoked", "prior-not-authorised", "prior-not-held", "digest-superseded", "set-malformed", "part-mismatch", "set-incomplete", "missing-capability", "module-unverified", "module-revoked", "trap", "fuel-exhausted", "memory-exhausted", "deadline-threshold", "unit-hash-mismatch", "budget-exhausted", "recovered") # The capability registry (../registry/capabilities.json): each capability's kind, status # and replicate class, for a session's tiers, re-decodes and classical rendition. with open(os.path.join(HERE, "..", "registry", "capabilities.json"), encoding="utf-8") as _f: REGISTRY = {c["capability"]: c for c in json.load(_f)["capabilities"]} # The codec glossary (../glossary/codecs.json): the capabilities an entry of era `classical` # links, from which a session's classical rendition is read (§2.4.12). with open(os.path.join(HERE, "..", "glossary", "codecs.json"), encoding="utf-8") as _f: CLASSICAL = {link["capability"] for e in json.load(_f)["entries"] if e["era"] == "classical" for link in (e.get("wai") or [])} class Reject(Exception): def __init__(self, step, reason): super().__init__(f"reject step={step} reason={reason}") self.step, self.reason = step, reason def is_int(v): return isinstance(v, int) and not isinstance(v, bool) and 0 <= v <= MAX_INT def is_hex(v, nbytes=None): if not isinstance(v, str) or len(v) % 2 or not re.fullmatch(r"[0-9a-f]*", v): return False return nbytes is None or len(v) == 2 * nbytes def is_token(s): return (isinstance(s, str) and s != "" and len(s.encode()) <= 2048 and not any(c.isspace() or unicodedata.category(c) == "Cc" for c in s)) def is_uri(s): return is_token(s) and re.fullmatch(r"[A-Za-z][A-Za-z0-9+.\-]*:.+", s, re.S) is not None def is_https(s): """§2.6.2: `https://`, then an authority (up to the first `/`, `?` or `#`) with no `@`, that is a non-empty host — the text before the first `:`, or what an IP literal's brackets enclose — optionally followed by `:` and a port of ASCII digits.""" if not isinstance(s, str) or not s.startswith("https://"): return False authority = re.split(r"[/?#]", s[len("https://"):], maxsplit=1)[0] if not is_uri(s) or "@" in authority: return False if authority.startswith("["): host, closed, after = authority[1:].partition("]") if not closed: return False else: host, colon, port = authority.partition(":") after = colon + port return host != "" and (after == "" or (after[0] == ":" and all(c in "0123456789" for c in after[1:]))) def registry_form(alg): labels = alg.split(".") return len(labels) >= 2 and all(re.fullmatch(r"[A-Za-z0-9_+\-]+", l) for l in labels) def days_in(year, month): """Days in a month of the proleptic Gregorian calendar (RFC 3339 Appendix C).""" if month == 2: return 29 if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) else 28 return 30 if month in (4, 6, 9, 11) else 31 def is_rfc3339(s): """RFC 3339 §5.6 date-time with §5.7's day-of-month rule; ASCII digits only (a regex `\\d` would also take any Unicode decimal digit). A second of 60 is accepted at any time of day: whether a leap second occurred then is not a property of the syntax.""" m = re.fullmatch(r"([0-9]{4})-([0-9]{2})-([0-9]{2})[Tt]([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]+)?" r"([Zz]|[+-]([0-9]{2}):([0-9]{2}))", s) if not m: return False yr, mo, da, hh, mi, ss = (int(m.group(i)) for i in (1, 2, 3, 4, 5, 6)) if not (1 <= mo <= 12 and 1 <= da <= days_in(yr, mo) and hh <= 23 and mi <= 59 and ss <= 60): return False return m.group(9) is None or (int(m.group(9)) <= 23 and int(m.group(10)) <= 59) def is_oer_id(s): return isinstance(s, str) and re.fullmatch(r"(sha256|blake3):[0-9a-f]{64}", s) is not None def members(obj, required, step): if any(k not in obj for k in required): raise Reject(step, "missing_required_field") if any(k not in required for k in obj): raise Reject(step, "unknown_member") def need(ok, step, reason="field_type"): if not ok: raise Reject(step, reason) def check_body(cls, b): """§3.1 step 2: the members, then each member's type and encoding together, in the member table's order, a nested object's members checked where the table reaches it. Where every failure left is `field_type` the checks are grouped; the order shows only where a nested object's missing or unknown member could be reported instead.""" need(isinstance(b, dict), 2) if cls == "lawful-basis": # §2.4.6: `record` is read before anything else, and names the table. if "record" not in b: raise Reject(2, "missing_required_field") need(isinstance(b["record"], str), 2) if b["record"] == "declaration": members(b, DECLARATION, 2) need(is_uri(b["vocabulary"]), 2) need(b["fragment"] is None or is_hex(b["fragment"], 32), 2) need(isinstance(b["purposes"], list), 2) for p in b["purposes"]: need(isinstance(p, dict), 2) members(p, PURPOSE, 2) need(is_token(p["purpose"]), 2) need(p["basis"] == "" or is_token(p["basis"]), 2) # an empty basis is step 4's need(isinstance(p["data"], list) and all(is_token(d) for d in p["data"]), 2) elif b["record"] == "decision": members(b, DECISION, 2) need(all(isinstance(b[k], str) for k in ("vocabulary", "purpose", "outcome")), 2) need(is_hex(b["declaration"], 32), 2) need(is_uri(b["vocabulary"]) and is_token(b["purpose"]) and is_token(b["outcome"]), 2) else: raise Reject(2, "field_type") return members(b, BODY[cls], 2) if cls == "reconstruct-session": check_session_body(b) return if cls == "stage": # In the member table's order: `vocabulary` before the objects, whose members are # checked where the table reaches them. need(is_uri(b["vocabulary"]), 2) for k in ("op_requested", "op_committed", "op_delivered", "presented"): pt = b[k] need(pt is not None or k != "presented", 2) if pt is not None: need(isinstance(pt, dict) and all(is_token(n) and is_int(x) for n, x in pt.items()), 2) need(isinstance(b["units_by_stage"], list) and all(is_int(n) for n in b["units_by_stage"]), 2) need(b["tier"] in STAGE_TIERS, 2) need(b["stage_hash"] is None or is_hex(b["stage_hash"], 32), 2) need(isinstance(b["objects"], list), 2) for o in b["objects"]: need(isinstance(o, dict), 2) members(o, STAGE_OBJECT, 2) need(is_int(o["layer"]) and is_hex(o["object"], 32), 2) need(isinstance(o["units"], dict), 2) members(o["units"], STAGE_UNITS, 2) need(is_int(o["units"]["from"]) and is_int(o["units"]["count"]), 2) need(is_int(o["bytes"]) and o["status"] in STAGE_STATUS and o["evidence"] in STAGE_EVIDENCE, 2) need(o["reason"] is None or isinstance(o["reason"], str), 2) need(all(is_int(b[k]) for k in ("conflicts", "deadline_misses", "bytes_envelopes")), 2) return if cls == "delivery-gap": need(all(is_int(b[k]) for k in ("track_alias", "group_id", "object_id")), 2) need(b["reason"] in GAP_REASONS, 2) need(isinstance(b["observed_at"], str) and is_rfc3339(b["observed_at"]), 2) return if cls in ("measurement", "operating-point"): check_figure_header(b) if cls == "measurement": br = b["bracket"] if br is not None: need(isinstance(br, dict), 2) members(br, BRACKET, 2) for side in ("idle", "busy"): need(isinstance(br[side], dict), 2) members(br[side], SUMMARY, 2) need(all(is_int(br[side][k]) for k in SUMMARY), 2) need(all(is_int(br[k]) for k in ("window_us", "completions", "in_flight")), 2) need(br["opening"] in ("from-rest", "running"), 2) bd = b["build"] need(isinstance(bd, dict), 2) members(bd, BUILD, 2) need(bd["profile"] in ("release", "debug"), 2) need(bd["toolchain"] is None or isinstance(bd["toolchain"], str), 2) need(bd["target"] is None or is_token(bd["target"]), 2) need(b["power_reader"] is None or isinstance(b["power_reader"], str), 2) else: need(is_uri(b["vocabulary"]), 2) for k in ("op_requested", "op_committed", "op_delivered"): pt = b[k] if pt is not None: need(isinstance(pt, dict), 2) for name, val in pt.items(): need(is_token(name) and is_int(val), 2) pr = b["prediction"] if pr is not None: need(isinstance(pr, dict), 2) members(pr, PREDICTION, 2) need(is_uri(pr["predictor"]), 2) need(is_hex(pr["descriptor_hash"], 32), 2) need(is_int(pr["predicted_joules_micro"]), 2) return if cls == "energy": # In the member table's order: a nested object's member check can report another # reason than a type check, so the order is the one every verifier uses. need(is_int(b["joules_micro"]), 2) need(isinstance(b["energy_provenance"], str), 2) u = b["energy_uncertainty"] if u is not None: need(isinstance(u, dict), 2) members(u, UNCERTAINTY, 2) need(is_int(u["relative_ppm"]) and is_int(u["window_us"]), 2) need(b["energy_counter_filtering"] is None or isinstance(b["energy_counter_filtering"], str), 2) need(b["energy_calibration_ref"] is None or isinstance(b["energy_calibration_ref"], str), 2) need(all(isinstance(b[k], str) for k in ("coverage", "grade", "method")), 2) need(is_int(b["work_units"]), 2) need(isinstance(b["work_unit"], str), 2) need(b["oer_receipt"] is None or isinstance(b["oer_receipt"], str), 2) need(is_uri(b["method"]) and is_token(b["work_unit"]), 2) need(b["oer_receipt"] is None or is_oer_id(b["oer_receipt"]), 2) elif cls == "log-inclusion": need(all(isinstance(b[k], str) for k in ("log", "profile", "entry")), 2) need(is_hex(b["log_receipt"]), 2) need(is_uri(b["log"]) and is_uri(b["profile"]) and is_token(b["entry"]), 2) else: # soft-binding, binding-recovery need(isinstance(b["alg"], str) and b["binding_type"] in ("watermark", "fingerprint"), 2) need(is_hex(b["value"]) and isinstance(b["resolution_endpoint"], str), 2) if cls == "soft-binding": need(isinstance(b["registry"], str), 2) need(is_token(b["alg"]) and is_uri(b["registry"]) and is_uri(b["resolution_endpoint"]), 2) else: need(is_hex(b["declaration"], 32), 2) need(b["observed"] is None or is_hex(b["observed"], 32), 2) need(isinstance(b["attests"], str), 2) need(is_token(b["alg"]) and is_uri(b["resolution_endpoint"]), 2) def nested(v, required): """A nested object a table fixes: an object, then its members, missing then unknown.""" need(isinstance(v, dict), 2) members(v, required, 2) return v def check_session_body(b): """§2.4.12, at §3.1 step 2: each member's type and encoding in the table's order, the members of the host, of each decoder entry, switch, missed presentation and stage set, and of `energy`, where the table reaches them.""" need(is_hex(b["session_nonce"], 32), 2) need(isinstance(b["media"], str) and re.fullmatch(r"[a-z0-9-]{1,32}", b["media"]) is not None, 2) h = nested(b["host"], SESSION_HOST) need(h["profile"] in HOST_PROFILES, 2) need(h["target"] is None or is_token(h["target"]), 2) bd = nested(h["build"], BUILD) need(bd["profile"] in ("release", "debug"), 2) need(bd["toolchain"] is None or isinstance(bd["toolchain"], str), 2) need(bd["target"] is None or is_token(bd["target"]), 2) need(is_int(h["threads"]), 2) need(isinstance(b["decoders"], list), 2) for d in b["decoders"]: nested(d, SESSION_DECODER) need(is_token(d["capability"]), 2) need(d["rendition"] is None or is_int(d["rendition"]), 2) need(is_hex(d["object"], 32), 2) need(is_int(d["first_unit"]), 2) need(d["path"] in PATHS, 2) need(isinstance(d["decoder"], str), 2) need(d["variant"] is None or is_token(d["variant"]), 2) need(d["pin"] is None or is_hex(d["pin"], 32), 2) need(d["tier"] is None or isinstance(d["tier"], str), 2) need(d["module_status"] is None or isinstance(d["module_status"], str), 2) need(is_int(d["leaves"]), 2) need(all(is_int(b[k]) for k in ("units", "leaf_count")), 2) need(b["output_root"] is None or is_hex(b["output_root"], 32), 2) need(all(is_int(b[k]) for k in ("primary_units", "fallback_units")), 2) need(isinstance(b["switches"], list), 2) for w in b["switches"]: nested(w, SESSION_SWITCH) need(is_int(w["unit"]) and w["from"] in PATHS and w["to"] in PATHS and isinstance(w["reason"], str), 2) need(is_int(b["switches_total"]), 2) need(b["deadline_us"] is None or is_int(b["deadline_us"]), 2) need(is_int(b["missed"]), 2) need(isinstance(b["missed_units"], list), 2) for x in b["missed_units"]: nested(x, SESSION_MISSED) need(all(is_int(x[k]) for k in SESSION_MISSED), 2) need(isinstance(b["stage_sets"], list), 2) for x in b["stage_sets"]: nested(x, SESSION_STAGE_SET) need(is_int(x["first_unit"]) and is_hex(x["claim"], 32), 2) need(b["framing"] in FRAMINGS, 2) need(all(is_int(b[k]) for k in ("counted_primary", "counted_fallback", "counted_discarded", "uncounted", "transferred", "receipt_bytes")), 2) need(b["classical_declared"] is None or is_int(b["classical_declared"]), 2) need(b["classical_source"] is None or is_token(b["classical_source"]), 2) e = b["energy"] if e is not None: nested(e, SESSION_ENERGY) need(is_int(e["joules_micro"]), 2) need(isinstance(e["energy_provenance"], str), 2) u = e["energy_uncertainty"] if u is not None: nested(u, UNCERTAINTY) need(is_int(u["relative_ppm"]) and is_int(u["window_us"]), 2) need(e["energy_counter_filtering"] is None or isinstance(e["energy_counter_filtering"], str), 2) need(e["energy_calibration_ref"] is None or isinstance(e["energy_calibration_ref"], str), 2) def decoder_ok(s): """A decoder identifier of §2.4.12: builtin:@ or module:sha256:.""" if s.startswith("module:sha256:"): return re.fullmatch(r"[0-9a-f]{64}", s[len("module:sha256:"):]) is not None if not s.startswith("builtin:") or "@" not in s: return False krate, version = s[len("builtin:"):].split("@", 1) return (re.fullmatch(r"[a-z0-9_-]{1,64}", krate) is not None and re.fullmatch(r"[0-9][0-9A-Za-z.+-]{0,63}", version) is not None) def replicate(cap): """A capability's replicate class as the registry states it, None for one it does not register or classify.""" c = REGISTRY.get(cap) return c["replicate"] if c else None def class_tier(cap): """SPEC §3.1 *Tier*: the tier a capability's class gives — decode-equivalence for an integer-exact neural decode, entropy-consistency for an entropy-exact one, none for every other class — or None for a capability with no class.""" r = replicate(cap) if r is None: return None return {"neural-int-exact": "decode-equivalence", "neural-entropy-exact": "entropy-consistency"}.get(r, "none") def selectable(entry): """SPEC §7.1: not a companion or a refinement, and a capability a sink may dispatch — one the registry does not list, or a payload that is not a candidate.""" if entry.get("role") in ("companion", "refinement"): return False c = REGISTRY.get(entry["capability"]) return c is None or (c["kind"] == "payload" and c["status"] != "candidate") def session_rules(b): """§2.4.12's rules, at §3.1 step 4, in order.""" ds = b["decoders"] need(b["primary_units"] + b["fallback_units"] == b["units"], 4, "session_path_sum") need(sum(d["leaves"] for d in ds) == b["leaf_count"] and b["leaf_count"] >= b["units"] and (b["units"] == 0) == (b["leaf_count"] == 0), 4, "session_leaf_sum") need((b["output_root"] is not None) == (b["leaf_count"] > 0), 4, "session_root_presence") t = b["host"]["threads"] need(t > 0 and t & (t - 1) == 0, 4, "session_host_malformed") for d in ds: builtin = d["decoder"].startswith("builtin:") tier_ok = (d["pin"] is None and d["tier"] is None) or ( d["pin"] is not None and d["tier"] is not None and d["tier"] in TIER_RANK) status_ok = builtin if d["module_status"] is None else (not builtin and d["module_status"] in MODULE_STATUSES) need(decoder_ok(d["decoder"]) and (d["rendition"] is None or d["rendition"] <= 65535) and tier_ok and status_ok and d["leaves"] != 0, 4, "session_decoder_malformed") keys = [tuple(d[k] for k in SESSION_DECODER if k != "leaves") for d in ds] need(len(set(keys)) == len(keys), 4, "session_decoder_malformed") for d in ds: ct = class_tier(d["capability"]) need(d["tier"] is None or ct is None or TIER_RANK[d["tier"]] <= TIER_RANK[ct], 4, "session_tier_exceeds_class") sw = b["switches"] need(len(sw) == min(b["switches_total"], 1024) and all(w["from"] != w["to"] and w["reason"] in SWITCH_REASONS for w in sw) and all(sw[i]["unit"] <= sw[i + 1]["unit"] for i in range(len(sw) - 1)), 4, "session_switch_invalid") mu = b["missed_units"] need(b["missed"] <= b["leaf_count"] and (b["deadline_us"] is not None or b["missed"] == 0) and len(mu) == min(b["missed"], 256) and all(1 <= x["layers"] <= 65535 and x["late_us"] >= 1 for x in mu) and all((mu[i]["unit"], mu[i]["layers"]) < (mu[i + 1]["unit"], mu[i + 1]["layers"]) for i in range(len(mu) - 1)), 4, "session_missed_invalid") ss = b["stage_sets"] need(all((ss[i]["first_unit"], ss[i]["claim"]) < (ss[i + 1]["first_unit"], ss[i + 1]["claim"]) for i in range(len(ss) - 1)), 4, "session_stage_sets_invalid") need(b["counted_discarded"] <= b["counted_primary"] + b["counted_fallback"], 4, "session_counted_invalid") cd, cs = b["classical_declared"], b["classical_source"] need((cd is None and cs is None) or (cd is not None and cs in CLASSICAL_SOURCES), 4, "session_classical_invalid") e = b["energy"] if e is not None: need(e["joules_micro"] != 0, 4, "energy_zero_figure") acquisition(e) # ------------------------------------------- §2.4.12 step 6: what a session is checked against def out_leaf(p): """staged-measurement Appendix C: a presentation's leaf.""" return hashlib.sha256(b"wai:out-leaf\x01" + p["unit"].to_bytes(8, "big") + p["layers"].to_bytes(2, "big") + p["decoder"].to_bytes(2, "big") + bytes.fromhex(p["out_digest"])).digest() def out_mth(leaves): """RFC 6962's Merkle tree hash, under the output tree's node domain.""" if len(leaves) == 1: return leaves[0] k = 1 while k * 2 < len(leaves): k *= 2 return hashlib.sha256(b"wai:out-node\x01" + out_mth(leaves[:k]) + out_mth(leaves[k:])).digest() def ctx_int(v, bits=64): if not (isinstance(v, int) and not isinstance(v, bool) and 0 <= v < 2 ** bits): raise ValueError("bad integer in a session context") return v def ctx_hash(v): if not is_hex(v, 32): raise ValueError("bad hash in a session context") return v def presentations(v): if not isinstance(v, list): raise ValueError("outputs must be an array") return [{"unit": ctx_int(p["unit"]), "layers": ctx_int(p["layers"], 16), "decoder": ctx_int(p["decoder"], 16), "out_digest": ctx_hash(p["out_digest"])} for p in v] def finals(ps): """Each unit presented, with its presentation at its largest layer count.""" out = {} for p in ps: if p["unit"] not in out or p["layers"] > out[p["unit"]]["layers"]: out[p["unit"]] = p return out def check_session_outputs(c, ps, redecoded): b = c["body"] need(c["class"] == "reconstruct-session", 6, "context_mismatch") keys = [(p["unit"], p["layers"]) for p in ps] need(len(ps) == b["leaf_count"] and len(set(keys)) == len(keys), 6, "session_output_mismatch") if ps: ordered = sorted(ps, key=lambda p: (p["unit"], p["layers"])) need(out_mth([out_leaf(p) for p in ordered]).hex() == b["output_root"], 6, "session_output_mismatch") else: need(b["output_root"] is None, 6, "session_output_mismatch") ds = b["decoders"] bad = "session_output_inconsistent" need(all(p["decoder"] < len(ds) and p["layers"] >= 1 for p in ps), 6, bad) fin = finals(ps) need(len(fin) == b["units"], 6, bad) need(all(d["leaves"] == sum(1 for p in ps if p["decoder"] == i) for i, d in enumerate(ds)), 6, bad) nxt = 0 for p in sorted(ps, key=lambda p: (p["layers"], p["unit"])): need(p["decoder"] <= nxt, 6, bad) if p["decoder"] == nxt: nxt += 1 primary = sum(1 for p in fin.values() if ds[p["decoder"]]["path"] == "primary") need(primary == b["primary_units"] and len(fin) - primary == b["fallback_units"], 6, bad) need(all((x["unit"], x["layers"]) in set(keys) for x in b["missed_units"]), 6, bad) for i, p in enumerate(ps): if replicate(ds[p["decoder"]]["capability"]) in ("neural-int-exact", "standard-defined") and i in redecoded: need(redecoded[i] == p["out_digest"], 6, "session_output_irreproducible") def objects_seen(v): if not isinstance(v, list): raise ValueError("objects must be an array") out = [] for o in v: ctx_hash(o["content_hash"]) if o["path"] not in PATHS or not isinstance(o["presented"], bool): raise ValueError("bad object") out.append({"content_hash": o["content_hash"], "path": o["path"], "counted": None if o["counted"] is None else ctx_int(o["counted"]), "transferred": ctx_int(o["transferred"]), "presented": o["presented"]}) return out def check_session_objects(c, objs): """§2.4.12 against the objects received: each (content hash, path) once; the entry object and each entry's object among them; the counted sums; and an object presented exactly when a decoder entry names it.""" b = c["body"] need(c["class"] == "reconstruct-session", 6, "context_mismatch") keys = [(o["content_hash"], o["path"]) for o in objs] need(len(set(keys)) == len(keys), 6, "session_object_duplicate") need(any(o["content_hash"] == c["subject"] for o in objs), 6, "session_entry_not_received") received = {o["content_hash"] for o in objs} need(all(d["object"] in received for d in b["decoders"]), 6, "session_source_not_received") sums = {"primary": 0, "fallback": 0} discarded = uncounted = transferred = 0 for o in objs: n = o["counted"] transferred += o["transferred"] if n is None: uncounted += 1 else: sums[o["path"]] += n if not o["presented"]: discarded += n need((sums["primary"], sums["fallback"], discarded, uncounted, transferred) == (b["counted_primary"], b["counted_fallback"], b["counted_discarded"], b["uncounted"], b["transferred"]), 6, "session_counted_mismatch") named = {d["object"] for d in b["decoders"]} need(all(o["presented"] == (o["content_hash"] in named) for o in objs), 6, "session_presented_mismatch") def wai2_read(data): """SPEC §7.1: a WAI2 envelope's manifest and each rendition's payload length, or None for bytes that are not one. The manifest is read as staged_verify.py and http_verify.py read one: strict JSON, then the members SPEC §7.1 requires with their types.""" if len(data) < 8 or data[:4] != b"WAI2": return None ml = struct.unpack_from(" len(data): return None try: m = SV.strict_json(data[8:8 + ml]) SV.check_manifest_v2(m) except (SV.NotJSON, SV.Schema): return None rs = m["renditions"] o = 8 + ml if o + 2 > len(data) or struct.unpack_from(" len(data): return None lengths, expected = [], 0 for i in range(len(rs)): off, ln = struct.unpack_from(" len(data): return None return m, lengths def plaintext_v2(m): """SPEC §6.1, §7.1: a WAI2 manifest's payloads are plaintext when its `security` is absent or null, or a security object whose `encryption` is absent, null or "none". A `security` that is not a security object (not an object, an `encryption` that is not a string, a `kid` that is not an integer from 0 to 2^64 - 1) is not plaintext.""" sec = m.get("security") if sec is None: return True if not isinstance(sec, dict): return False enc, kid = sec.get("encryption"), sec.get("kid") if enc is not None and not isinstance(enc, str): return False if kid is not None and not (isinstance(kid, int) and not isinstance(kid, bool) and 0 <= kid < 2 ** 64): return False return enc is None or enc == "none" def classical_rendition(m): """§2.4.12: the first rendition, in manifest order, that is selectable and of a classical capability whose registry media is the manifest's `media` and is not text.""" for i, e in enumerate(m["renditions"]): c = REGISTRY.get(e["capability"]) if (selectable(e) and e["capability"] in CLASSICAL and c is not None and c["media"] == m["media"] and c["media"] != "text"): return i return None def envelopes(v): if not isinstance(v, list): raise ValueError("envelopes must be an array") out = [] for e in v: if not is_hex(e["envelope"]): raise ValueError("bad envelope") out.append(bytes.fromhex(e["envelope"])) return out def counted_of(e): """staged-measurement §3: an envelope's counted bytes, or None where it is not countable, for whatever reason (measure_verify.py's reading of the byte rule).""" try: return MV.count_any(e)["total"] except Exception: # noqa: BLE001 (every way an object fails the byte rule is "not countable") return None def payload_units(cap, p): """§2.4.12: the units a base payload holds where WAI reads them from its format: a WIV1 clip's frames, one for a WIH1 picture, a WIA1 clip or a WIS1 tensor; None for a capability with no pinned integer format, or a payload not in its own.""" m = IPV.CAP_MAGIC.get(cap) if m is None: return None try: if m == b"WIV1": return IPV.parse_wiv1(p, None)["n_frames"] {b"WIH1": IPV.parse_wih1, b"WIA1": IPV.parse_wia1, b"WIS1": IPV.parse_wis1}[m](p) return 1 except Exception: # noqa: BLE001 (a payload that does not parse has no units to read) return None def read_envelope(data): """A WAI1 or WAI2 envelope whose payloads are plaintext, read: ("one", manifest, payload) or ("multi", manifest, payloads); None for any other bytes (inert).""" if data[:4] == b"WAI2": r = wai2_read(data) if r is None or not plaintext_v2(r[0]): return None m, lengths = r o = 8 + struct.unpack_from("= len(rs): return None e = rs[i] if not layer_entry(e): return ("base", None if sealed(obj) else payload_units(e["capability"], payloads[i])) # SPEC §7.1: a sink offers a WAI2's refinements only when every component conforms. if component_roles(rs) is not None: return ("na",) ref = SV.parse_refines_ids(e["refines"]) bj = next(j for j, y in enumerate(rs) if y.get("id") == ref["base"]) return layer_of(e["capability"], ref, component(m, payloads, bj, sealed(obj)), names_of(None, (obj, bj)), None if sealed(obj) else payloads[i]) need(not any((given(k) or ("",))[0] == "na" for k in range(len(ds))), 6, "session_layer_not_applicable") for k, d in enumerate(ds): g = given(k) if g is not None and g[0] == "layer": need(not g[3] or d["first_unit"] in g[3], 6, "session_first_unit_mismatch") if ps is not None: for p in ps: g = given(p["decoder"]) if p["decoder"] < len(ds) else None if g is not None and g[0] in ("base", "layer"): need(p["layers"] == (1 if g[0] == "base" else g[1]), 6, "session_layers_mismatch") for p in ps: g = given(p["decoder"]) if p["decoder"] < len(ds) else None covers = None if g is not None and g[0] == "base" and g[1] is not None: covers = (0, g[1]) elif g is not None and g[0] == "layer" and g[2] is not None: covers = g[2] if covers is not None: f = ds[p["decoder"]]["first_unit"] + covers[0] need(f <= p["unit"] < f + covers[1], 6, "session_unit_outside_object") # 9. The classical length, over the held WAI2 envelopes, readable or encrypted. decoded = {d["capability"] for d in b["decoders"]} total = None for r in held_envs: if r[0] != "multi": continue m = r[1] i = classical_rendition(m) if i is not None and m["renditions"][i]["capability"] not in decoded: total = (total or 0) + len(r[2][i]) need(total == b["classical_declared"], 6, "session_classical_mismatch") def check_session_stage_claims(c, wires, ps, objs): """§2.4.12 against the stage claims. A sink names one for each epoch of a stage set, and bases over the same units of the title each have their own: each claim's identity and objects first, then with the outputs each claim's units and its count from below, and each unit of a set from above, by the objects listed as applied or partial by the claims whose sets hold it.""" if not isinstance(wires, list) or not all(isinstance(w, str) for w in wires): raise ValueError("stage_claims must be an array of wire forms") b = c["body"] need(c["class"] == "reconstruct-session", 6, "context_mismatch") received = {o["content_hash"] for o in objs} if objs is not None else None claims = [] for w in wires: try: s = verify_claim(w) except Reject: raise Reject(6, "context_invalid") link = link_hash(s) named = [x["first_unit"] for x in b["stage_sets"] if x["claim"] == link] need(s["class"] == "stage" and s["signer_pubkey"] == c["signer_pubkey"] and named, 6, "context_mismatch") arrived = [o for o in s["body"]["objects"] if o["status"] != "dropped" and o["bytes"] > 0] if received is not None: need(s["subject"] in received and all(o["object"] in received for o in arrived), 6, "session_stage_disagrees") claims.append((s, named)) # Each entry naming a claim's base, or a layer object it lists, has a first unit at # which stage_sets names a claim listing that object. at = {} for s, named in claims: for h in [s["subject"]] + [o["object"] for o in s["body"]["objects"]]: at.setdefault(h, set()).update(named) need(all(d["first_unit"] in at[d["object"]] for d in b["decoders"] if d["object"] in at), 6, "session_first_unit_mismatch") if ps is None: return fin = finals(ps) def in_set(f, n): return [(u - f, p["layers"]) for u, p in fin.items() if f <= u < f + n] sets = [] for s, named in claims: ubs = s["body"]["units_by_stage"] n = sum(ubs) for f in named: presented = in_set(f, n) need(len(presented) == n, 6, "session_stage_disagrees") for st in range(1, len(ubs)): need(sum(1 for _, l in presented if l > st) >= sum(ubs[st:]), 6, "session_stage_disagrees") sets.append((f, n, [o for o in s["body"]["objects"] if o["status"] in ("applied", "partial")])) for u, p in fin.items(): holding = [(f, applied) for f, n, applied in sets if f <= u < f + n] if not holding: continue for layer in range(1, p["layers"]): need(any(o["layer"] == layer and o["units"]["from"] <= u - f < o["units"]["from"] + o["units"]["count"] for f, applied in holding for o in applied), 6, "session_stage_disagrees") def check_figure_header(b): """§2.4.8's header at step 2, in its table's order: `figure_profile` a term, as it is read; the index an integer or null; the group a hash or null; then the figure and the class's members as §2.4.5 types them.""" need(is_token(b["figure_profile"]), 2) need(b["figure_index"] is None or is_int(b["figure_index"]), 2) need(b["group"] is None or is_hex(b["group"], 32), 2) need(is_int(b["joules_micro"]), 2) need(isinstance(b["energy_provenance"], str), 2) u = b["energy_uncertainty"] if u is not None: need(isinstance(u, dict), 2) members(u, UNCERTAINTY, 2) need(is_int(u["relative_ppm"]) and is_int(u["window_us"]), 2) need(b["energy_counter_filtering"] is None or isinstance(b["energy_counter_filtering"], str), 2) need(b["energy_calibration_ref"] is None or isinstance(b["energy_calibration_ref"], str), 2) # ------------------------------------------- a measurement report's arithmetic (§3.1, §6.2) def summary_live(s): """energy-measurement §3.1: a rail is live over a bracket only if it advanced in two consecutive intervals, in at least five, and in at least four of every five — facts that describe one sequence of `n` intervals. A power sample advances only where it differs from the one before it, so a bracket whose samples are all equal (`sd_uw` 0) advanced once at most, and is never live whatever facts it carries.""" n, live, run = s["n"], s["live_intervals"], s["longest_live_run"] return s["sd_uw"] > 0 and run <= live <= n and run >= 2 and live >= 5 and live * 5 >= n * 4 def work(br): c, n = br["completions"], br["in_flight"] return (2 * c + n, 2) if br["opening"] == "from-rest" else (c, 1) def bracket_figure(br): """F = floor((2·M·T·Wd + 10^6·Wn) / (2·10^6·Wn)), or None.""" m = br["busy"]["mean_uw"] - br["idle"]["mean_uw"] t, c, n = br["window_us"], br["completions"], br["in_flight"] if m <= 0 or t == 0 or c == 0 or n == 0: return None wn, wd = work(br) f = (2 * m * t * wd + 10 ** 6 * wn) // (2 * 10 ** 6 * wn) return f if 0 < f <= MAX_INT else None def k_and_p(br, f): m = br["busy"]["mean_uw"] - br["idle"]["mean_uw"] t, n = br["window_us"], br["in_flight"] wn, wd = work(br) ni, nb, si, sb = br["idle"]["n"], br["busy"]["n"], br["idle"]["sd_uw"], br["busy"]["sd_uw"] k = 12 * ni * nb * wn ** 4 * f * f p = 12 * wn ** 2 * t ** 2 * wd ** 2 * (si * si * nb + sb * sb * ni) + 4 * ni * nb * m * m * t * t * wd * wd * n * n \ + 10 ** 12 * ni * nb * wn ** 4 return k, p def least_ppm(br, f): """The least p with p^2·K >= P, or None above 2^32 - 1.""" k, p = k_and_p(br, f) if k == 0: return None q = -(-p // k) # p^2 >= ceil(P / K) r = __import__("math").isqrt(q) while r * r * k < p: r += 1 while r > 1 and (r - 1) * (r - 1) * k >= p: r -= 1 return r if 1 <= r <= 2 ** 32 - 1 else None def bracket_rules(br, joules, label, u): """§2.4.8 rule 5, in its order.""" hw = label in ("OnChipCounter", "CalibratedInstrument") need(br["idle"]["n"] >= 2 and br["busy"]["n"] >= 2, 4, "measurement_too_few_samples") need(not hw or (summary_live(br["idle"]) and summary_live(br["busy"])), 4, "measurement_rail_not_live") need(br["window_us"] != 0 and br["completions"] != 0 and br["in_flight"] != 0, 4, "measurement_empty_burst") m = br["busy"]["mean_uw"] - br["idle"]["mean_uw"] need(m > 0, 4, "measurement_no_marginal") need(not hw or m >= br["idle"]["sd_uw"], 4, "measurement_below_attribution_floor") need(bracket_figure(br) == joules, 4, "measurement_figure_mismatch") if hw: need(u["window_us"] == br["window_us"], 4, "measurement_window_mismatch") k, p = k_and_p(br, joules) need(u["relative_ppm"] ** 2 * k >= p, 4, "measurement_declaration_below_bracket") def figure_rules(c): """§2.4.8 rules 1-3: a non-zero figure, a profile for the subject type with an index and a group exactly where it has them, and the class.""" b = c["body"] need(b["joules_micro"] != 0, 4, "energy_zero_figure") prof = PROFILES.get(b["figure_profile"]) need(prof is not None and prof[0] == c["subject_type"], 4, "figure_unknown_profile") need((b["figure_index"] is not None) == prof[1], 4, "figure_index_mismatch") need((b["group"] is not None) == prof[3], 4, "figure_group_mismatch") acquisition(b) def acquisition(b): """§2.4.5 rule 2: the class is one energy-measurement.md §2 names, not the legacy HwShunt, and carries exactly the evidence §2.3-§2.5 give it: a refined hardware class a non-zero declared uncertainty whose relative part fits in 32 bits (§2.7) and which reaches the resolution floor, an on-chip counter a filtering state and no calibration reference, a calibrated instrument a calibration reference that is not all white space and no filtering state; every other class none of the three.""" label = b["energy_provenance"] need(label in STRONGEST, 4, "energy_unknown_acquisition") need(label != "HwShunt", 4, "energy_legacy_acquisition") u, f, ref = b["energy_uncertainty"], b["energy_counter_filtering"], b["energy_calibration_ref"] ev = "energy_acquisition_evidence" if label in ("OnChipCounter", "CalibratedInstrument"): need(u is not None and 0 < u["relative_ppm"] <= 2 ** 32 - 1 and u["window_us"] > 0, 4, ev) if label == "OnChipCounter": need(f in FILTERING and ref is None, 4, ev) else: need(f is None and ref is not None and ref.strip(WHITE_SPACE) != "", 4, ev) need(b["joules_micro"] * u["relative_ppm"] >= RESOLUTION_FLOOR, 4, ev) else: need(u is None and f is None and ref is None, 4, ev) def class_rules(c): """§3.1 step 4 — §2.4.5-§2.4.7 and §2.6.""" cls, b = c["class"], c["body"] if cls == "energy": need(b["joules_micro"] != 0, 4, "energy_zero_figure") acquisition(b) need(b["coverage"] in COVERAGE, 4, "energy_unknown_coverage") need(b["grade"] in GRADES, 4, "energy_unknown_grade") need(GRADES.index(b["grade"]) <= GRADES.index(STRONGEST[b["energy_provenance"]]), 4, "energy_grade_exceeds_acquisition") need(b["work_units"] != 0, 4, "energy_zero_work") elif cls == "lawful-basis" and b["record"] == "declaration": need(len(b["purposes"]) > 0, 4, "lawful_basis_no_purposes") seen = set() for p in b["purposes"]: need(p["basis"] != "", 4, "lawful_basis_purpose_without_basis") need(p["purpose"] not in seen, 4, "lawful_basis_duplicate_purpose") seen.add(p["purpose"]) elif cls == "measurement": figure_rules(c) # Rule 4: a hardware figure is a reading, and its report names the reader. need(b["energy_provenance"] not in ("OnChipCounter", "CalibratedInstrument") or b["power_reader"] is not None, 4, "measurement_no_power_reader") if b["bracket"] is not None: bracket_rules(b["bracket"], b["joules_micro"], b["energy_provenance"], b["energy_uncertainty"]) elif cls == "operating-point": figure_rules(c) pts = [b[k] for k in ("op_requested", "op_committed", "op_delivered")] need(all(p is None or len(p) > 0 for p in pts), 4, "operating_point_empty_point") req, com, dlv = pts need(not (dlv is not None and com is None), 4, "operating_point_delivered_alone") need(not ((req is not None or com is not None) and dlv is None), 4, "operating_point_incomplete") need(dlv is not None or b["prediction"] is not None, 4, "operating_point_empty") elif cls == "stage": stage_rules(b) elif cls == "reconstruct-session": session_rules(b) elif cls == "log-inclusion": need(b["log_receipt"] != "", 4, "log_inclusion_no_evidence") elif cls in ("soft-binding", "binding-recovery"): need(registry_form(b["alg"]), 4, "soft_binding_alg_not_registry_form") need(b["value"] != "", 4, "soft_binding_empty_value") need(is_https(b["resolution_endpoint"]), 4, "soft_binding_endpoint_not_https") if cls == "binding-recovery": need(b["attests"] == "ancestor", 4, "recovery_not_ancestor") need(b["observed"] != c["subject"], 4, "recovery_of_hard_bound_object") def stage_layers(pt): """The layers a stage point names: exactly the member `wai-stage:layers`, 0 to 255.""" if len(pt) != 1 or "wai-stage:layers" not in pt or pt["wai-stage:layers"] > 255: return None return pt["wai-stage:layers"] STAGE_REFUSED_UNCHECKED = ("refines-malformed", "capability-not-refinement", "base-absent") # §4.1 steps 1-2 def refused_unchecked(o): """An object the sink refused before it checked its refines against the set: its units are as its refines states them, and it is no layer of the set's.""" return o["status"] == "refused" and o["reason"] in STAGE_REFUSED_UNCHECKED def merge(spans): """Half-open intervals, sorted, merged, none empty.""" out = [] for a, e in sorted(x for x in spans if x[0] < x[1]): if out and a <= out[-1][1]: out[-1] = (out[-1][0], max(out[-1][1], e)) else: out.append((a, e)) return out def length(v): return sum(e - a for a, e in v) def intersect(v, span): a, e = span return [(max(x, a), min(y, e)) for x, y in v if max(x, a) < min(y, e)] def subtract(v, w): out = [] for a, e in v: at = a for x, y in w: if y <= at or x >= e: continue if x > at: out.append((at, x)) at = max(at, y) if at < e: out.append((at, e)) return out def covered(objs, n): """How many of the units 0..n the objects' spans cover between them.""" spans = sorted((min(o["units"]["from"], n), min(o["units"]["from"] + o["units"]["count"], n)) for o in objs) total, reach = 0, 0 for a, e in spans: a = max(a, reach) if e > a: total += e - a reach = e return total def delivered_stage(b, n): """§2.4.10: the highest k such that, at every layer from 1 to k, the objects listed with delivered-verified evidence — an object refused before its refines was checked excepted — cover every one of the n units.""" k = 0 for layer in range(1, 256): objs = [o for o in b["objects"] if o["layer"] == layer and o["evidence"] == "delivered-verified" and not refused_unchecked(o)] if covered(objs, n) < n: break k = layer return k def stage_rules(b): """§2.4.10's rules, in order: the points, the stage hash, the units, the objects, the units against the objects, the delivered stage.""" pts = [b[k] for k in ("op_requested", "op_committed", "op_delivered")] need(b["vocabulary"] == STAGE_VOCABULARY, 4, "stage_point_malformed") need(all(stage_layers(p) is not None for p in pts + [b["presented"]] if p is not None), 4, "stage_point_malformed") req, com, dlv = pts need(not ((req is not None or com is not None) and dlv is None), 4, "stage_point_incomplete") need((b["stage_hash"] is not None) == (b["tier"] == "decode-equivalence"), 4, "stage_hash_without_tier") ubs = b["units_by_stage"] n = sum(ubs) need(0 < len(ubs) <= 256 and n > 0 and ubs[-1] != 0, 4, "stage_units_malformed") lowest = next(i for i, c in enumerate(ubs) if c != 0) need(stage_layers(b["presented"]) == lowest and b["conflicts"] <= n and b["deadline_misses"] <= n, 4, "stage_units_malformed") for o in b["objects"]: u, st, ev, r = o["units"], o["status"], o["evidence"], o["reason"] in_set = refused_unchecked(o) or u["from"] + u["count"] <= n least = 2 if st == "partial" else 1 # a partial object applied to some units of its span, not all need(1 <= o["layer"] <= 255 and u["count"] >= least and in_set, 4, "stage_objects_malformed") if st in ("applied", "late", "dropped"): need(r is None, 4, "stage_objects_malformed") else: need(r in STAGE_REASONS, 4, "stage_objects_malformed") if st in ("applied", "partial") and dlv is not None: need(ev == "delivered-verified", 4, "stage_objects_malformed") # §10.1 over a base verified first if st == "dropped": need(ev != "delivered-verified", 4, "stage_objects_malformed") need((st == "dropped") == (o["bytes"] == 0), 4, "stage_objects_malformed") keys = [(o["layer"], o["units"]["from"], o["object"]) for o in b["objects"]] need(all(x < y for x, y in zip(keys, keys[1:])), 4, "stage_objects_malformed") need(sum(o["bytes"] for o in b["objects"]) < b["bytes_envelopes"], 4, "stage_objects_malformed") # Two objects of one layer are never both applied to a unit. for layer in {o["layer"] for o in b["objects"]}: spans = sorted((o["units"]["from"], o["units"]["from"] + o["units"]["count"]) for o in b["objects"] if o["layer"] == layer and o["status"] == "applied") need(all(spans[i + 1][0] >= max(e for _, e in spans[:i + 1]) for i in range(len(spans) - 1)), 4, "stage_objects_malformed") # The units against the objects, per unit: an applied object of layer s lies within the # spans of layer s - 1's applied and partial objects (every unit for layer 1), a partial # one meets them, and the units at stage s or above are no more than the units layer s's # applied objects cover and, of the rest below, at most each partial object's span less # one. reaching = [o for o in b["objects"] if o["status"] in ("applied", "partial")] top = max([o["layer"] for o in reaching] + [len(ubs) - 1]) below = [(0, n)] for st in range(1, top + 1): layer = [o for o in reaching if o["layer"] == st] span = lambda o: (min(o["units"]["from"], n), min(o["units"]["from"] + o["units"]["count"], n)) applied = merge([span(o) for o in layer if o["status"] == "applied"]) free, partial_most = [], 0 for o in layer: inside = intersect(below, span(o)) if o["status"] == "applied": need(length(inside) >= o["units"]["count"], 4, "stage_units_unsupported") else: need(len(inside) > 0, 4, "stage_units_unsupported") open_ = subtract(inside, applied) partial_most += min(length(open_), o["units"]["count"] - 1) free += open_ reach = length(applied) + min(length(merge(free)), partial_most) need(sum(ubs[st:]) <= reach, 4, "stage_units_unsupported") below = merge([span(o) for o in layer]) if dlv is not None: need(stage_layers(dlv) == delivered_stage(b, n), 4, "stage_delivered_disagrees") def verify_claim(text): """§3.1 steps 1-4. Returns the claim; raises Reject.""" try: c = parse_strict(text) except ValueError: raise Reject(1, "malformed") need(isinstance(c, dict), 1, "malformed") if "kind" not in c: raise Reject(1, "missing_required_field") need(c["kind"] == "claim", 1, "wrong_kind") members(c, ENVELOPE, 1) need(isinstance(c["class"], str), 1) need(c["class"] != "delivery", 1, "class_reserved") need(c["class"] in ADMITS, 1, "unknown_class") need(c["subject_type"] in ("object", "receipt"), 1) need(is_hex(c["subject"], 32), 1) need(is_int(c["seq"]), 1) need(isinstance(c["issued_at"], str) and is_rfc3339(c["issued_at"]), 1) need(is_hex(c["signer_pubkey"], 32), 1) need(isinstance(c["signer_id"], str), 1) need(c["parent_receipt_hash"] is None or is_hex(c["parent_receipt_hash"], 32), 1) need(is_hex(c["sig"], 64), 1) need(c["subject_type"] in ADMITS[c["class"]], 1, "subject_type_not_admitted") check_body(c["class"], c["body"]) check_signature(bytes.fromhex(c["signer_pubkey"]), bytes.fromhex(c["sig"]), signing_input(c)) class_rules(c) return c def check_chain(c, previous): """§3.1 step 5.""" parent = c["parent_receipt_hash"] if previous is None: need(parent is None, 5, "parent_not_held") return need(parent is not None, 5, "chain_break") need(previous["signer_pubkey"] == c["signer_pubkey"] and parent == link_hash(previous) and c["seq"] > previous["seq"], 5, "chain_break") def match(alg, declared, observed, exact_algs): need(alg in exact_algs, 6, "recovery_no_matcher") need(declared == observed, 6, "recovery_value_mismatch") def dig(v, *path): """The member at `path` through JSON objects, or None.""" for k in path: v = v.get(k) if isinstance(v, dict) else None return v def figure_rounds_to_micro(figure, uj): """§2.4.5 check 4: whether an OER receipt's joules_total is `uj` microjoules to the nearest microjoule, a figure half-way between two admitting either. The figure is read as the OER reference reads a quantity for its own composition checks (`OER.dec`: a decimal string of at most 34 significand digits, or an integer within I-JSON's range) and compared in its exact context, so no digit is rounded away: 0.0120004 followed by twenty-five nines is below half-way, although rounding it to 28 digits would make it a tie. Check 2 has already refused a receipt whose figure is anything else; such a value is no figure here either.""" try: with localcontext(OER.EXACT): return abs(OER.dec(figure) * 1000000 - uj) * 2 <= 1 except TypeError: return False def check_context(c, ctx): """§3.1 step 6.""" b = c["body"] if "object" in ctx: o = ctx["object"] # The corpus gives a delivered object's microjoules as an integer within 64 bits, as # wai-rs's harness requires; anything else (`-0`, which is read as a double, among # them) is a malformed corpus, not a verdict. j = o.get("joules_micro") if not (isinstance(j, int) and not isinstance(j, bool) and 0 <= j < 2 ** 64): raise ValueError("bad object.joules_micro") need(c["class"] == "energy" and o["content_hash"] == c["subject"], 6, "context_mismatch") need(o["joules_micro"] == b["joules_micro"], 6, "energy_disagrees_with_delivery") label = class_of_json(o) # the label in the object's leaf, where its leaf is labelled need(label is None or label == class_of_body(b), 6, "energy_class_disagrees_with_delivery") if "figure" in ctx: check_figure(c, figure_view(ctx["figure"])) elif c["class"] == "measurement": # §2.4.8: a report is its figure signer's only once step 6 has run against the # figure (check 5); checked without it, it is not accepted. raise Reject(6, "figure_not_held") if "oer" in ctx: r, keys = ctx["oer"]["receipt"], ctx["oer"]["keys"] need(c["class"] == "energy" and b["oer_receipt"] is not None and dig(r, "receipt_id") == b["oer_receipt"], 6, "context_mismatch") # The reference reports every failure as a reason (oer-receipts §7); an exception # here is a defect in it, and is left to surface as one rather than read as a verdict. need(OER.verify_receipt(r, keys) == [], 6, "energy_oer_invalid") need(dig(r, "subject", "output") == c["subject"], 6, "context_mismatch") need(dig(r, "provenance", "how") == b["grade"], 6, "energy_disagrees_with_oer") need(figure_rounds_to_micro(dig(r, "energy", "joules_total"), b["joules_micro"]), 6, "energy_disagrees_with_oer") if "gap_object" in ctx: # §2.4.11: the object receipt at the claim's coordinates, as the verifier holds it # once verified (by inclusion at least). g = ctx["gap_object"] need(c["class"] == "delivery-gap", 6, "context_mismatch") need(all(g[k] == b[k] for k in ("track_alias", "group_id", "object_id")), 6, "context_mismatch") need(g["content_hash"] == c["subject"], 6, "gap_subject_mismatch") # §2.4.12: a session against its outputs, the objects it received, the WAI2 envelopes # it received and the stage claims it names, in that order. ps = presentations(ctx["outputs"]) if "outputs" in ctx else None if ps is not None: red = ctx.get("redecoded", []) if not isinstance(red, list): raise ValueError("redecoded must be an array") found = {} for x in red: found[ctx_int(x["index"])] = ctx_hash(x["out_digest"]) check_session_outputs(c, ps, found) objs = objects_seen(ctx["objects"]) if "objects" in ctx else None if objs is not None: check_session_objects(c, objs) if "envelopes" in ctx: check_session_envelopes(c, envelopes(ctx["envelopes"]), objs, ps) if "stage_claims" in ctx: check_session_stage_claims(c, ctx["stage_claims"], ps, objs) if "declaration" in ctx: # In §3.1 step 6's order: the claim must be one that names a declaration; the # declaration must itself pass steps 1-4 (context_invalid) — its signer_pubkey is # only its signer's once that is established; then it must be of the class named. recovery = c["class"] == "binding-recovery" decision = c["class"] == "lawful-basis" and b["record"] == "decision" # §2.6.3: the declaration is context only together with the publisher key of a # verified delivery receipt for its subject. A corpus that gives one without the # other is malformed, not a verdict. if recovery and "publisher" not in ctx: raise ValueError("a recovery record's declaration is context only with a publisher key") need(recovery or decision, 6, "context_mismatch") if not isinstance(ctx["declaration"], str): raise TypeError("a declaration is given as its wire form") try: d = verify_claim(ctx["declaration"]) except Reject: raise Reject(6, "context_invalid") db = d["body"] if recovery: need(d["class"] == "soft-binding", 6, "context_mismatch") need(b["declaration"] == link_hash(d) and c["subject"] == d["subject"] and b["alg"] == db["alg"] and b["binding_type"] == db["binding_type"], 6, "recovery_declaration_mismatch") need(d["signer_pubkey"] == ctx["publisher"], 6, "recovery_signer_mismatch") match(db["alg"], db["value"], b["value"], ctx.get("exact_match_algs", [])) else: need(d["class"] == "lawful-basis" and db["record"] == "declaration", 6, "context_mismatch") need(b["declaration"] == link_hash(d) and c["subject"] == d["subject"] and c["subject_type"] == d["subject_type"] and b["vocabulary"] == db["vocabulary"], 6, "decision_declaration_mismatch") need(any(p["purpose"] == b["purpose"] for p in db["purposes"]), 6, "decision_purpose_not_declared") def class_of_json(m): """A class as energy-measurement §2.7 reads one from a receipt's JSON members: None for no class; (label, ppm, window, filtering, reference) otherwise, an on-chip counter's absent filtering state read as Unknown. A label that cannot be read is a malformed corpus (ValueError), not a verdict.""" label = m.get("energy_provenance") u, f, r = m.get("energy_uncertainty"), m.get("energy_counter_filtering"), m.get("energy_calibration_ref") if label is None: if u is not None or f is not None or r is not None: raise ValueError("evidence without a class") return None if label in ("OnChipCounter", "CalibratedInstrument"): if not isinstance(u, dict): raise ValueError("a refined class without its uncertainty") ppm, win = u["relative_ppm"], u["window_us"] if label == "OnChipCounter": return (label, ppm, win, "Unknown" if f is None else f, None) return (label, ppm, win, None, r) if label not in STRONGEST: raise ValueError("unknown class " + str(label)) return (label, None, None, None, None) def class_of_body(b): """The class a claim's body restates, in class_of_json's form (step 4 has admitted it).""" return class_of_json(b) def figure_view(f): """A `figure` context as (profile, identity, group, {index: (joules, class)}, signer), or None for a receipt that does not verify. A `claim` is an energy claim's wire form, viewed under `moq-jwp:claim-link`; a `view` is given as the verifier holds it.""" if "claim" in f: if not isinstance(f["claim"], str): raise TypeError("a figure claim is given as its wire form") try: e = verify_claim(f["claim"]) except Reject: return None figs = {} if e["class"] == "energy": figs[None] = (e["body"]["joules_micro"], class_of_body(e["body"])) elif e["class"] == "reconstruct-session" and e["body"]["energy"] is not None: # energy-measurement §6.1: a session's figure is its `energy`, with its class. figs[None] = (e["body"]["energy"]["joules_micro"], class_of_json(e["body"]["energy"])) return ("moq-jwp:claim-link", link_hash(e), None, figs, e["signer_pubkey"]) v = f["view"] figs = {x["index"]: (x["joules_micro"], class_of_json(x)) for x in v["figures"]} return (v["profile"], v["identity"], v["group"], figs, v["signer"]) def check_figure(c, view): """§2.4.8 / §2.4.9 against the figure, in order.""" b = c["body"] need(c["class"] in ("measurement", "operating-point"), 6, "context_mismatch") need(view is not None, 6, "context_invalid") profile, identity, group, figs, signer = view need(profile == b["figure_profile"] and identity == c["subject"] and group == b["group"] and b["figure_index"] in figs, 6, "context_mismatch") joules, cls = figs[b["figure_index"]] need(joules == b["joules_micro"], 6, "figure_disagrees") need(cls is not None, 6, "figure_unclassified") need(cls == class_of_body(b), 6, "figure_class_disagrees") if c["class"] == "measurement": need(PROFILES[b["figure_profile"]][2], 6, "figure_signer_unknown") need(signer == c["signer_pubkey"], 6, "figure_signer_mismatch") def held_previous(ctx): """§3.1 step 5's previous claim: one that does not itself pass steps 1-4 is not held — failing steps 1-3 it is not known to be its signer's, failing step 4 it breaks its class's rules.""" if "previous" not in ctx: return None if not isinstance(ctx["previous"], str): raise TypeError("a previous claim is given as its wire form") try: return verify_claim(ctx["previous"]) except Reject: return None def recover(ctx): """§3.2. Returns the ancestor; raises Reject.""" d = verify_claim(ctx["declaration"]) need(d["class"] == "soft-binding", 6, "context_mismatch") need(d["signer_pubkey"] == ctx["publisher"], 6, "recovery_signer_mismatch") need(ctx.get("observed_hash") != d["subject"], 6, "recovery_of_hard_bound_object") match(d["body"]["alg"], d["body"]["value"], ctx["observed_value"], ctx.get("exact_match_algs", [])) return d["subject"] def verdict(v): """This verifier's verdict on one vector: `accept link_hash=` for an accepted claim, `accept ancestor=` for a recovery, or `reject step= reason=