diff --git a/contract-bridge/README.contract.md b/contract-bridge/README.contract.md new file mode 100644 index 0000000..ab48b95 --- /dev/null +++ b/contract-bridge/README.contract.md @@ -0,0 +1,36 @@ +# Biscuit × semantic_byte ZK security (contract-native) + +[Eclipse Biscuit](https://github.com/eclipse-biscuit/biscuit) provides **attenuated +authorization** (Datalog policies on signed tokens). `/contract` provides: + +| Layer | Module | Role | +|-------|--------|------| +| State | `semantic_byte.zig` | 8-bit emergence per cell; seal bit (b5) | +| Hiding | `commit.zig` | commit-reveal over byte streams (honest wall: NOT SNARK) | +| RBAC | `capability.zig` | signed capability ladder + zk-sealed params | +| Identity | `zkproof.zig` | Schnorr PoK of discrete log | +| Deploy | `scripts/deploy.sh` | sign-before-swap wallet gate | + +**Security composition:** semantic bytes define *what* is sealed; commitments hide the +stream until open; Biscuit policies define *who* may deploy/attenuate; `contract-deploy` +enforces the live binary swap under witness. + +## Gates + +```bash +bash scripts/gate.sh +bash ../../../witness/biscuit_semantic_byte_gate.sh +``` + +## Build deploy policy artifact + +```bash +python3 scripts/build_deploy_policy.py --cells 1,42,6,7 +bash ../../../scripts/deploy-biscuit-policy.sh --policy references/upstream/biscuit/out/contract_deploy_policy.json +``` + +## Wallet deploy witness + +```bash +bash ../../../scripts/deploy.sh --witness 'bash witness/biscuit_semantic_byte_gate.sh' +``` \ No newline at end of file diff --git a/contract-bridge/scripts/build_deploy_policy.py b/contract-bridge/scripts/build_deploy_policy.py new file mode 100755 index 0000000..28657a1 --- /dev/null +++ b/contract-bridge/scripts/build_deploy_policy.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +"""Build contract-deploy authorization policy artifact.""" +from __future__ import annotations + +import argparse +import os +import sys + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +sys.path.insert(0, ROOT) + +from semantic_byte_biscuit import policy_bundle, write_policy_artifact # noqa: E402 + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--cells", default="1,42,6,7", help="comma-separated semantic byte values") + ap.add_argument("--out-dir", default=os.path.join(ROOT, "out")) + ap.add_argument("--name", default="contract_deploy_policy") + args = ap.parse_args() + + cells = [int(x.strip()) for x in args.cells.split(",") if x.strip()] + bundle = policy_bundle(cells, scope="contract-deploy") + os.makedirs(args.out_dir, exist_ok=True) + path = os.path.join(args.out_dir, f"{args.name}.json") + write_policy_artifact(path, bundle) + print(path) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) \ No newline at end of file diff --git a/contract-bridge/scripts/gate.sh b/contract-bridge/scripts/gate.sh new file mode 100755 index 0000000..0018154 --- /dev/null +++ b/contract-bridge/scripts/gate.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Runnable gate: biscuit semantic-byte ZK bridge (Python + native zig ground truth). +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" +PY="${PYTHON:-python3}" +CONTRACT_ROOT="$(cd "$ROOT/../../.." && pwd)" + +"$PY" -m unittest test_portability -v +PYTHONPATH="$ROOT" "$PY" scripts/list_security_primitives.py | grep -q semantic_commitment + +echo "== libcontract semantic_byte.zig ==" +(cd "$CONTRACT_ROOT/libcontract" && zig test src/semantic_byte.zig --test-filter 'SemanticByte' >/dev/null) + +echo "== libcontract commit.zig (zk commitment) ==" +(cd "$CONTRACT_ROOT/libcontract" && zig test src/commit.zig >/dev/null) + +echo "== libcontract capability.zig (RBAC ladder) ==" +(cd "$CONTRACT_ROOT/libcontract" && zig test src/capability.zig >/dev/null) + +if "$PY" -c "import biscuit_auth" 2>/dev/null; then + echo "== optional: biscuit-auth python present ==" + "$PY" -c "import biscuit_auth; print('biscuit_auth', biscuit_auth.__name__)" +else + echo "skip: biscuit-auth wheel (policy DSL + zig gates are sufficient)" +fi + +echo "GATE GREEN: biscuit semantic-byte zk security" \ No newline at end of file diff --git a/contract-bridge/scripts/list_security_primitives.py b/contract-bridge/scripts/list_security_primitives.py new file mode 100755 index 0000000..88b1ae8 --- /dev/null +++ b/contract-bridge/scripts/list_security_primitives.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +"""Map Biscuit + /contract ZK security primitives.""" +from __future__ import annotations + +PRIMITIVES = [ + ("semantic_byte", "8-bit emergence state — libcontract/src/semantic_byte.zig"), + ("semantic_commitment", "commit-reveal over byte stream — libcontract/src/commit.zig"), + ("capability", "signed RBAC ladder + zk-sealed params — capability.zig"), + ("biscuit_policy", "Datalog attenuation over committed facts — eclipse-biscuit/biscuit"), + ("zkproof", "Schnorr PoK discrete log — libcontract/src/zkproof.zig (identity)"), + ("contract-deploy", "sign-before-swap deploy gate — scripts/deploy.sh"), +] + +def main() -> None: + print("Biscuit × semantic_byte ZK security (contract-native)") + for name, desc in PRIMITIVES: + print(f"{name}\t{desc}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/contract-bridge/semantic_byte_biscuit.py b/contract-bridge/semantic_byte_biscuit.py new file mode 100644 index 0000000..baa1e58 --- /dev/null +++ b/contract-bridge/semantic_byte_biscuit.py @@ -0,0 +1,148 @@ +"""Biscuit × contract semantic_byte ZK security bridge. + +Layers (native to /contract): + 1. semantic_byte.zig — 8-bit emergence state per cell (seal bit b5) + 2. commit.zig — hiding+binding commitment over byte streams (NOT SNARK; honest wall) + 3. capability.zig — signed RBAC ladder with zk-sealed params + 4. biscuit Datalog — attenuated authorization policies over committed facts + +Biscuit proves *who may act*; semantic bytes prove *what state is sealed*; +commit proves *the stream is fixed* without revealing it until open. +""" +from __future__ import annotations + +import hashlib +import json +import os +import secrets +from dataclasses import dataclass + +HEBREW = "אבגדהוזחטיכלמנסעפצקרשת" +COMMIT_DOMAIN = b"aiko-commit-v1" +BLIND_LEN = 32 + + +@dataclass(frozen=True) +class SemanticByte: + value: int + + def __post_init__(self) -> None: + if not 0 <= self.value <= 255: + raise ValueError(f"out of range: {self.value}") + + @property + def seal_bit(self) -> bool: + return bool(self.value & 32) + + def hebrew(self) -> str: + return HEBREW[self.value % 22] + + def biscuit_fact(self, index: int) -> str: + return f"semantic_byte({index}, {self.value});" + + def biscuit_seal_fact(self, index: int) -> str: + return f"semantic_seal({index});" if self.seal_bit else "" + + +def witness_pair(a: int, b: int) -> int: + return 2 if a == 1 and b == 1 else (a * b) & 0xFF + + +def operator_collapse_at_42(a: int, b: int) -> int: + if (a, b) in ((6, 7), (7, 6)): + return 42 + mul = (a * b) & 0xFF + add = (a + b) & 0xFF + xor = a ^ b + if mul in (add, xor) or add == xor: + return 42 + return mul + + +def commit(value: bytes, blinding: bytes) -> bytes: + if len(blinding) != BLIND_LEN: + raise ValueError("blinding must be 32 bytes") + h = hashlib.sha256() + h.update(COMMIT_DOMAIN) + h.update(blinding) + h.update(value) + return h.digest() + + +def verify_commitment(commitment: bytes, value: bytes, blinding: bytes) -> bool: + return commit(value, blinding) == commitment + + +def commit_semantic_stream(cells: list[int]) -> dict: + """ZK-seal a semantic-byte stream (commit-reveal; mirrors commit.zig).""" + payload = bytes(c & 0xFF for c in cells) + blinding = secrets.token_bytes(BLIND_LEN) + c = commit(payload, blinding) + return { + "commitment": c.hex(), + "blinding": blinding.hex(), + "length": len(cells), + "domain": COMMIT_DOMAIN.decode(), + } + + +def semantic_facts(cells: list[int]) -> list[str]: + facts: list[str] = [] + for i, v in enumerate(cells): + sb = SemanticByte(v) + facts.append(sb.biscuit_fact(i)) + sf = sb.biscuit_seal_fact(i) + if sf: + facts.append(sf) + return facts + + +def deploy_authorization_policy( + *, + commitment_hex: str, + max_byte: int = 42, + scope: str = "contract-deploy", + role: str = "operator", +) -> str: + """Datalog-shaped policy for /contract-deploy attenuation.""" + lines = [ + f'right("{scope}", "{role}");', + f'semantic_commitment("{commitment_hex}");', + "check if semantic_byte($i, $b), $b <= " + str(max_byte) + ";", + "check if semantic_seal($i);", + f'allow if right("{scope}", "{role}"), semantic_commitment("{commitment_hex}");', + ] + return "\n".join(lines) + + +def attenuation_block(parent_max: int, child_max: int) -> str: + """Biscuit attenuation: child token cannot exceed parent byte ceiling.""" + if child_max > parent_max: + raise ValueError("attenuation violation: child exceeds parent") + return "\n".join( + [ + f"check if semantic_byte($i, $b), $b <= {child_max};", + f"// attenuated from parent max {parent_max}", + ] + ) + + +def policy_bundle(cells: list[int], scope: str = "contract-deploy") -> dict: + sealed = commit_semantic_stream(cells) + policy = deploy_authorization_policy( + commitment_hex=sealed["commitment"], + max_byte=max(cells) if cells else 255, + scope=scope, + ) + return { + "sealed": sealed, + "facts": semantic_facts(cells), + "policy": policy, + "attenuation": attenuation_block(max(cells) if cells else 255, 42), + } + + +def write_policy_artifact(path: str, bundle: dict) -> None: + os.makedirs(os.path.dirname(path) or ".", exist_ok=True) + with open(path, "w", encoding="utf-8") as f: + json.dump(bundle, f, indent=2) \ No newline at end of file diff --git a/contract-bridge/test_portability.py b/contract-bridge/test_portability.py new file mode 100644 index 0000000..0c700e9 --- /dev/null +++ b/contract-bridge/test_portability.py @@ -0,0 +1,75 @@ +"""Portability tests — biscuit semantic-byte ZK bridge (no biscuit-auth wheel required).""" +import json +import os +import tempfile +import unittest + +from semantic_byte_biscuit import ( + BLIND_LEN, + SemanticByte, + attenuation_block, + commit, + commit_semantic_stream, + deploy_authorization_policy, + operator_collapse_at_42, + policy_bundle, + semantic_facts, + verify_commitment, + witness_pair, + write_policy_artifact, +) + + +class TestSemanticByteBiscuit(unittest.TestCase): + def test_seal_bit_on_42(self): + sb = SemanticByte(42) + self.assertTrue(sb.seal_bit) + self.assertEqual(sb.hebrew(), "ש") + + def test_witness_and_collapse(self): + self.assertEqual(witness_pair(1, 1), 2) + self.assertEqual(operator_collapse_at_42(6, 7), 42) + + def test_commit_round_trip(self): + val = b"semantic-byte-stream" + blind = bytes(range(BLIND_LEN)) + c = commit(val, blind) + self.assertTrue(verify_commitment(c, val, blind)) + self.assertFalse(verify_commitment(c, b"tampered", blind)) + + def test_stream_commitment(self): + cells = [1, 2, 42, 255] + sealed = commit_semantic_stream(cells) + self.assertEqual(sealed["length"], 4) + payload = bytes(cells) + blind = bytes.fromhex(sealed["blinding"]) + c = bytes.fromhex(sealed["commitment"]) + self.assertTrue(verify_commitment(c, payload, blind)) + + def test_facts_include_seal(self): + facts = semantic_facts([42, 10]) + self.assertTrue(any("semantic_byte(0, 42)" in f for f in facts)) + self.assertTrue(any("semantic_seal(0)" in f for f in facts)) + + def test_deploy_policy_mentions_commitment(self): + sealed = commit_semantic_stream([6, 7, 42]) + pol = deploy_authorization_policy(commitment_hex=sealed["commitment"]) + self.assertIn("semantic_commitment", pol) + self.assertIn("contract-deploy", pol) + + def test_attenuation_rejects_escalation(self): + with self.assertRaises(ValueError): + attenuation_block(42, 100) + + def test_policy_bundle_writes(self): + bundle = policy_bundle([1, 42, 6, 7]) + with tempfile.TemporaryDirectory() as td: + path = os.path.join(td, "deploy-policy.json") + write_policy_artifact(path, bundle) + loaded = json.load(open(path, encoding="utf-8")) + self.assertIn("policy", loaded) + self.assertIn("sealed", loaded) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file