canonical.py file-level

at sha256:a · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:6 fix(ISR): default require_independent_second_reviewer to require Opera… · aaronrene · Sep 2, 2026
1 """Canonical JSON and entry hashing (§K9.7)."""
2
3 from __future__ import annotations
4
5 import hashlib
6 import json
7 from typing import Any
8
9
10 def canonical_json(value: Any) -> str:
11 """Serialize ``value`` as canonical JSON for hashing."""
12 return json.dumps(value, ensure_ascii=False, separators=(",", ":"), sort_keys=True)
13
14
15 def _hash_payload(body: dict[str, Any]) -> dict[str, Any]:
16 """Build the canonical-hash payload, excluding ``entry_hash`` and ``provenance.sig``."""
17 payload = {key: val for key, val in body.items() if key != "entry_hash"}
18 provenance = payload.get("provenance")
19 if isinstance(provenance, dict) and "sig" in provenance:
20 stripped = {key: val for key, val in provenance.items() if key != "sig"}
21 payload = {**payload, "provenance": stripped}
22 return payload
23
24
25 def compute_entry_hash(body: dict[str, Any]) -> str:
26 """Compute lowercase hex SHA-256 of canonical JSON without ``entry_hash`` or ``provenance.sig``."""
27 digest = hashlib.sha256(canonical_json(_hash_payload(body)).encode("utf-8")).hexdigest()
28 return digest.lower()