#!/usr/bin/env python3 """Read signing authority from evidence already authenticated by cosign. The pinned L2 KMS public key authenticates the policy carried by new signatures. Historical signatures without a policy retain their source-owned policy check. This module does not authenticate unsigned JSON and must run after cosign verify. """ from __future__ import annotations import argparse import base64 import hashlib import json import re from pathlib import Path OWNER = "pingcap-inc/nexuscrm-pingcap-pack" KEY = "sha256:348ad9206d3cc94e68dbd5bbfb422f5e57f75a90c550ffd7d7c4684942fbd8bf" WORKFLOW = OWNER + "/.github/workflows/publish-serviceification-test-stack.yml" BUILDER = OWNER + "/.github/workflows/trusted-publish-serviceification-test-stack.yml@" def unique_object(pairs): result = {} for key, value in pairs: if key in result: raise ValueError(f"duplicate signing-policy field: {key}") result[key] = value return result def select(signatures: list[dict], legacy_policy: bytes) -> tuple[bytes, dict, dict]: candidates = [] for signature in signatures: annotations = signature.get("optional", {}) embedded = annotations.get("io.nexuscrm.signer.policy-base64") if embedded is not None: if not isinstance(embedded, str): raise ValueError("signed L2 policy is not encoded text") raw = base64.b64decode(embedded + "=" * (-len(embedded) % 4), validate=True) else: raw = legacy_policy policy = json.loads(raw, object_pairs_hook=unique_object) digest = "sha256:" + hashlib.sha256(raw).hexdigest() if annotations.get("io.nexuscrm.signer.policy-sha256") != digest: continue if (policy.get("schema_version") != ("nexuscrm.l2-oci-owner-signing-policy/v3" if embedded is not None else "nexuscrm.l2-oci-owner-signing-policy/v2") or policy.get("publisher_state") != "active" or policy.get("owner_repository") != OWNER or policy.get("public_key_sha256") != KEY or policy.get("workflow_path") != WORKFLOW): raise ValueError("signed L2 policy has an invalid authority") builder = policy.get("trusted_workflow_ref", "") if not isinstance(builder, str) or not re.fullmatch(re.escape(BUILDER) + r"(?:[0-9a-f]{40}|refs/heads/main)", builder): raise ValueError("signed L2 builder is not a reviewed workflow identity") source_ref = annotations.get("io.nexuscrm.source.ref", "") workflow = annotations.get("io.nexuscrm.signer.workflow", "") if not isinstance(source_ref, str) or not re.fullmatch(r"refs/heads/[^\s]+", source_ref): raise ValueError("L2 signature has an invalid source ref") if not isinstance(workflow, str) or not re.fullmatch(re.escape(WORKFLOW) + r"@refs/heads/[^\s]+", workflow): raise ValueError("L2 signature has an invalid publisher workflow") if embedded is None and workflow != WORKFLOW + "@" + source_ref: raise ValueError("legacy L2 signature publisher disagrees with source ref") authority = annotations.get("io.nexuscrm.signer.authority-commit", "") if embedded is not None and (not isinstance(authority, str) or not re.fullmatch(r"[0-9a-f]{40}", authority)): raise ValueError("L2 signature lacks the exact signing authority revision") context = {"source_ref": source_ref, "workflow_ref": workflow, "builder_ref": builder, "authority_commit": authority, "policy_sha256": digest} # Multiple signatures after a policy-only re-sign are valid. Select one # deterministically and bind provenance to that same authority below. candidates.append((embedded is None, digest, authority, raw, signature, context)) if not candidates: raise ValueError("no verified L2 signature binds a valid signing policy") _, _, _, raw, signature, context = min(candidates, key=lambda item: item[:3]) return raw, signature, context def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--signatures", type=Path, required=True) parser.add_argument("--legacy-policy", type=Path, required=True) parser.add_argument("--policy-output", type=Path, required=True) parser.add_argument("--signature-output", type=Path, required=True) parser.add_argument("--context-output", type=Path, required=True) args = parser.parse_args() raw, signature, context = select(json.loads(args.signatures.read_text()), args.legacy_policy.read_bytes()) args.policy_output.write_bytes(raw) args.signature_output.write_text(json.dumps([signature])) args.context_output.write_text(json.dumps(context)) if __name__ == "__main__": main()