WI-4 (#831): verbatim-hashed normative fragments (B_payload/H_payload) (#844)
All checks were successful
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful

This commit was merged in pull request #844.
This commit is contained in:
2026-07-19 20:34:20 +00:00
parent e4d7d4502d
commit e522b22fa4
3 changed files with 336 additions and 1 deletions

View File

@@ -0,0 +1,176 @@
#!/usr/bin/env python3
"""Fail-closed construction of verbatim-hashed normative fragments.
This is the single construction path for the Claude and Pi adapters. The
payload contains only versioned source metadata and exact validated source
bytes. ``h_payload`` is derived afterwards and is deliberately not representable
as a payload input, preventing cryptographic self-reference.
"""
from __future__ import annotations
import hashlib
import hmac
import struct
from collections.abc import Sequence
from typing import Final
MAX_FRAGMENT_BYTES: Final = 64 * 1024
HASH_DOMAIN_SEPARATOR: Final = b"MOSAIC/H_PAYLOAD/v1\x00"
SOURCE_DOMAIN_SEPARATOR: Final = b"MOSAIC/H_SOURCE/v1\x00"
_PAYLOAD_VERSION_LABEL: Final = b"MOSAIC/B_PAYLOAD/v1"
class NormativeFragment:
"""A source identity, its expected digest, and its exact resolved bytes."""
def __init__(self, source_id: str, content: bytes | None, expected_sha256: str) -> None:
self.source_id = source_id
self.content = content
self.expected_sha256 = expected_sha256
class ConstructionResult:
"""A source-admission decision and, only when admitted, derived payload values.
``promotion`` means the construction has produced the only values a later
receipt protocol may use to attempt promotion. It never performs broker
promotion itself. A REFUSED result has no payload/hash values, so it cannot
advance to that later protocol.
"""
def __init__(
self,
injection_decision: str,
promotion: bool,
source_reason: str | None,
b_payload: bytes | None,
h_payload: str | None,
h_source: str | None,
) -> None:
self.injectionDecision = injection_decision
self.promotion = promotion
self.source_reason = source_reason
self.b_payload = b_payload
self.h_payload = h_payload
self.h_source = h_source
def length_frame(parts: Sequence[bytes]) -> bytes:
"""Encode a finite ordered byte sequence with unambiguous 64-bit framing."""
framed = bytearray(struct.pack(">Q", len(parts)))
for part in parts:
if not isinstance(part, bytes):
raise TypeError("length framing requires bytes")
framed.extend(struct.pack(">Q", len(part)))
framed.extend(part)
return bytes(framed)
def _sha256_hex(payload: bytes) -> str:
return hashlib.sha256(payload).hexdigest()
def _valid_digest(value: object) -> bool:
return (
isinstance(value, str)
and len(value) == 64
and all(character in "0123456789abcdef" for character in value)
)
def _source_reason(fragment: NormativeFragment) -> str | None:
if not isinstance(fragment.source_id, str) or not fragment.source_id:
return "missing"
if fragment.content is None:
return "missing"
if not isinstance(fragment.content, bytes):
return "missing"
if len(fragment.content) > MAX_FRAGMENT_BYTES:
return "oversize"
if not _valid_digest(fragment.expected_sha256):
return "hash-mismatch"
actual = _sha256_hex(fragment.content)
if not hmac.compare_digest(actual, fragment.expected_sha256):
return "hash-mismatch"
return None
def _refused(reason: str) -> ConstructionResult:
return ConstructionResult(
injection_decision="REFUSED",
promotion=False,
source_reason=reason,
b_payload=None,
h_payload=None,
h_source=None,
)
def build_payload(
*,
manifest_version: int,
generator_version: str,
fragments: Sequence[NormativeFragment],
) -> ConstructionResult:
"""Construct B_payload then H_payload after fail-closed source validation.
The sequence order is caller-supplied resolved-source order and is encoded
directly. Changing source order, source identity, metadata, or any exact
fragment byte therefore changes the framed B_payload and its derived hash.
"""
if type(manifest_version) is not int or manifest_version < 0:
return _refused("missing")
if not isinstance(generator_version, str) or not generator_version:
return _refused("missing")
validated = list(fragments)
if not validated:
return _refused("missing")
for fragment in validated:
if not isinstance(fragment, NormativeFragment):
return _refused("missing")
reason = _source_reason(fragment)
if reason is not None:
return _refused(reason)
source_identities = [
length_frame([
fragment.source_id.encode("utf-8"),
fragment.expected_sha256.encode("ascii"),
])
for fragment in validated
]
h_source = _sha256_hex(SOURCE_DOMAIN_SEPARATOR + length_frame(source_identities))
payload_parts = [
_PAYLOAD_VERSION_LABEL,
str(manifest_version).encode("ascii"),
generator_version.encode("utf-8"),
*source_identities,
*(fragment.content for fragment in validated),
]
# h_payload is intentionally not an argument or field in payload_parts.
b_payload = length_frame(payload_parts)
h_payload = _sha256_hex(HASH_DOMAIN_SEPARATOR + length_frame([b_payload]))
return ConstructionResult(
injection_decision="ACCEPTED",
promotion=True,
source_reason=None,
b_payload=b_payload,
h_payload=h_payload,
h_source=h_source,
)
def build_for_claude(**kwargs: object) -> ConstructionResult:
"""Claude adapter entrypoint; delegates to the sole shared constructor."""
return build_payload(**kwargs) # type: ignore[arg-type]
def build_for_pi(**kwargs: object) -> ConstructionResult:
"""Pi adapter entrypoint; delegates to the sole shared constructor."""
return build_payload(**kwargs) # type: ignore[arg-type]

View File

@@ -25,7 +25,7 @@
"lint": "eslint src",
"typecheck": "tsc --noEmit",
"test": "vitest run --passWithNoTests && pnpm run test:framework-shell",
"test:framework-shell": "python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh"
"test:framework-shell": "python3 src/lease-broker/daemon_deadline_unittest.py && python3 src/lease-broker/normative_fragments_unittest.py && python3 src/mutator-gate/runtime_tools_unittest.py && python3 src/mutator-gate/runtime_launch_guard_unittest.py && python3 framework/tools/lease-broker/check-runtime-launches.py --root ../.. && bash framework/tools/codex/test-pr-diff-context.sh"
},
"dependencies": {
"@mosaicstack/brain": "workspace:*",

View File

@@ -0,0 +1,159 @@
#!/usr/bin/env python3
"""RED-first contract tests for verbatim-hashed normative fragments."""
from __future__ import annotations
import hashlib
import importlib.util
import tempfile
import unittest
from pathlib import Path
MODULE_PATH = Path(__file__).parents[2] / "framework/tools/lease-broker/normative_fragments.py"
def shipped_module():
# Each test reaches the shipped implementation; no test doubles or local
# reimplementation of construction are allowed on this admission surface.
assert MODULE_PATH.is_file(), f"shipped construction module is missing: {MODULE_PATH}"
spec = importlib.util.spec_from_file_location("normative_fragments", MODULE_PATH)
if spec is None or spec.loader is None:
raise RuntimeError("unable to load normative fragment construction")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def fragment(module, source_id: str, content: bytes):
return module.NormativeFragment(
source_id=source_id,
content=content,
expected_sha256=hashlib.sha256(content).hexdigest(),
)
def valid_fragments(module):
return [
fragment(module, "authority/constitution", b"Constitution\n"),
fragment(module, "authority/runtime", b"Runtime\n"),
]
class NormativeFragmentsTest(unittest.TestCase):
def test_t24_claude_and_pi_builders_are_byte_identical_and_one_way(self) -> None:
module = shipped_module()
fragments = valid_fragments(module)
claude = module.build_for_claude(
manifest_version=1,
generator_version="wi4-test",
fragments=fragments,
)
pi = module.build_for_pi(
manifest_version=1,
generator_version="wi4-test",
fragments=fragments,
)
self.assertEqual(claude.injectionDecision, "ACCEPTED")
self.assertTrue(claude.promotion)
self.assertEqual(claude.b_payload, pi.b_payload)
self.assertEqual(claude.h_payload, pi.h_payload)
self.assertEqual(
claude.h_payload,
hashlib.sha256(module.HASH_DOMAIN_SEPARATOR + module.length_frame([claude.b_payload])).hexdigest(),
)
self.assertNotIn(b"h_payload", claude.b_payload)
mutated_fragment = module.build_for_claude(
manifest_version=1,
generator_version="wi4-test",
fragments=[
fragment(module, "authority/constitution", b"Constitution changed\n"),
fragment(module, "authority/runtime", b"Runtime\n"),
],
)
mutated_metadata = module.build_for_claude(
manifest_version=2,
generator_version="wi4-test",
fragments=fragments,
)
self.assertNotEqual(claude.h_payload, mutated_fragment.h_payload)
self.assertNotEqual(claude.h_payload, mutated_metadata.h_payload)
def test_length_framing_and_domain_separation_prevent_ambiguous_construction(self) -> None:
module = shipped_module()
left = module.length_frame([b"ab", b"c"])
right = module.length_frame([b"a", b"bc"])
self.assertNotEqual(left, right)
self.assertNotEqual(
hashlib.sha256(module.HASH_DOMAIN_SEPARATOR + left).hexdigest(),
hashlib.sha256(module.HASH_DOMAIN_SEPARATOR + right).hexdigest(),
)
self.assertNotEqual(
hashlib.sha256(module.HASH_DOMAIN_SEPARATOR + left).hexdigest(),
hashlib.sha256(b"other-context\x00" + left).hexdigest(),
)
def test_source_invalidation_missing_refuses_real_construction_and_promotion(self) -> None:
module = shipped_module()
missing = module.NormativeFragment(
source_id="authority/missing",
content=None,
expected_sha256=hashlib.sha256(b"missing").hexdigest(),
)
result = module.build_for_claude(
manifest_version=1,
generator_version="wi4-test",
fragments=[missing],
)
self.assertEqual(result.injectionDecision, "REFUSED")
self.assertFalse(result.promotion)
self.assertEqual(result.source_reason, "missing")
self.assertIsNone(result.b_payload)
self.assertIsNone(result.h_payload)
def test_source_invalidation_oversize_refuses_real_construction_and_promotion(self) -> None:
module = shipped_module()
content = b"x" * (module.MAX_FRAGMENT_BYTES + 1)
oversize = fragment(module, "authority/oversize", content)
result = module.build_for_pi(
manifest_version=1,
generator_version="wi4-test",
fragments=[oversize],
)
self.assertEqual(result.injectionDecision, "REFUSED")
self.assertFalse(result.promotion)
self.assertEqual(result.source_reason, "oversize")
self.assertIsNone(result.b_payload)
self.assertIsNone(result.h_payload)
def test_source_invalidation_hash_mismatch_refuses_real_construction_and_promotion(self) -> None:
module = shipped_module()
mismatch = module.NormativeFragment(
source_id="authority/hash-mismatch",
content=b"trusted bytes",
expected_sha256=hashlib.sha256(b"different bytes").hexdigest(),
)
result = module.build_for_claude(
manifest_version=1,
generator_version="wi4-test",
fragments=[mismatch],
)
self.assertEqual(result.injectionDecision, "REFUSED")
self.assertFalse(result.promotion)
self.assertEqual(result.source_reason, "hash-mismatch")
self.assertIsNone(result.b_payload)
self.assertIsNone(result.h_payload)
if __name__ == "__main__":
unittest.main()