idea19-missionledger-verifi.../missionledger/cli.py

107 lines
3.8 KiB
Python

"""Small CLI for Mission CSFD operations.
Usage:
python -m missionledger.cli generate --plandelta plan.json
python -m missionledger.cli inspect --plandelta plan.json
python -m missionledger.cli sign --plandelta plan.json --keyfile key.bin
python -m missionledger.cli verify --plandelta plan.json --signature SIGHEX --vkfile vk.bin
This is a lightweight tool meant for demos and tests. For production use,
integrate with a proper key management system and secure proof stores.
"""
from __future__ import annotations
import argparse
import json
import sys
from typing import Optional
from .dsl import PlanDelta
from .csfd import generate_csfd, sign_csfd, verify_csfd_signature
def load_plandelta(path: str) -> PlanDelta:
with open(path, "rb") as f:
data = json.load(f)
# allow plain dicts matching PlanDelta fields
return PlanDelta(delta=data.get("delta", {}), version=int(data.get("version", 0)), metadata=data.get("metadata", {}))
def cmd_generate(args: argparse.Namespace) -> int:
pd = load_plandelta(args.plandelta)
csfd = generate_csfd(pd, signer=args.signer or "", n_frames=args.nframes)
print(json.dumps({"frames": [f.__dict__ for f in csfd.frames], "signer": csfd.signer, "digest_hex": csfd.digest_hex}, sort_keys=True, separators=(",", ":")))
return 0
def cmd_inspect(args: argparse.Namespace) -> int:
# inspect is same as generate but pretty-prints
pd = load_plandelta(args.plandelta)
csfd = generate_csfd(pd, signer=args.signer or "", n_frames=args.nframes)
print(json.dumps({"frames": [f.__dict__ for f in csfd.frames], "signer": csfd.signer, "digest_hex": csfd.digest_hex}, indent=2, sort_keys=True))
return 0
def cmd_sign(args: argparse.Namespace) -> int:
pd = load_plandelta(args.plandelta)
csfd = generate_csfd(pd, signer=args.signer or "", n_frames=args.nframes)
# read key bytes
with open(args.keyfile, "rb") as f:
keyb = f.read()
sig = sign_csfd(csfd, keyb)
out = {"signature": sig, "digest_hex": csfd.digest_hex}
print(json.dumps(out, sort_keys=True, separators=(",", ":")))
return 0
def cmd_verify(args: argparse.Namespace) -> int:
pd = load_plandelta(args.plandelta)
csfd = generate_csfd(pd, signer=args.signer or "", n_frames=args.nframes)
with open(args.vkfile, "rb") as f:
vk = f.read()
ok = verify_csfd_signature(csfd, args.signature, vk)
print(json.dumps({"ok": bool(ok)}))
return 0
def main(argv: Optional[list[str]] = None) -> int:
p = argparse.ArgumentParser(prog="mission-csfd", description="Mission CSFD helper CLI (demo)")
sub = p.add_subparsers(dest="cmd")
gen = sub.add_parser("generate")
gen.add_argument("--plandelta", required=True)
gen.add_argument("--signer", required=False)
gen.add_argument("--nframes", type=int, default=3)
gen.set_defaults(func=cmd_generate)
ins = sub.add_parser("inspect")
ins.add_argument("--plandelta", required=True)
ins.add_argument("--signer", required=False)
ins.add_argument("--nframes", type=int, default=3)
ins.set_defaults(func=cmd_inspect)
signp = sub.add_parser("sign")
signp.add_argument("--plandelta", required=True)
signp.add_argument("--keyfile", required=True)
signp.add_argument("--signer", required=False)
signp.add_argument("--nframes", type=int, default=1)
signp.set_defaults(func=cmd_sign)
ver = sub.add_parser("verify")
ver.add_argument("--plandelta", required=True)
ver.add_argument("--signature", required=True)
ver.add_argument("--vkfile", required=True)
ver.add_argument("--signer", required=False)
ver.add_argument("--nframes", type=int, default=1)
ver.set_defaults(func=cmd_verify)
args = p.parse_args(argv)
if not hasattr(args, "func"):
p.print_help()
return 2
return args.func(args)
if __name__ == "__main__":
raise SystemExit(main())