uofa-demo / src /uofa_cli /adversarial /hash_utils.py
cloudronin's picture
push build context (uofa source + packs + space app)
a28ec65 verified
Raw
History Blame Contribute Delete
1.41 kB
"""SHA-256 hash for the adversarialProvenance block (v1.1 §8.1).
The hash covers the block contents with sorted keys, excluding the
``provenanceBlockHash`` field itself. This lets ``uofa verify`` detect
tampering with the synthetic-marker state without requiring separate
signing infrastructure.
"""
from __future__ import annotations
import hashlib
import json
PROVENANCE_BLOCK_KEY = "adversarialProvenance"
HASH_FIELD = "provenanceBlockHash"
def compute_provenance_block_hash(block: dict) -> str:
"""Return the hex SHA-256 digest of *block* minus the hash field.
The caller is responsible for prepending any ``sha256:`` prefix.
"""
stripped = {k: v for k, v in block.items() if k != HASH_FIELD}
canonical = json.dumps(stripped, sort_keys=True, ensure_ascii=False, separators=(",", ":"))
return hashlib.sha256(canonical.encode("utf-8")).hexdigest()
def verify_provenance_block_hash(block: dict) -> tuple[bool, str, str]:
"""Recompute the block hash and compare it to the stored value.
Returns (ok, stored_hex, recomputed_hex). *stored_hex* has any
``sha256:`` prefix stripped. *ok* is False if the field is missing.
"""
stored_raw = block.get(HASH_FIELD, "")
stored_hex = stored_raw.split(":", 1)[1] if ":" in stored_raw else stored_raw
recomputed = compute_provenance_block_hash(block)
return stored_hex == recomputed, stored_hex, recomputed