#!/usr/bin/env python3 """An external agent governed through the CAIN E33 sidecar. Imports NOTHING from CAIN: only json, subprocess, base64, hashlib and `cryptography`. It signs each ABI request with its own Ed25519 key. python3 examples/e33/agent_via_sidecar.py """ import base64 import json import subprocess import sys from pathlib import Path from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ed25519 import hashlib ROOT = Path(__file__).resolve().parents[2] DOMAIN = "CAIN42/E33-ABI-REQUEST/v1" def canon(o): return json.dumps(o, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode() def sign(key, body): digest = hashlib.sha256(canon({"domain": DOMAIN, "body": body})).hexdigest() return base64.b64encode(key.sign(digest.encode())).decode() def main() -> int: key = ed25519.Ed25519PrivateKey.generate() pub = base64.b64encode(key.public_key().public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)).decode() p = subprocess.Popen([sys.executable, "-m", "cain45.l5.operating_sidecar"], cwd=ROOT, stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True) p.stdin.write(json.dumps({"bootstrap": {"abi_pub": pub}}) + "\n") p.stdin.flush() hello = json.loads(p.stdout.readline()) ident, now = hello["identity"], hello["now"] results = {} def call(op, args, forge=False): body = {"abi": "cain.gabi/1.0", "op": op, "args": args} k = ed25519.Ed25519PrivateKey.generate() if forge else key p.stdin.write(json.dumps({**body, "signature_b64": sign(k, body)}) + "\n") p.stdin.flush() return json.loads(p.stdout.readline()) results["allowed_write"] = call("request_execution", {"identity": ident, "kind": "invoke_tool", "target": "docs/tools/report", "intent": "write a report", "now": now + 10}) results["out_of_scope"] = call("request_execution", {"identity": ident, "kind": "move_money", "target": "payments/x", "intent": "pay", "now": now + 11}) results["forged_signature"] = call("request_execution", {"identity": ident, "kind": "invoke_tool", "target": "docs/tools/x", "intent": "x", "now": now + 12}, forge=True) results["malformed"] = (p.stdin.write("not json\n"), p.stdin.flush(), json.loads(p.stdout.readline()))[2] pid = results["allowed_write"]["result"]["proof"] results["proof"] = call("produce_proof", {"operation_id": results["allowed_write"]["result"]["operation_id"]}) p.stdin.close() p.wait(timeout=60) summary = {"identity": ident, "allowed": results["allowed_write"]["result"]["state"], "proof_id": pid, "out_of_scope": results["out_of_scope"]["result"]["state"], "forged": results["forged_signature"].get("error"), "malformed": results["malformed"].get("error"), "proof_signed": bool(results["proof"]["result"] and results["proof"]["result"]["signature_b64"])} print(json.dumps(summary)) ok = summary["allowed"] == "REASSESS" and summary["out_of_scope"] == "DENIED" and \ summary["forged"] == "ABI_UNAUTHENTICATED" and summary["malformed"] == "ABI_MALFORMED" and summary["proof_signed"] return 0 if ok else 1 if __name__ == "__main__": sys.exit(main())