Files
ai-contract-platform/yuqei-infra/scripts/prepare-policy-legal-approval.py
T

1269 lines
59 KiB
Python

#!/usr/bin/env python3
"""Prepare, sign, and verify named legal approval and trust requests for public policies."""
from __future__ import annotations
import argparse
import base64
import hashlib
import json
import os
import re
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey, Ed25519PublicKey
PACKAGE_SCHEMA = "yuqei-public-policy-review-package-v1"
PENDING_PAYLOAD_SCHEMA = "yuqei-public-policy-approval-pending-v1"
APPROVED_PAYLOAD_SCHEMA = "yuqei-public-policy-approval-payload-v1"
APPROVAL_SCHEMA = "yuqei-public-policy-legal-approval-v1"
KEYRING_SCHEMA = "yuqei-public-policy-legal-keyring-v2"
TRUST_REQUEST_SCHEMA = "yuqei-public-policy-key-trust-request-v2"
TRUST_APPROVAL_PAYLOAD_SCHEMA = "yuqei-public-policy-key-trust-approval-payload-v2"
TRUST_APPROVAL_SCHEMA = "yuqei-public-policy-key-trust-approval-v2"
SIGNATURE_DOMAIN = b"YUQEI_PUBLIC_POLICY_LEGAL_APPROVAL_V1\0"
TRUST_SIGNATURE_DOMAIN = b"YUQEI_PUBLIC_POLICY_KEY_TRUST_APPROVAL_V2\0"
SIGN_CONFIRMATION = "I_APPROVE_THIS_EXACT_POLICY_PAYLOAD"
TRUST_SIGN_CONFIRMATION = "I_APPROVE_THIS_EXACT_POLICY_KEY_TRUST_PAYLOAD"
TRUST_REVIEW_ATTESTATIONS = [
"confirmed_same_named_reviewer_identity_and_authority",
"verified_legal_signing_key_fingerprint_out_of_band",
"accepts_separate_key_security_or_operations_responsibility",
]
EXPECTED_POLICY_IDS = {
"user_terms",
"privacy_policy",
"service_agreement",
"ai_use_notice",
}
MAX_POLICY_BYTES = 512 * 1024
MAX_JSON_BYTES = 2 * 1024 * 1024
MAX_APPROVAL_WINDOW = timedelta(days=7)
SHA256_PATTERN = re.compile(r"^[0-9a-f]{64}$")
KEY_ID_PATTERN = re.compile(r"^[a-z0-9][a-z0-9._-]{2,127}$")
PROHIBITED_MARKERS = ("TODO", "TBD", "产品草案", "待补充", "待确认")
SOLE_POLICY_REVIEWER = {
"approver_id": "chen-yuqi",
"reviewer": "陈钰麒",
"role": "总经理",
"organization": "内蒙古钰麒科技有限公司",
}
class PolicyApprovalError(ValueError):
pass
def canonical_json(value: Any) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
def sha256_bytes(value: bytes) -> str:
return hashlib.sha256(value).hexdigest()
def parse_timestamp(value: str, *, label: str) -> datetime:
try:
parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
except ValueError as exc:
raise PolicyApprovalError(f"{label} must be an ISO-8601 timestamp") from exc
if parsed.tzinfo is None or parsed.utcoffset() is None:
raise PolicyApprovalError(f"{label} must include a timezone")
return parsed
def normalized_timestamp(value: str, *, label: str) -> str:
return parse_timestamp(value, label=label).isoformat()
def read_bounded(path: Path, *, label: str, max_bytes: int) -> bytes:
try:
size = path.stat().st_size
except OSError as exc:
raise PolicyApprovalError(f"{label} is unavailable: {path}") from exc
if size <= 0 or size > max_bytes:
raise PolicyApprovalError(f"{label} size is outside the allowed range")
return path.read_bytes()
def strict_json(raw: bytes, *, label: str) -> dict[str, Any]:
def reject_duplicates(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
result: dict[str, Any] = {}
for key, value in pairs:
if key in result:
raise PolicyApprovalError(f"{label} contains duplicate key: {key}")
result[key] = value
return result
try:
value = json.loads(raw.decode("utf-8"), object_pairs_hook=reject_duplicates)
except (UnicodeError, json.JSONDecodeError) as exc:
raise PolicyApprovalError(f"{label} is not valid UTF-8 JSON") from exc
if not isinstance(value, dict):
raise PolicyApprovalError(f"{label} must be a JSON object")
return value
def write_atomic(path: Path, raw: bytes, *, private: bool = False) -> None:
path = path.resolve()
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(f".{path.name}.{os.getpid()}.tmp")
try:
temporary.write_bytes(raw)
if private and os.name != "nt":
temporary.chmod(0o600)
temporary.replace(path)
finally:
temporary.unlink(missing_ok=True)
def load_public_key(path: Path) -> tuple[Ed25519PublicKey, bytes, bytes]:
pem = read_bounded(path, label="policy legal public key", max_bytes=64 * 1024)
try:
key = serialization.load_pem_public_key(pem)
except ValueError as exc:
raise PolicyApprovalError("policy legal public key is not valid PEM") from exc
if not isinstance(key, Ed25519PublicKey):
raise PolicyApprovalError("policy legal public key must be Ed25519")
raw = key.public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
return key, pem, raw
def load_policy_material(policy_dir: Path) -> tuple[dict[str, Any], bytes, list[dict[str, Any]]]:
policy_dir = policy_dir.resolve()
manifest_path = policy_dir / "manifest.json"
manifest_raw = read_bounded(manifest_path, label="policy manifest", max_bytes=MAX_JSON_BYTES)
manifest = strict_json(manifest_raw, label="policy manifest")
if manifest.get("schema_version") != "yuqei-public-policy-candidate-v1":
raise PolicyApprovalError("policy manifest schema_version is invalid")
version = _required_text(manifest, "version")
effective_at = normalized_timestamp(_required_text(manifest, "effective_at"), label="effective_at")
if effective_at != manifest["effective_at"]:
raise PolicyApprovalError("effective_at must use canonical ISO-8601 formatting")
publisher = manifest.get("publisher")
if not isinstance(publisher, dict):
raise PolicyApprovalError("policy publisher must be an object")
for field in (
"legal_name",
"unified_social_credit_code",
"registered_address",
"public_contact_email",
"privacy_contact_email",
):
_required_text(publisher, field)
if manifest.get("existing_user_reconsent") not in {"required_on_next_login", "not_required"}:
raise PolicyApprovalError("existing_user_reconsent is invalid")
if manifest.get("approval_window_days") != 7:
raise PolicyApprovalError("policy approval window must be exactly seven days")
documents = manifest.get("documents")
if not isinstance(documents, list) or len(documents) != len(EXPECTED_POLICY_IDS):
raise PolicyApprovalError("policy manifest must bind exactly four documents")
material: list[dict[str, Any]] = []
seen_ids: set[str] = set()
seen_files: set[str] = set()
for document in documents:
if not isinstance(document, dict):
raise PolicyApprovalError("policy document manifest entry must be an object")
policy_id = _required_text(document, "policy_id")
title = _required_text(document, "title")
public_path = _required_text(document, "public_path")
source_file = _required_text(document, "source_file")
if Path(source_file).name != source_file or source_file in seen_files:
raise PolicyApprovalError("policy source_file must be unique and traversal-free")
if policy_id in seen_ids:
raise PolicyApprovalError("policy_id values must be unique")
source_path = policy_dir / source_file
raw = read_bounded(source_path, label=f"policy document {policy_id}", max_bytes=MAX_POLICY_BYTES)
if b"\r" in raw or raw.startswith(b"\xef\xbb\xbf"):
raise PolicyApprovalError(f"policy document {policy_id} must use BOM-free LF UTF-8")
try:
text = raw.decode("utf-8")
except UnicodeError as exc:
raise PolicyApprovalError(f"policy document {policy_id} is not UTF-8") from exc
if not text.startswith(f"# {title}\n"):
raise PolicyApprovalError(f"policy document {policy_id} title does not match manifest")
if version not in text or "2026年8月13日 00:00(北京时间)" not in text:
raise PolicyApprovalError(f"policy document {policy_id} omits version or effective time")
markers = [marker for marker in PROHIBITED_MARKERS if marker in text]
if markers:
raise PolicyApprovalError(f"policy document {policy_id} contains unresolved markers")
material.append(
{
"policy_id": policy_id,
"title": title,
"public_path": public_path,
"source_file": source_file,
"sha256": sha256_bytes(raw),
"size_bytes": len(raw),
}
)
seen_ids.add(policy_id)
seen_files.add(source_file)
if seen_ids != EXPECTED_POLICY_IDS:
raise PolicyApprovalError("policy manifest contains an unexpected policy set")
material.sort(key=lambda item: item["policy_id"])
return manifest, manifest_raw, material
def build_review_package(
*,
policy_dir: Path,
public_key_file: Path,
key_id: str,
approver_id: str,
reviewer: str,
role: str,
organization: str,
prepared_at: str,
key_not_before: str,
key_not_after: str,
) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]:
if not KEY_ID_PATTERN.fullmatch(key_id):
raise PolicyApprovalError("key_id format is invalid")
prepared_at = normalized_timestamp(prepared_at, label="prepared_at")
key_not_before = normalized_timestamp(key_not_before, label="key_not_before")
key_not_after = normalized_timestamp(key_not_after, label="key_not_after")
if parse_timestamp(key_not_after, label="key_not_after") <= parse_timestamp(
key_not_before,
label="key_not_before",
):
raise PolicyApprovalError("key_not_after must be later than key_not_before")
manifest, manifest_raw, documents = load_policy_material(policy_dir)
_, public_pem, public_raw = load_public_key(public_key_file)
identity = {
"approver_id": _nonempty(approver_id, "approver_id"),
"reviewer": _nonempty(reviewer, "reviewer"),
"role": _nonempty(role, "role"),
"organization": _nonempty(organization, "organization"),
}
if identity != SOLE_POLICY_REVIEWER:
raise PolicyApprovalError("policy approval must use the configured sole named reviewer")
if identity["organization"] != manifest["publisher"]["legal_name"]:
raise PolicyApprovalError("approver organization must match the policy publisher")
review_package = {
"schema_version": PACKAGE_SCHEMA,
"status": "pending_named_legal_review",
"prepared_at": prepared_at,
"policy_dir_name": policy_dir.resolve().name,
"policy_version": manifest["version"],
"effective_at": manifest["effective_at"],
"manifest_sha256": sha256_bytes(manifest_raw),
"documents": documents,
"publisher": manifest["publisher"],
"governing_law": manifest["governing_law"],
"dispute_resolution": manifest["dispute_resolution"],
"existing_user_reconsent": manifest["existing_user_reconsent"],
"approval_window_days": manifest["approval_window_days"],
"approver": identity,
"key": {
"key_id": key_id,
"algorithm": "Ed25519",
"public_key_file_sha256": sha256_bytes(public_pem),
"public_key_raw_sha256": sha256_bytes(public_raw),
"not_before": key_not_before,
"not_after": key_not_after,
},
}
review_package_raw = canonical_json(review_package)
pending_payload = {
"schema_version": PENDING_PAYLOAD_SCHEMA,
"status": "pending_named_legal_review",
"prepared_at": prepared_at,
"review_package_sha256": sha256_bytes(review_package_raw),
"policy_version": manifest["version"],
"effective_at": manifest["effective_at"],
"documents": documents,
"publisher": manifest["publisher"],
"governing_law": manifest["governing_law"],
"dispute_resolution": manifest["dispute_resolution"],
"existing_user_reconsent": manifest["existing_user_reconsent"],
"approval_request": {
**identity,
"key_id": key_id,
"algorithm": "Ed25519",
"public_key_file_sha256": sha256_bytes(public_pem),
"public_key_raw_sha256": sha256_bytes(public_raw),
"key_not_before": key_not_before,
"key_not_after": key_not_after,
"maximum_approval_window_seconds": int(MAX_APPROVAL_WINDOW.total_seconds()),
"required_confirmation": SIGN_CONFIRMATION,
},
}
keyring_candidate = {
"schema_version": KEYRING_SCHEMA,
"status": "candidate_not_trusted",
"keys": [
{
**identity,
"key_id": key_id,
"algorithm": "Ed25519",
"public_key_pem": public_pem.decode("ascii"),
"public_key_file_sha256": sha256_bytes(public_pem),
"public_key_raw_sha256": sha256_bytes(public_raw),
"not_before": key_not_before,
"not_after": key_not_after,
"revoked": False,
}
],
}
return review_package, pending_payload, keyring_candidate
def finalize_payload(
pending_payload: dict[str, Any],
*,
approved_at: str,
expires_at: str,
) -> dict[str, Any]:
if pending_payload.get("schema_version") != PENDING_PAYLOAD_SCHEMA:
raise PolicyApprovalError("pending payload schema_version is invalid")
if pending_payload.get("status") != "pending_named_legal_review":
raise PolicyApprovalError("only a pending policy payload can be finalized")
approved = parse_timestamp(approved_at, label="approved_at")
expires = parse_timestamp(expires_at, label="expires_at")
prepared = parse_timestamp(_required_text(pending_payload, "prepared_at"), label="prepared_at")
if approved < prepared:
raise PolicyApprovalError("approved_at cannot precede prepared_at")
if expires <= approved or expires - approved > MAX_APPROVAL_WINDOW:
raise PolicyApprovalError("approval validity must be greater than zero and no longer than seven days")
request = pending_payload.get("approval_request")
if not isinstance(request, dict):
raise PolicyApprovalError("pending approval_request is invalid")
key_not_before = parse_timestamp(_required_text(request, "key_not_before"), label="key_not_before")
key_not_after = parse_timestamp(_required_text(request, "key_not_after"), label="key_not_after")
if approved < key_not_before or expires > key_not_after:
raise PolicyApprovalError("approval window must remain within the signing key validity window")
return {
**pending_payload,
"schema_version": APPROVED_PAYLOAD_SCHEMA,
"status": "approved",
"pending_payload_sha256": sha256_bytes(canonical_json(pending_payload)),
"decision": {
"decision": "approved",
"approved_at": approved.isoformat(),
"expires_at": expires.isoformat(),
"confirmation": SIGN_CONFIRMATION,
},
}
def sign_payload(
payload: dict[str, Any],
*,
private_key_file: Path,
password_file: Path,
confirmation: str,
) -> dict[str, Any]:
if confirmation != SIGN_CONFIRMATION:
raise PolicyApprovalError(f"signing requires confirmation {SIGN_CONFIRMATION}")
if payload.get("schema_version") != APPROVED_PAYLOAD_SCHEMA or payload.get("status") != "approved":
raise PolicyApprovalError("only a finalized approved payload can be signed")
private_raw = read_bounded(private_key_file, label="policy legal private key", max_bytes=64 * 1024)
password = read_bounded(password_file, label="policy legal private key password", max_bytes=4096).strip()
if not password:
raise PolicyApprovalError("policy legal private key password is empty")
try:
key = serialization.load_pem_private_key(private_raw, password=password)
except (TypeError, ValueError) as exc:
raise PolicyApprovalError("policy legal private key or password is invalid") from exc
if not isinstance(key, Ed25519PrivateKey):
raise PolicyApprovalError("policy legal private key must be Ed25519")
public_raw = key.public_key().public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
expected = payload.get("approval_request", {}).get("public_key_raw_sha256")
if sha256_bytes(public_raw) != expected:
raise PolicyApprovalError("private key does not match the approved public key binding")
payload_raw = canonical_json(payload)
signature = key.sign(SIGNATURE_DOMAIN + payload_raw)
approval_id = f"policy-approval-{sha256_bytes(payload_raw + signature)[:24]}"
return {
"schema_version": APPROVAL_SCHEMA,
"approval_id": approval_id,
"payload": payload,
"signature": base64.b64encode(signature).decode("ascii"),
}
def verify_approval(
approval: dict[str, Any],
*,
keyring: dict[str, Any],
policy_dir: Path,
checked_at: str,
allow_candidate_keyring: bool,
) -> dict[str, Any]:
if approval.get("schema_version") != APPROVAL_SCHEMA:
raise PolicyApprovalError("policy approval schema_version is invalid")
payload = approval.get("payload")
if not isinstance(payload, dict) or payload.get("schema_version") != APPROVED_PAYLOAD_SCHEMA:
raise PolicyApprovalError("signed policy approval payload is invalid")
decision = payload.get("decision")
if not isinstance(decision, dict) or decision.get("decision") != "approved":
raise PolicyApprovalError("policy approval decision is not approved")
checked = parse_timestamp(checked_at, label="checked_at")
approved = parse_timestamp(_required_text(decision, "approved_at"), label="approved_at")
expires = parse_timestamp(_required_text(decision, "expires_at"), label="expires_at")
if checked < approved or checked > expires:
raise PolicyApprovalError("policy approval is not valid at checked_at")
manifest, manifest_raw, documents = load_policy_material(policy_dir)
if documents != payload.get("documents"):
raise PolicyApprovalError("policy document bytes do not match the signed payload")
manifest_bindings = {
"policy_version": manifest.get("version"),
"effective_at": manifest.get("effective_at"),
"publisher": manifest.get("publisher"),
"governing_law": manifest.get("governing_law"),
"dispute_resolution": manifest.get("dispute_resolution"),
"existing_user_reconsent": manifest.get("existing_user_reconsent"),
}
if any(payload.get(field) != value for field, value in manifest_bindings.items()):
raise PolicyApprovalError("policy manifest legal terms do not match the signed payload")
request = payload.get("approval_request")
if not isinstance(request, dict):
raise PolicyApprovalError("policy approval_request is invalid")
_require_sole_policy_reviewer(request, label="policy approval")
expected_review_package = {
"schema_version": PACKAGE_SCHEMA,
"status": "pending_named_legal_review",
"prepared_at": _required_text(payload, "prepared_at"),
"policy_dir_name": policy_dir.resolve().name,
"policy_version": manifest["version"],
"effective_at": manifest["effective_at"],
"manifest_sha256": sha256_bytes(manifest_raw),
"documents": documents,
"publisher": manifest["publisher"],
"governing_law": manifest["governing_law"],
"dispute_resolution": manifest["dispute_resolution"],
"existing_user_reconsent": manifest["existing_user_reconsent"],
"approval_window_days": manifest["approval_window_days"],
"approver": {
field: request.get(field)
for field in ("approver_id", "reviewer", "role", "organization")
},
"key": {
"key_id": request.get("key_id"),
"algorithm": request.get("algorithm"),
"public_key_file_sha256": request.get("public_key_file_sha256"),
"public_key_raw_sha256": request.get("public_key_raw_sha256"),
"not_before": request.get("key_not_before"),
"not_after": request.get("key_not_after"),
},
}
if payload.get("review_package_sha256") != sha256_bytes(canonical_json(expected_review_package)):
raise PolicyApprovalError("policy review package does not match the signed payload")
if keyring.get("schema_version") != KEYRING_SCHEMA:
raise PolicyApprovalError("policy keyring schema_version is invalid")
if keyring.get("status") == "trusted":
verify_trusted_keyring_basis(keyring, approval=approval)
elif not (allow_candidate_keyring and keyring.get("status") == "candidate_not_trusted"):
raise PolicyApprovalError("policy keyring is not trusted")
keys = keyring.get("keys")
if not isinstance(keys, list):
raise PolicyApprovalError("policy keyring keys are invalid")
matches = [item for item in keys if isinstance(item, dict) and item.get("key_id") == request.get("key_id")]
if len(matches) != 1:
raise PolicyApprovalError("policy signing key must resolve uniquely")
entry = matches[0]
_require_sole_policy_reviewer(entry, label="policy keyring entry")
if entry.get("revoked") is not False:
raise PolicyApprovalError("policy signing key is revoked")
key_not_before = parse_timestamp(_required_text(entry, "not_before"), label="key not_before")
key_not_after = parse_timestamp(_required_text(entry, "not_after"), label="key not_after")
if approved < key_not_before or expires > key_not_after:
raise PolicyApprovalError("policy approval is outside the signing key validity window")
for field in ("approver_id", "reviewer", "role", "organization", "public_key_raw_sha256"):
if entry.get(field) != request.get(field):
raise PolicyApprovalError(f"policy key identity mismatch: {field}")
public_pem = entry.get("public_key_pem")
if not isinstance(public_pem, str) or not public_pem.strip():
raise PolicyApprovalError("policy keyring public key is invalid")
try:
public_key = serialization.load_pem_public_key(public_pem.encode("ascii"))
except (UnicodeError, ValueError) as exc:
raise PolicyApprovalError("policy keyring public key is invalid") from exc
if not isinstance(public_key, Ed25519PublicKey):
raise PolicyApprovalError("policy keyring public key must be Ed25519")
try:
signature = base64.b64decode(_required_text(approval, "signature"), validate=True)
public_key.verify(signature, SIGNATURE_DOMAIN + canonical_json(payload))
except Exception as exc:
raise PolicyApprovalError("policy approval signature verification failed") from exc
expected_id = f"policy-approval-{sha256_bytes(canonical_json(payload) + signature)[:24]}"
if approval.get("approval_id") != expected_id:
raise PolicyApprovalError("policy approval_id does not match its signed material")
return {
"status": "verified",
"approval_id": expected_id,
"policy_version": payload["policy_version"],
"effective_at": payload["effective_at"],
"checked_at": checked.isoformat(),
"documents": documents,
}
def verify_trusted_keyring_basis(keyring: dict[str, Any], *, approval: dict[str, Any]) -> None:
basis = keyring.get("trust_basis")
if not isinstance(basis, dict):
raise PolicyApprovalError("trusted policy keyring lacks a signed single-reviewer trust basis")
if basis.get("policy_activation_authorized") is not False:
raise PolicyApprovalError("trusted policy keyring must not authorize policy activation")
trust_approval = basis.get("trust_approval")
if not isinstance(trust_approval, dict) or trust_approval.get("schema_version") != TRUST_APPROVAL_SCHEMA:
raise PolicyApprovalError("trusted policy keyring trust approval is invalid")
payload = trust_approval.get("payload")
if not isinstance(payload, dict) or payload.get("schema_version") != TRUST_APPROVAL_PAYLOAD_SCHEMA:
raise PolicyApprovalError("trusted policy keyring trust payload is invalid")
decision = payload.get("decision")
trust_approver = payload.get("trust_approver")
legal_approver = payload.get("legal_approver")
if not isinstance(decision, dict) or not isinstance(trust_approver, dict) or not isinstance(legal_approver, dict):
raise PolicyApprovalError("trusted policy keyring trust bindings are invalid")
_require_sole_policy_reviewer(legal_approver, label="trusted legal approval")
_require_sole_policy_reviewer(trust_approver, label="trusted key confirmation")
if decision.get("decision") != "approve_policy_signing_key_trust":
raise PolicyApprovalError("trusted policy keyring trust decision is invalid")
if decision.get("policy_activation_authorized") is not False:
raise PolicyApprovalError("trusted policy keyring trust decision must not authorize policy activation")
if decision.get("responsibility") not in {"security", "operations"}:
raise PolicyApprovalError("trusted policy keyring trust responsibility is invalid")
if decision.get("attestations") != TRUST_REVIEW_ATTESTATIONS:
raise PolicyApprovalError("trusted policy keyring trust attestations are incomplete")
if payload.get("policy_approval_id") != approval.get("approval_id") or payload.get(
"policy_approval_sha256"
) != sha256_bytes(canonical_json(approval)):
raise PolicyApprovalError("trusted policy keyring does not authorize this exact policy approval")
trusted_at = parse_timestamp(_required_text(basis, "trusted_at"), label="trusted_at")
approved = parse_timestamp(_required_text(decision, "approved_at"), label="approved_at")
expires = parse_timestamp(_required_text(decision, "expires_at"), label="expires_at")
if trusted_at < approved or trusted_at > expires:
raise PolicyApprovalError("trusted policy keyring was issued outside its approval window")
candidate = {key: value for key, value in keyring.items() if key != "trust_basis"}
candidate["status"] = "candidate_not_trusted"
candidate_sha256 = sha256_bytes(canonical_json(candidate))
if payload.get("candidate_keyring_sha256") != candidate_sha256:
raise PolicyApprovalError("trusted policy keyring candidate binding does not match")
if basis.get("trust_request_sha256") != payload.get("trust_request_sha256"):
raise PolicyApprovalError("trusted policy keyring trust request binding does not match")
if basis.get("trust_approval_sha256") != sha256_bytes(canonical_json(trust_approval)):
raise PolicyApprovalError("trusted policy keyring trust approval hash does not match")
keys = candidate.get("keys")
if not isinstance(keys, list) or len(keys) != 1 or not isinstance(keys[0], dict):
raise PolicyApprovalError("trusted policy keyring candidate key set is invalid")
legal_key = keys[0]
_require_sole_policy_reviewer(legal_key, label="trusted policy keyring entry")
for field in ("approver_id", "reviewer", "role", "organization"):
if legal_approver.get(field) != legal_key.get(field):
raise PolicyApprovalError(
f"trusted policy keyring legal approval identity mismatch: {field}"
)
if trust_approver.get(field) != legal_key.get(field):
raise PolicyApprovalError(
f"trusted policy keyring does not use the same named reviewer: {field}"
)
if trust_approver.get("public_key_raw_sha256") == legal_key.get("public_key_raw_sha256"):
raise PolicyApprovalError("trusted policy keyring must use a separate trust key")
public_pem = basis.get("trust_approver_public_key_pem")
if not isinstance(public_pem, str) or not public_pem.strip():
raise PolicyApprovalError("trusted policy keyring trust approver public key is invalid")
try:
public_key = serialization.load_pem_public_key(public_pem.encode("ascii"))
except (UnicodeError, ValueError) as exc:
raise PolicyApprovalError("trusted policy keyring trust approver public key is invalid") from exc
if not isinstance(public_key, Ed25519PublicKey):
raise PolicyApprovalError("trusted policy keyring trust approver key must be Ed25519")
public_raw = public_key.public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
if sha256_bytes(public_pem.encode("ascii")) != trust_approver.get("public_key_file_sha256") or sha256_bytes(
public_raw
) != trust_approver.get("public_key_raw_sha256"):
raise PolicyApprovalError("trusted policy keyring trust approver key binding does not match")
try:
signature = base64.b64decode(_required_text(trust_approval, "signature"), validate=True)
public_key.verify(signature, TRUST_SIGNATURE_DOMAIN + canonical_json(payload))
except Exception as exc:
raise PolicyApprovalError("trusted policy keyring trust approval signature verification failed") from exc
expected_id = f"policy-key-trust-approval-{sha256_bytes(canonical_json(payload) + signature)[:24]}"
if trust_approval.get("trust_approval_id") != expected_id or basis.get("trust_approval_id") != expected_id:
raise PolicyApprovalError("trusted policy keyring trust approval_id does not match")
def build_trust_request(
approval: dict[str, Any],
*,
candidate_keyring: dict[str, Any],
policy_dir: Path,
requested_at: str,
) -> dict[str, Any]:
if candidate_keyring.get("schema_version") != KEYRING_SCHEMA:
raise PolicyApprovalError("candidate policy keyring schema_version is invalid")
if candidate_keyring.get("status") != "candidate_not_trusted":
raise PolicyApprovalError("trust request requires a candidate_not_trusted keyring")
requested_at = normalized_timestamp(requested_at, label="requested_at")
verified = verify_approval(
approval,
keyring=candidate_keyring,
policy_dir=policy_dir,
checked_at=requested_at,
allow_candidate_keyring=True,
)
payload = approval["payload"]
request = payload["approval_request"]
decision = payload["decision"]
return {
"schema_version": TRUST_REQUEST_SCHEMA,
"status": "pending_single_reviewer_trust_approval",
"requested_at": requested_at,
"activation_authorized": False,
"approval_id": verified["approval_id"],
"approval_sha256": sha256_bytes(canonical_json(approval)),
"candidate_keyring_sha256": sha256_bytes(canonical_json(candidate_keyring)),
"policy_version": verified["policy_version"],
"effective_at": verified["effective_at"],
"approval_expires_at": normalized_timestamp(
_required_text(decision, "expires_at"),
label="expires_at",
),
"documents": verified["documents"],
"key": {
field: request[field]
for field in (
"key_id",
"algorithm",
"approver_id",
"reviewer",
"role",
"organization",
"public_key_file_sha256",
"public_key_raw_sha256",
)
},
"required_reviewer_actions": [
"confirm_the_named_reviewer_identity_and_authority",
"verify_public_key_fingerprint_out_of_band",
"record_one_named_security_or_operations_confirmation",
"install_a_separate-key_trusted_keyring",
"rerun_policy_approval_verification_with_candidate_override_disabled",
],
"prohibited_automatic_actions": [
"mark_candidate_keyring_trusted",
"publish_policy_documents",
"change_policy_lifecycle_to_effective",
"start_edge",
"change_public_traffic",
],
}
def validate_trust_request(
trust_request: dict[str, Any],
*,
approval: dict[str, Any],
candidate_keyring: dict[str, Any],
policy_dir: Path,
checked_at: str,
) -> dict[str, Any]:
if trust_request.get("schema_version") != TRUST_REQUEST_SCHEMA:
raise PolicyApprovalError("policy trust request schema_version is invalid")
if trust_request.get("status") != "pending_single_reviewer_trust_approval":
raise PolicyApprovalError("policy trust request is not pending named-reviewer approval")
if trust_request.get("activation_authorized") is not False:
raise PolicyApprovalError("policy trust request must not authorize activation")
requested_at = _required_text(trust_request, "requested_at")
expected = build_trust_request(
approval,
candidate_keyring=candidate_keyring,
policy_dir=policy_dir,
requested_at=requested_at,
)
if canonical_json(trust_request) != canonical_json(expected):
raise PolicyApprovalError("policy trust request does not match its signed source material")
checked = parse_timestamp(checked_at, label="checked_at")
if checked < parse_timestamp(requested_at, label="requested_at"):
raise PolicyApprovalError("policy trust request cannot be checked before it was requested")
verify_approval(
approval,
keyring=candidate_keyring,
policy_dir=policy_dir,
checked_at=checked.isoformat(),
allow_candidate_keyring=True,
)
return expected
def build_trust_approval_payload(
trust_request: dict[str, Any],
*,
approval: dict[str, Any],
candidate_keyring: dict[str, Any],
policy_dir: Path,
trust_approver_public_key_file: Path,
key_id: str,
approver_id: str,
reviewer: str,
role: str,
organization: str,
responsibility: str,
approved_at: str,
expires_at: str,
key_not_before: str,
key_not_after: str,
) -> dict[str, Any]:
if not KEY_ID_PATTERN.fullmatch(key_id):
raise PolicyApprovalError("trust approver key_id format is invalid")
approved_at = normalized_timestamp(approved_at, label="approved_at")
expires_at = normalized_timestamp(expires_at, label="expires_at")
key_not_before = normalized_timestamp(key_not_before, label="key_not_before")
key_not_after = normalized_timestamp(key_not_after, label="key_not_after")
approved = parse_timestamp(approved_at, label="approved_at")
expires = parse_timestamp(expires_at, label="expires_at")
if expires <= approved or expires - approved > MAX_APPROVAL_WINDOW:
raise PolicyApprovalError("trust approval validity must be greater than zero and no longer than seven days")
if approved < parse_timestamp(key_not_before, label="key_not_before") or expires > parse_timestamp(
key_not_after, label="key_not_after"
):
raise PolicyApprovalError("trust approval window must remain within the trust key validity window")
request = validate_trust_request(
trust_request,
approval=approval,
candidate_keyring=candidate_keyring,
policy_dir=policy_dir,
checked_at=approved_at,
)
legal_expiry = parse_timestamp(_required_text(request, "approval_expires_at"), label="approval_expires_at")
if expires > legal_expiry:
raise PolicyApprovalError("trust approval cannot outlive the legal policy approval")
_, public_pem, public_raw = load_public_key(trust_approver_public_key_file)
identity = {
"approver_id": _nonempty(approver_id, "trust approver_id"),
"reviewer": _nonempty(reviewer, "trust reviewer"),
"role": _nonempty(role, "trust role"),
"organization": _nonempty(organization, "trust organization"),
}
_require_sole_policy_reviewer(identity, label="policy trust confirmation")
responsibility = _nonempty(responsibility, "trust responsibility").lower()
if responsibility not in {"security", "operations"}:
raise PolicyApprovalError("trust responsibility must be security or operations")
legal_key = request.get("key")
if not isinstance(legal_key, dict):
raise PolicyApprovalError("policy trust request legal key binding is invalid")
_require_sole_policy_reviewer(legal_key, label="policy trust request")
public_raw_sha256 = sha256_bytes(public_raw)
for field in ("approver_id", "reviewer", "role", "organization"):
if identity[field] != legal_key.get(field):
raise PolicyApprovalError(
f"trust confirmation must use the same named reviewer: {field}"
)
if public_raw_sha256 == legal_key.get("public_key_raw_sha256"):
raise PolicyApprovalError("trust approver key must differ from the legal approval key")
return {
"schema_version": TRUST_APPROVAL_PAYLOAD_SCHEMA,
"status": "pending_single_reviewer_trust_signature",
"trust_request_sha256": sha256_bytes(canonical_json(request)),
"policy_approval_id": request["approval_id"],
"policy_approval_sha256": request["approval_sha256"],
"candidate_keyring_sha256": request["candidate_keyring_sha256"],
"policy_version": request["policy_version"],
"effective_at": request["effective_at"],
"documents": request["documents"],
"legal_approver": {
field: legal_key[field]
for field in ("approver_id", "reviewer", "role", "organization", "key_id", "public_key_raw_sha256")
},
"trust_approver": {
**identity,
"key_id": key_id,
"algorithm": "Ed25519",
"public_key_file_sha256": sha256_bytes(public_pem),
"public_key_raw_sha256": public_raw_sha256,
"key_not_before": key_not_before,
"key_not_after": key_not_after,
},
"decision": {
"decision": "approve_policy_signing_key_trust",
"responsibility": responsibility,
"attestations": TRUST_REVIEW_ATTESTATIONS,
"approved_at": approved_at,
"expires_at": expires_at,
"confirmation": TRUST_SIGN_CONFIRMATION,
"policy_activation_authorized": False,
},
}
def sign_trust_approval_payload(
payload: dict[str, Any],
*,
private_key_file: Path,
password_file: Path,
confirmation: str,
) -> dict[str, Any]:
if confirmation != TRUST_SIGN_CONFIRMATION:
raise PolicyApprovalError(f"trust signing requires confirmation {TRUST_SIGN_CONFIRMATION}")
if payload.get("schema_version") != TRUST_APPROVAL_PAYLOAD_SCHEMA:
raise PolicyApprovalError("trust approval payload schema_version is invalid")
if payload.get("status") != "pending_single_reviewer_trust_signature":
raise PolicyApprovalError("only a pending trust approval payload can be signed")
decision = payload.get("decision")
if not isinstance(decision, dict) or decision.get("policy_activation_authorized") is not False:
raise PolicyApprovalError("trust approval payload must not authorize policy activation")
private_raw = read_bounded(private_key_file, label="trust approver private key", max_bytes=64 * 1024)
password = read_bounded(password_file, label="trust approver private key password", max_bytes=4096).strip()
if not password:
raise PolicyApprovalError("trust approver private key password is empty")
try:
key = serialization.load_pem_private_key(private_raw, password=password)
except (TypeError, ValueError) as exc:
raise PolicyApprovalError("trust approver private key or password is invalid") from exc
if not isinstance(key, Ed25519PrivateKey):
raise PolicyApprovalError("trust approver private key must be Ed25519")
public_raw = key.public_key().public_bytes(serialization.Encoding.Raw, serialization.PublicFormat.Raw)
trust_approver = payload.get("trust_approver")
if not isinstance(trust_approver, dict) or sha256_bytes(public_raw) != trust_approver.get(
"public_key_raw_sha256"
):
raise PolicyApprovalError("trust approver private key does not match the payload binding")
payload_raw = canonical_json(payload)
signature = key.sign(TRUST_SIGNATURE_DOMAIN + payload_raw)
approval_id = f"policy-key-trust-approval-{sha256_bytes(payload_raw + signature)[:24]}"
return {
"schema_version": TRUST_APPROVAL_SCHEMA,
"trust_approval_id": approval_id,
"payload": payload,
"signature": base64.b64encode(signature).decode("ascii"),
}
def issue_trusted_keyring(
trust_approval: dict[str, Any],
*,
trust_request: dict[str, Any],
approval: dict[str, Any],
candidate_keyring: dict[str, Any],
policy_dir: Path,
trust_approver_public_key_file: Path,
checked_at: str,
) -> dict[str, Any]:
if trust_approval.get("schema_version") != TRUST_APPROVAL_SCHEMA:
raise PolicyApprovalError("policy key trust approval schema_version is invalid")
payload = trust_approval.get("payload")
if not isinstance(payload, dict) or payload.get("schema_version") != TRUST_APPROVAL_PAYLOAD_SCHEMA:
raise PolicyApprovalError("signed policy key trust approval payload is invalid")
checked_at = normalized_timestamp(checked_at, label="checked_at")
request = validate_trust_request(
trust_request,
approval=approval,
candidate_keyring=candidate_keyring,
policy_dir=policy_dir,
checked_at=checked_at,
)
expected_bindings = {
"trust_request_sha256": sha256_bytes(canonical_json(request)),
"policy_approval_id": request["approval_id"],
"policy_approval_sha256": request["approval_sha256"],
"candidate_keyring_sha256": request["candidate_keyring_sha256"],
"policy_version": request["policy_version"],
"effective_at": request["effective_at"],
"documents": request["documents"],
}
for field, expected in expected_bindings.items():
if payload.get(field) != expected:
raise PolicyApprovalError(f"trust approval binding mismatch: {field}")
decision = payload.get("decision")
if not isinstance(decision, dict) or decision.get("decision") != "approve_policy_signing_key_trust":
raise PolicyApprovalError("policy key trust decision is invalid")
if decision.get("responsibility") not in {"security", "operations"}:
raise PolicyApprovalError("policy key trust responsibility is invalid")
if decision.get("attestations") != TRUST_REVIEW_ATTESTATIONS:
raise PolicyApprovalError("policy key trust attestations are incomplete")
if decision.get("policy_activation_authorized") is not False:
raise PolicyApprovalError("policy key trust approval must not authorize policy activation")
checked = parse_timestamp(checked_at, label="checked_at")
approved = parse_timestamp(_required_text(decision, "approved_at"), label="approved_at")
expires = parse_timestamp(_required_text(decision, "expires_at"), label="expires_at")
if checked < approved or checked > expires:
raise PolicyApprovalError("policy key trust approval is not valid at checked_at")
trust_approver = payload.get("trust_approver")
legal_approver = payload.get("legal_approver")
if not isinstance(trust_approver, dict) or not isinstance(legal_approver, dict):
raise PolicyApprovalError("policy key trust approver bindings are invalid")
expected_payload = build_trust_approval_payload(
trust_request,
approval=approval,
candidate_keyring=candidate_keyring,
policy_dir=policy_dir,
trust_approver_public_key_file=trust_approver_public_key_file,
key_id=_required_text(trust_approver, "key_id"),
approver_id=_required_text(trust_approver, "approver_id"),
reviewer=_required_text(trust_approver, "reviewer"),
role=_required_text(trust_approver, "role"),
organization=_required_text(trust_approver, "organization"),
responsibility=_required_text(decision, "responsibility"),
approved_at=_required_text(decision, "approved_at"),
expires_at=_required_text(decision, "expires_at"),
key_not_before=_required_text(trust_approver, "key_not_before"),
key_not_after=_required_text(trust_approver, "key_not_after"),
)
if canonical_json(payload) != canonical_json(expected_payload):
raise PolicyApprovalError("policy key trust approval payload does not match its source material")
public_key, public_pem, public_raw = load_public_key(trust_approver_public_key_file)
if sha256_bytes(public_pem) != trust_approver.get("public_key_file_sha256") or sha256_bytes(
public_raw
) != trust_approver.get("public_key_raw_sha256"):
raise PolicyApprovalError("trust approver public key does not match the signed payload")
if sha256_bytes(public_raw) == legal_approver.get("public_key_raw_sha256"):
raise PolicyApprovalError("trust approver key must differ from the legal approval key")
try:
signature = base64.b64decode(_required_text(trust_approval, "signature"), validate=True)
public_key.verify(signature, TRUST_SIGNATURE_DOMAIN + canonical_json(payload))
except Exception as exc:
raise PolicyApprovalError("policy key trust approval signature verification failed") from exc
expected_id = f"policy-key-trust-approval-{sha256_bytes(canonical_json(payload) + signature)[:24]}"
if trust_approval.get("trust_approval_id") != expected_id:
raise PolicyApprovalError("policy key trust approval_id does not match its signed material")
return {
**candidate_keyring,
"status": "trusted",
"trust_basis": {
"trust_approval_id": expected_id,
"trust_approval_sha256": sha256_bytes(canonical_json(trust_approval)),
"trust_request_sha256": expected_bindings["trust_request_sha256"],
"trusted_at": checked_at,
"trust_approval": trust_approval,
"trust_approver_public_key_pem": public_pem.decode("ascii"),
"trust_approver": {
field: trust_approver[field]
for field in (
"approver_id",
"reviewer",
"role",
"organization",
"key_id",
"public_key_file_sha256",
"public_key_raw_sha256",
)
},
"policy_activation_authorized": False,
},
}
def _required_text(value: dict[str, Any], key: str) -> str:
return _nonempty(value.get(key), key)
def _require_sole_policy_reviewer(value: dict[str, Any], *, label: str) -> dict[str, str]:
identity = {
field: _required_text(value, field)
for field in ("approver_id", "reviewer", "role", "organization")
}
if identity != SOLE_POLICY_REVIEWER:
raise PolicyApprovalError(
f"{label} must use the configured sole named reviewer"
)
return identity
def _nonempty(value: Any, label: str) -> str:
if not isinstance(value, str) or not value.strip() or value != value.strip():
raise PolicyApprovalError(f"{label} must be a trimmed non-empty string")
return value
def _load_json_file(path: Path, *, label: str) -> dict[str, Any]:
return strict_json(read_bounded(path, label=label, max_bytes=MAX_JSON_BYTES), label=label)
def _prepare_command(args: argparse.Namespace) -> dict[str, Any]:
package, payload, keyring = build_review_package(
policy_dir=args.policy_dir,
public_key_file=args.public_key_file,
key_id=args.key_id,
approver_id=args.approver_id,
reviewer=args.reviewer,
role=args.role,
organization=args.organization,
prepared_at=args.prepared_at or datetime.now(timezone.utc).astimezone().isoformat(),
key_not_before=args.key_not_before,
key_not_after=args.key_not_after,
)
package_raw = canonical_json(package)
payload_raw = canonical_json(payload)
keyring_raw = canonical_json(keyring)
write_atomic(args.review_package_output, package_raw)
write_atomic(args.payload_output, payload_raw)
write_atomic(args.keyring_output, keyring_raw)
return {
"status": "prepared_not_signed",
"review_package_sha256": sha256_bytes(package_raw),
"payload_sha256": sha256_bytes(payload_raw),
"keyring_sha256": sha256_bytes(keyring_raw),
"documents": package["documents"],
}
def _finalize_command(args: argparse.Namespace) -> dict[str, Any]:
pending = _load_json_file(args.pending_payload_file, label="pending policy payload")
payload = finalize_payload(pending, approved_at=args.approved_at, expires_at=args.expires_at)
raw = canonical_json(payload)
write_atomic(args.output, raw)
return {"status": "finalized_not_signed", "payload_sha256": sha256_bytes(raw)}
def _sign_command(args: argparse.Namespace) -> dict[str, Any]:
payload = _load_json_file(args.payload_file, label="final policy payload")
approval = sign_payload(
payload,
private_key_file=args.private_key_file,
password_file=args.private_key_password_file,
confirmation=args.confirmation,
)
raw = canonical_json(approval)
write_atomic(args.output, raw, private=True)
return {
"status": "signed",
"approval_id": approval["approval_id"],
"approval_sha256": sha256_bytes(raw),
}
def _verify_command(args: argparse.Namespace) -> dict[str, Any]:
approval = _load_json_file(args.approval_file, label="policy approval")
keyring = _load_json_file(args.keyring_file, label="policy keyring")
return verify_approval(
approval,
keyring=keyring,
policy_dir=args.policy_dir,
checked_at=args.checked_at or datetime.now(timezone.utc).astimezone().isoformat(),
allow_candidate_keyring=args.allow_candidate_keyring,
)
def _prepare_trust_request_command(args: argparse.Namespace) -> dict[str, Any]:
approval = _load_json_file(args.approval_file, label="policy approval")
keyring = _load_json_file(args.candidate_keyring_file, label="candidate policy keyring")
request = build_trust_request(
approval,
candidate_keyring=keyring,
policy_dir=args.policy_dir,
requested_at=args.requested_at or datetime.now(timezone.utc).astimezone().isoformat(),
)
raw = canonical_json(request)
write_atomic(args.output, raw)
return {
"status": request["status"],
"trust_request_sha256": sha256_bytes(raw),
"approval_id": request["approval_id"],
"activation_authorized": False,
}
def _prepare_trust_approval_command(args: argparse.Namespace) -> dict[str, Any]:
trust_request = _load_json_file(args.trust_request_file, label="policy trust request")
approval = _load_json_file(args.approval_file, label="policy approval")
keyring = _load_json_file(args.candidate_keyring_file, label="candidate policy keyring")
payload = build_trust_approval_payload(
trust_request,
approval=approval,
candidate_keyring=keyring,
policy_dir=args.policy_dir,
trust_approver_public_key_file=args.trust_approver_public_key_file,
key_id=args.key_id,
approver_id=args.approver_id,
reviewer=args.reviewer,
role=args.role,
organization=args.organization,
responsibility=args.responsibility,
approved_at=args.approved_at,
expires_at=args.expires_at,
key_not_before=args.key_not_before,
key_not_after=args.key_not_after,
)
raw = canonical_json(payload)
write_atomic(args.output, raw)
return {
"status": payload["status"],
"trust_approval_payload_sha256": sha256_bytes(raw),
"activation_authorized": False,
}
def _sign_trust_approval_command(args: argparse.Namespace) -> dict[str, Any]:
payload = _load_json_file(args.payload_file, label="policy trust approval payload")
trust_approval = sign_trust_approval_payload(
payload,
private_key_file=args.private_key_file,
password_file=args.private_key_password_file,
confirmation=args.confirmation,
)
raw = canonical_json(trust_approval)
write_atomic(args.output, raw, private=True)
return {
"status": "signed_single_reviewer_trust_approval",
"trust_approval_id": trust_approval["trust_approval_id"],
"trust_approval_sha256": sha256_bytes(raw),
"activation_authorized": False,
}
def _issue_trusted_keyring_command(args: argparse.Namespace) -> dict[str, Any]:
trust_approval = _load_json_file(args.trust_approval_file, label="policy key trust approval")
trust_request = _load_json_file(args.trust_request_file, label="policy trust request")
approval = _load_json_file(args.approval_file, label="policy approval")
keyring = _load_json_file(args.candidate_keyring_file, label="candidate policy keyring")
trusted = issue_trusted_keyring(
trust_approval,
trust_request=trust_request,
approval=approval,
candidate_keyring=keyring,
policy_dir=args.policy_dir,
trust_approver_public_key_file=args.trust_approver_public_key_file,
checked_at=args.checked_at or datetime.now(timezone.utc).astimezone().isoformat(),
)
raw = canonical_json(trusted)
write_atomic(args.output, raw)
return {
"status": "trusted_keyring_issued_policy_not_activated",
"trusted_keyring_sha256": sha256_bytes(raw),
"trust_approval_id": trusted["trust_basis"]["trust_approval_id"],
"activation_authorized": False,
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
commands = parser.add_subparsers(dest="command", required=True)
prepare = commands.add_parser("prepare", help="create unsigned policy review material")
prepare.add_argument("--policy-dir", type=Path, required=True)
prepare.add_argument("--public-key-file", type=Path, required=True)
prepare.add_argument("--key-id", required=True)
prepare.add_argument("--approver-id", required=True)
prepare.add_argument("--reviewer", required=True)
prepare.add_argument("--role", required=True)
prepare.add_argument("--organization", required=True)
prepare.add_argument("--prepared-at")
prepare.add_argument("--key-not-before", required=True)
prepare.add_argument("--key-not-after", required=True)
prepare.add_argument("--review-package-output", type=Path, required=True)
prepare.add_argument("--payload-output", type=Path, required=True)
prepare.add_argument("--keyring-output", type=Path, required=True)
finalize = commands.add_parser("finalize-payload", help="bind approval timestamps after review")
finalize.add_argument("--pending-payload-file", type=Path, required=True)
finalize.add_argument("--approved-at", required=True)
finalize.add_argument("--expires-at", required=True)
finalize.add_argument("--output", type=Path, required=True)
sign = commands.add_parser("sign", help="sign a finalized payload with the reviewer-owned key")
sign.add_argument("--payload-file", type=Path, required=True)
sign.add_argument("--private-key-file", type=Path, required=True)
sign.add_argument("--private-key-password-file", type=Path, required=True)
sign.add_argument("--confirmation", required=True)
sign.add_argument("--output", type=Path, required=True)
verify = commands.add_parser("verify", help="verify signature, key identity, and policy bytes")
verify.add_argument("--approval-file", type=Path, required=True)
verify.add_argument("--keyring-file", type=Path, required=True)
verify.add_argument("--policy-dir", type=Path, required=True)
verify.add_argument("--checked-at")
verify.add_argument("--allow-candidate-keyring", action="store_true")
trust = commands.add_parser(
"prepare-trust-request",
help="bind a verified signed approval to a candidate keyring without granting trust",
)
trust.add_argument("--approval-file", type=Path, required=True)
trust.add_argument("--candidate-keyring-file", type=Path, required=True)
trust.add_argument("--policy-dir", type=Path, required=True)
trust.add_argument("--requested-at")
trust.add_argument("--output", type=Path, required=True)
trust_prepare = commands.add_parser(
"prepare-trust-approval",
help="create the exact same-reviewer, separate-key trust confirmation payload",
)
trust_prepare.add_argument("--trust-request-file", type=Path, required=True)
trust_prepare.add_argument("--approval-file", type=Path, required=True)
trust_prepare.add_argument("--candidate-keyring-file", type=Path, required=True)
trust_prepare.add_argument("--policy-dir", type=Path, required=True)
trust_prepare.add_argument("--trust-approver-public-key-file", type=Path, required=True)
trust_prepare.add_argument("--key-id", required=True)
trust_prepare.add_argument("--approver-id", required=True)
trust_prepare.add_argument("--reviewer", required=True)
trust_prepare.add_argument("--role", required=True)
trust_prepare.add_argument("--organization", required=True)
trust_prepare.add_argument("--responsibility", choices=("security", "operations"), required=True)
trust_prepare.add_argument("--approved-at", required=True)
trust_prepare.add_argument("--expires-at", required=True)
trust_prepare.add_argument("--key-not-before", required=True)
trust_prepare.add_argument("--key-not-after", required=True)
trust_prepare.add_argument("--output", type=Path, required=True)
trust_sign = commands.add_parser(
"sign-trust-approval",
help="sign an exact trust confirmation payload with the reviewer's separate trust key",
)
trust_sign.add_argument("--payload-file", type=Path, required=True)
trust_sign.add_argument("--private-key-file", type=Path, required=True)
trust_sign.add_argument("--private-key-password-file", type=Path, required=True)
trust_sign.add_argument("--confirmation", required=True)
trust_sign.add_argument("--output", type=Path, required=True)
trust_issue = commands.add_parser(
"issue-trusted-keyring",
help="verify both approvals and issue a trusted keyring without activating policies",
)
trust_issue.add_argument("--trust-approval-file", type=Path, required=True)
trust_issue.add_argument("--trust-request-file", type=Path, required=True)
trust_issue.add_argument("--approval-file", type=Path, required=True)
trust_issue.add_argument("--candidate-keyring-file", type=Path, required=True)
trust_issue.add_argument("--policy-dir", type=Path, required=True)
trust_issue.add_argument("--trust-approver-public-key-file", type=Path, required=True)
trust_issue.add_argument("--checked-at")
trust_issue.add_argument("--output", type=Path, required=True)
return parser.parse_args()
def main() -> int:
args = parse_args()
try:
if args.command == "prepare":
result = _prepare_command(args)
elif args.command == "finalize-payload":
result = _finalize_command(args)
elif args.command == "sign":
result = _sign_command(args)
elif args.command == "prepare-trust-request":
result = _prepare_trust_request_command(args)
elif args.command == "prepare-trust-approval":
result = _prepare_trust_approval_command(args)
elif args.command == "sign-trust-approval":
result = _sign_trust_approval_command(args)
elif args.command == "issue-trusted-keyring":
result = _issue_trusted_keyring_command(args)
else:
result = _verify_command(args)
except (OSError, PolicyApprovalError) as exc:
print(f"POLICY_LEGAL_APPROVAL_FAIL error={exc}", file=sys.stderr)
return 2
print(json.dumps(result, ensure_ascii=False, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())