build(agent): jabba#56a767 iteration

This commit is contained in:
agent-56a7678c6cd71659 2026-04-29 21:24:03 +02:00
parent 19b74dc2ca
commit 82b5731339
3 changed files with 71 additions and 0 deletions

View File

@ -4,6 +4,18 @@ from dataclasses import dataclass, asdict
from typing import Any, Dict, List
import hashlib
import json
from typing import Optional
try:
from nacl.signing import SigningKey, VerifyKey
from nacl.exceptions import BadSignatureError
_HAS_PYNACL = True
except Exception:
# Keep module import-safe for environments without PyNaCl installed.
SigningKey = None
VerifyKey = None
BadSignatureError = Exception
_HAS_PYNACL = False
from .dsl import PlanDelta
@ -97,3 +109,36 @@ def generate_csfd(plan_delta: PlanDelta, signer: str = "", n_frames: int = 3) ->
csfd = CSFDDigest(frames=frames, signer=signer, digest_hex=digest_hex)
return csfd
def sign_csfd(csfd: CSFDDigest, signing_key_bytes: bytes) -> str:
"""Sign a CSFD digest with an ed25519 private key (SigningKey bytes).
Returns the signature as a hex string. Requires PyNaCl.
"""
if not _HAS_PYNACL:
raise RuntimeError("PyNaCl is required for signing CSFDs")
if not signing_key_bytes:
raise ValueError("signing_key_bytes must be provided")
sk = SigningKey(signing_key_bytes)
# Sign the compact digest_hex to produce a small signature
sig = sk.sign(bytes.fromhex(csfd.digest_hex)).signature
return sig.hex()
def verify_csfd_signature(csfd: CSFDDigest, signature_hex: str, verify_key_bytes: bytes) -> bool:
"""Verify a previously-created signature on the CSFD digest.
Returns True if valid, False otherwise. Requires PyNaCl.
"""
if not _HAS_PYNACL:
raise RuntimeError("PyNaCl is required for signature verification")
vk = VerifyKey(verify_key_bytes)
try:
vk.verify(bytes.fromhex(csfd.digest_hex), bytes.fromhex(signature_hex))
return True
except BadSignatureError:
return False

View File

@ -9,6 +9,7 @@ description = "MVP core for verifiable contract-driven mission planning in inter
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
dependencies = ["pynacl>=1.4.0"]
[tool.setuptools.packages.find]
where = ["."]

View File

@ -31,3 +31,28 @@ def test_csfd_size_bound():
serialized = json.dumps({"frames": [f.__dict__ for f in csfd.frames], "signer": csfd.signer, "digest_hex": csfd.digest_hex}, sort_keys=True, separators=(",", ":"))
assert len(serialized.encode()) <= 512
def test_csfd_signing_and_verification():
import pytest
pytest.importorskip("nacl")
# now import local helpers
from missionledger.dsl import PlanDelta
from missionledger.csfd import generate_csfd, sign_csfd, verify_csfd_signature
# create a small plandelta
pd = PlanDelta(delta={"x": 1}, version=1, metadata={"invariants": {"a": True}})
csfd = generate_csfd(pd, signer="s", n_frames=1)
# Generate an ephemeral Ed25519 keypair for signing
from nacl.signing import SigningKey
sk = SigningKey.generate()
sig_hex = sign_csfd(csfd, sk.encode())
assert isinstance(sig_hex, str)
assert len(sig_hex) > 0
vk = sk.verify_key
ok = verify_csfd_signature(csfd, sig_hex, vk.encode())
assert ok is True