proof-aware-crypto-tooling-.../provider/src/pacta_provider/cli.py

138 lines
5.3 KiB
Python
Raw Normal View History

2026-07-03 11:03:58 +00:00
from __future__ import annotations
import argparse
import json
from pathlib import Path
from pacta.config import load_config
from pacta.signing import generate_ed25519_keypair
from pacta.yamlio import dump_data
from .discovery import discover_toolchains
from .service import build_attestation
2026-07-03 12:09:34 +00:00
from .transparency_log import TransparencyLog
2026-07-03 11:03:58 +00:00
def main(argv: list[str] | None = None) -> int:
parser = build_parser()
args = parser.parse_args(argv)
try:
return args.func(args)
except Exception as exc:
print(f"error: {exc}")
return 2
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="pacta-provider", description="PACTA proof-checking attestation provider.")
sub = parser.add_subparsers(dest="command", required=True)
discover = sub.add_parser("discover", help="Find reusable local Lean/Aeneas toolchains.")
discover.add_argument("--root", action="append")
discover.add_argument("--max-depth", type=int, default=6)
discover.set_defaults(func=cmd_discover)
init_key = sub.add_parser("init-key", help="Create an Ed25519 provider signing keypair.")
init_key.add_argument("--key-dir", default="provider/state/local-provider")
init_key.set_defaults(func=cmd_init_key)
check = sub.add_parser("check", help="Run proof checks and emit a signed attestation.")
check.add_argument("--config", required=True)
check.add_argument("--repo-name", required=True)
check.add_argument("--repo", required=True)
check.add_argument("--provider", required=True)
check.add_argument("--private-key", required=True)
check.add_argument("--public-key", required=True)
check.add_argument("--env-script")
check.add_argument("--lean-project-dir")
check.add_argument("--timeout", type=int, default=120)
check.add_argument("--log-dir", default="provider/out/logs")
check.add_argument("--out", required=True)
check.set_defaults(func=cmd_check)
2026-07-03 12:09:34 +00:00
log_init = sub.add_parser("log-init", help="Initialize a local RFC9162-style transparency log.")
log_init.add_argument("--log-dir", default="provider/state/transparency-log")
log_init.add_argument("--provider", required=True)
log_init.add_argument("--public-key", required=True)
log_init.set_defaults(func=cmd_log_init)
log_append = sub.add_parser("log-append", help="Append a signed proof-check attestation and emit an inclusion receipt.")
log_append.add_argument("--log-dir", default="provider/state/transparency-log")
log_append.add_argument("--attestation", required=True)
log_append.add_argument("--private-key", required=True)
log_append.add_argument("--public-key", required=True)
log_append.add_argument("--out", required=True)
log_append.set_defaults(func=cmd_log_append)
log_sth = sub.add_parser("log-sth", help="Sign and print the latest transparency-log tree head.")
log_sth.add_argument("--log-dir", default="provider/state/transparency-log")
log_sth.add_argument("--private-key", required=True)
log_sth.add_argument("--public-key", required=True)
log_sth.set_defaults(func=cmd_log_sth)
2026-07-03 11:03:58 +00:00
return parser
def cmd_discover(args: argparse.Namespace) -> int:
candidates = discover_toolchains(args.root, max_depth=args.max_depth)
print(json.dumps([candidate.to_dict() for candidate in candidates], indent=2))
return 0 if candidates else 1
def cmd_init_key(args: argparse.Namespace) -> int:
key_dir = Path(args.key_dir)
private_key = key_dir / "provider.ed25519.key"
public_key = key_dir / "provider.ed25519.pub"
if private_key.exists() or public_key.exists():
raise ValueError(f"Refusing to overwrite existing key files in {key_dir}")
generate_ed25519_keypair(private_key, public_key)
print(f"private_key: {private_key}")
print(f"public_key: {public_key}")
return 0
def cmd_check(args: argparse.Namespace) -> int:
config = load_config(args.config)
repo = config.repo_named(args.repo_name)
attestation = build_attestation(
repo,
args.repo,
provider=args.provider,
private_key=args.private_key,
public_key=args.public_key,
env_script=args.env_script,
lean_project_dir=args.lean_project_dir,
timeout=args.timeout,
log_dir=args.log_dir,
)
dump_data(attestation, args.out)
print(f"attestation: {args.out}")
return 0
2026-07-03 12:09:34 +00:00
def cmd_log_init(args: argparse.Namespace) -> int:
metadata = TransparencyLog(args.log_dir).init(args.provider, args.public_key)
print(json.dumps(metadata, indent=2, sort_keys=True))
return 0
def cmd_log_append(args: argparse.Namespace) -> int:
receipt = TransparencyLog(args.log_dir).append_attestation(
args.attestation,
private_key_path=args.private_key,
public_key_path=args.public_key,
receipt_out=args.out,
)
print(f"receipt: {args.out}")
print(f"log_id: {receipt['log_id']}")
print(f"tree_size: {receipt['tree_size']}")
print(f"leaf_hash: {receipt['leaf_hash']}")
print(f"ed25519_sth_signature: {receipt['sth']['signatures']['ed25519']['status']}")
print(f"ml_dsa_sth_signature: {receipt['sth']['signatures']['ml_dsa']['status']}")
return 0
def cmd_log_sth(args: argparse.Namespace) -> int:
sth = TransparencyLog(args.log_dir).latest_sth(args.private_key, args.public_key)
print(json.dumps(sth, indent=2, sort_keys=True))
return 0