build(agent): jabba#56a767 iteration
This commit is contained in:
parent
19b74dc2ca
commit
82b5731339
|
|
@ -4,6 +4,18 @@ from dataclasses import dataclass, asdict
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
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
|
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)
|
csfd = CSFDDigest(frames=frames, signer=signer, digest_hex=digest_hex)
|
||||||
|
|
||||||
return csfd
|
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
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ description = "MVP core for verifiable contract-driven mission planning in inter
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
license = {text = "MIT"}
|
license = {text = "MIT"}
|
||||||
|
dependencies = ["pynacl>=1.4.0"]
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["."]
|
where = ["."]
|
||||||
|
|
|
||||||
|
|
@ -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=(",", ":"))
|
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
|
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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue