339 lines
15 KiB
Python
339 lines
15 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""Validate locked upstream metadata and the discovery-only manifest."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
EXPECTED_SDK_COMMIT = "d2e2e585740362976a39fdd5ccf390f199a7bc37"
|
|
EXPECTED_SDK_SHA256 = "ebfb0acb5260511951a80e17db41650c62d20a8caf8659a230b928dc85005984"
|
|
EXPECTED_ELFLDR_COMMIT = "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2"
|
|
EXPECTED_ELFLDR_SHA256 = "092d16ee0ede0c494947efd38d1a17bbd7cc4b022d3858ea898833c188c703e8"
|
|
EXPECTED_PLDMGR_COMMIT = "cfbc70f30f419b09bf2b52283f7409e2d3117ee1"
|
|
EXPECTED_PLDMGR_SHA256 = "518740adbacccb9094fadb07dd424c53ee290f38306449ccc9d6957fdf813c0b"
|
|
|
|
|
|
def fail(message: str) -> None:
|
|
raise ValueError(message)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--locks", type=Path, required=True)
|
|
parser.add_argument("--symbols", type=Path, required=True)
|
|
parser.add_argument("--sbom", type=Path, required=True)
|
|
parser.add_argument("--artifact-schema", type=Path, required=True)
|
|
parser.add_argument("--denylist-schema", type=Path, required=True)
|
|
parser.add_argument("--denylist", type=Path, required=True)
|
|
parser.add_argument("--runtime-profile-schema", type=Path, required=True)
|
|
parser.add_argument("--runtime-profile", type=Path, required=True)
|
|
parser.add_argument("--phase06-audit", type=Path, required=True)
|
|
parser.add_argument("--phase07-audit", type=Path, required=True)
|
|
parser.add_argument("--phase07-proof", type=Path, required=True)
|
|
parser.add_argument("--sdk-stub", type=Path)
|
|
args = parser.parse_args()
|
|
|
|
locks = json.loads(args.locks.read_text(encoding="utf-8"))
|
|
manifest = json.loads(args.symbols.read_text(encoding="utf-8"))
|
|
sbom = json.loads(args.sbom.read_text(encoding="utf-8"))
|
|
artifact_schema = json.loads(args.artifact_schema.read_text(encoding="utf-8"))
|
|
denylist_schema = json.loads(args.denylist_schema.read_text(encoding="utf-8"))
|
|
denylist = json.loads(args.denylist.read_text(encoding="utf-8"))
|
|
runtime_schema = json.loads(
|
|
args.runtime_profile_schema.read_text(encoding="utf-8")
|
|
)
|
|
runtime_profile = json.loads(args.runtime_profile.read_text(encoding="utf-8"))
|
|
phase06_audit = json.loads(args.phase06_audit.read_text(encoding="utf-8"))
|
|
phase07_audit = json.loads(args.phase07_audit.read_text(encoding="utf-8"))
|
|
phase07_proof = json.loads(args.phase07_proof.read_text(encoding="utf-8"))
|
|
|
|
if locks.get("schema_version") != 1:
|
|
fail("unsupported lock schema")
|
|
if locks.get("verified_at") != "2026-07-17":
|
|
fail("upstream verification date is stale")
|
|
sdk = locks["sources"]["ps5_payload_sdk"]
|
|
if sdk["commit"] != EXPECTED_SDK_COMMIT:
|
|
fail("unexpected SDK commit")
|
|
if sdk["asset"]["sha256"] != EXPECTED_SDK_SHA256:
|
|
fail("unexpected SDK asset digest")
|
|
if sdk["asset"]["size"] != 8810966:
|
|
fail("unexpected SDK asset size")
|
|
elfldr = locks["sources"]["ps5_elfldr_installed"]
|
|
if (
|
|
elfldr["commit"] != EXPECTED_ELFLDR_COMMIT
|
|
or elfldr["asset"]["sha256"] != EXPECTED_ELFLDR_SHA256
|
|
or elfldr["asset"]["size"] != 397000
|
|
or elfldr["installed_identity"]["release_asset_hash_match"] is not True
|
|
):
|
|
fail("exact installed elfldr identity changed")
|
|
payload_manager = locks["sources"]["ps5_payload_manager"]
|
|
if (
|
|
payload_manager["commit"] != EXPECTED_PLDMGR_COMMIT
|
|
or payload_manager["asset"]["sha256"] != EXPECTED_PLDMGR_SHA256
|
|
or payload_manager["asset"]["size"] != 2050320
|
|
or payload_manager["installed_identity"]["release_asset_hash_match"] is not True
|
|
):
|
|
fail("exact installed Payload Manager identity changed")
|
|
excluded = locks["sources"]["ps5_elfldr_candidate_not_installed"]
|
|
if (
|
|
excluded["commit"] != "148b71c2fb9155d2550ef6a14eb03433e23acaeb"
|
|
or excluded["installed_identity"]["status"] != "excluded_by_installed_hash"
|
|
or excluded["installed_identity"]["release_asset_hash_match"] is not False
|
|
):
|
|
fail("excluded elfldr candidate status changed")
|
|
|
|
if manifest.get("schema_version") != 1:
|
|
fail("unsupported symbol-manifest schema")
|
|
if manifest.get("purpose") != "read_only_symbol_discovery":
|
|
fail("manifest purpose widened")
|
|
if manifest.get("firmware_allowlist") != ["9.60"]:
|
|
fail("discovery-only firmware allowlist must be exactly ['9.60']")
|
|
|
|
policy = manifest["global_policy"]
|
|
expected_false = ("log_symbol_addresses", "submit", "draw", "dispatch", "flip",
|
|
"mutate_gpu_memory")
|
|
if policy.get("call_resolved_symbols") != "never":
|
|
fail("resolved-symbol call policy changed")
|
|
if any(policy.get(name) is not False for name in expected_false):
|
|
fail("a mutating or address-logging policy was enabled")
|
|
|
|
names = [entry["name"] for entry in manifest["symbols"]]
|
|
if names != sorted(names):
|
|
fail("symbols must remain sorted for deterministic review")
|
|
if len(names) != len(set(names)):
|
|
fail("duplicate symbol")
|
|
for entry in manifest["symbols"]:
|
|
if not re.fullmatch(r"sceGnm[A-Za-z0-9_]+", entry["name"]):
|
|
fail(f"invalid GNM symbol name: {entry['name']}")
|
|
if entry.get("abi_status") != "name_only_unverified":
|
|
fail(f"ABI confidence widened: {entry['name']}")
|
|
if entry.get("call_policy") != "never":
|
|
fail(f"call policy widened: {entry['name']}")
|
|
|
|
if args.sdk_stub is not None:
|
|
stub = args.sdk_stub.read_text(encoding="utf-8")
|
|
missing = [name for name in names if f".global {name}\\n" not in stub]
|
|
if missing:
|
|
fail(f"symbols missing from pinned SDK stub: {', '.join(missing)}")
|
|
|
|
if sbom.get("spdxVersion") != "SPDX-2.3" or sbom.get("dataLicense") != "CC0-1.0":
|
|
fail("unsupported SPDX document metadata")
|
|
packages = {package["name"]: package for package in sbom["packages"]}
|
|
required_packages = {
|
|
"chimera-gfx",
|
|
"PS5 Payload SDK",
|
|
"PS5 Payload Manager",
|
|
"PS5 ELF Loader",
|
|
"PS5 SDL2 fork",
|
|
"RetroArch",
|
|
"actions/checkout",
|
|
"Ubuntu container image",
|
|
}
|
|
if set(packages) != required_packages:
|
|
fail("SBOM package inventory differs from the reviewed direct inventory")
|
|
if packages["chimera-gfx"]["licenseDeclared"] != "GPL-3.0-or-later":
|
|
fail("project license differs from the accepted ADR")
|
|
if packages["PS5 SDL2 fork"]["licenseDeclared"] != "Zlib":
|
|
fail("SDL license inventory is incorrect")
|
|
if artifact_schema.get("$schema") != "https://json-schema.org/draft/2020-12/schema":
|
|
fail("artifact schema draft changed")
|
|
if artifact_schema.get("properties", {}).get("schema_version", {}).get("const") != 1:
|
|
fail("artifact schema version changed")
|
|
execution_required = artifact_schema["properties"]["execution"]["required"]
|
|
if "execution_eligible" not in execution_required:
|
|
fail("artifact schema does not require explicit execution eligibility")
|
|
if denylist_schema.get("properties", {}).get("fail_closed", {}).get("const") is not True:
|
|
fail("denylist schema is not fail-closed")
|
|
if denylist.get("schema_version") != 1 or denylist.get("fail_closed") is not True:
|
|
fail("artifact denylist metadata changed")
|
|
if denylist.get("hash_algorithm") != "sha256":
|
|
fail("artifact denylist must use SHA-256")
|
|
entries = denylist.get("entries")
|
|
if not isinstance(entries, list) or len(entries) != 1:
|
|
fail("artifact denylist must retain the one permanent blocked artifact")
|
|
entry = entries[0]
|
|
if entry.get("sha256") != (
|
|
"4be1c17b4964f2b68c39b5145bc4af4619c32512d60269ecf5c39728b390fa63"
|
|
):
|
|
fail("permanently blocked artifact hash changed")
|
|
if (
|
|
entry.get("status") != "BLOCKED"
|
|
or entry.get("permanent") is not True
|
|
or entry.get("execution_eligible") is not False
|
|
):
|
|
fail("artifact denylist entry is not permanently blocked")
|
|
|
|
if runtime_schema.get("$schema") != "https://json-schema.org/draft/2020-12/schema":
|
|
fail("controlled runtime schema draft changed")
|
|
if runtime_schema.get("properties", {}).get("schema_version", {}).get("const") != 1:
|
|
fail("controlled runtime schema version changed")
|
|
runtime_properties = runtime_schema.get("properties", {})
|
|
if runtime_properties.get("profile", {}).get("const") != "controlled-ps5-runtime":
|
|
fail("controlled runtime schema profile name changed")
|
|
if runtime_properties.get("execution_authorized", {}).get("const") is not False:
|
|
fail("controlled runtime schema can grant execution authority")
|
|
expected_effects_schema = runtime_properties.get("expected_volatile_effects", {})
|
|
if (
|
|
expected_effects_schema.get("type") != "array"
|
|
or expected_effects_schema.get("uniqueItems") is not True
|
|
):
|
|
fail("controlled runtime schema weakens expected volatile effects")
|
|
required_runtime_fields = set(runtime_schema.get("required", []))
|
|
if {
|
|
"artifact",
|
|
"budgets",
|
|
"decision",
|
|
"deployment",
|
|
"effects",
|
|
"execution",
|
|
"execution_authorized",
|
|
"expected_volatile_effects",
|
|
"firmware",
|
|
"hard_blockers",
|
|
"loader",
|
|
"payload_manager",
|
|
"sdk",
|
|
} - required_runtime_fields:
|
|
fail("controlled runtime schema no longer requires all safety metadata")
|
|
|
|
if runtime_profile.get("schema_version") != 1:
|
|
fail("unsupported controlled runtime profile")
|
|
if runtime_profile.get("profile") != "controlled-ps5-runtime":
|
|
fail("controlled runtime profile name changed")
|
|
if runtime_profile.get("decision") != "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT":
|
|
fail("Phase-0.7 controlled runtime is not deployment-ready")
|
|
if runtime_profile.get("execution_authorized") is not False:
|
|
fail("Phase-0.7 profile granted execution authority")
|
|
if runtime_profile["execution"] != {
|
|
"authorized": False,
|
|
"executed": False,
|
|
"execution_eligible": True,
|
|
"transferred": False,
|
|
}:
|
|
fail("Phase-0.7 execution state is inconsistent")
|
|
runtime_artifact = runtime_profile["artifact"]
|
|
if runtime_artifact != {
|
|
"built": True,
|
|
"filename": "chimera-gfx-lifecycle-probe.elf",
|
|
"id": "chimera-gfx-lifecycle-phase07-fw960-v1",
|
|
"sha256": "bfb4a5cc768e162fe4c2fddf41c3978e152722918a39085277fd172cb95a7182",
|
|
"size": 112680,
|
|
"source_commit": "fe08300339a13f899fb78ea404ada381a5cba87c",
|
|
}:
|
|
fail("Phase-0.7 artifact identity changed")
|
|
if runtime_profile["firmware"] != {
|
|
"device_attested": False,
|
|
"evidence": "jens_explicitly_confirmed_exact_9.60",
|
|
"exact": "9.60",
|
|
}:
|
|
fail("Phase-0.7 firmware evidence changed")
|
|
if runtime_profile["budgets"] != {
|
|
"automatic_retry": False,
|
|
"filesystem_write_budget": "controlled_artifact_directory_only",
|
|
"maximum_runtime_ms": 2000,
|
|
"payload_network_access": "none",
|
|
"persistent_write_budget": "controlled_artifact_removable",
|
|
}:
|
|
fail("Phase-0.7 controlled budgets changed")
|
|
if runtime_profile["deployment"] != {
|
|
"installed": False,
|
|
"ready_for_installation": True,
|
|
"rollback_prepared": True,
|
|
}:
|
|
fail("Phase-0.7 deployment state changed")
|
|
if runtime_profile["loader"] != {
|
|
"base_commit": "699e8bcff03e91e8d6ca6eba281af25c5a58d8c2",
|
|
"hardened_commit": "197623058f509eddde18868dafcb92fdcac66464",
|
|
"installed": False,
|
|
"release": "v0.23-chimera-phase07",
|
|
"reproducible": True,
|
|
"sha256": "63e810982471eb40cae3a20aa9df9a0a02892f420e429874fae4e99aa400b561",
|
|
"size": 397000,
|
|
}:
|
|
fail("Phase-0.7 loader identity changed")
|
|
if runtime_profile["payload_manager"] != {
|
|
"base_commit": "cfbc70f30f419b09bf2b52283f7409e2d3117ee1",
|
|
"hardened_commit": "e23d94ff91233aa770e2342800c1467875bdef44",
|
|
"installed": False,
|
|
"release": "v0.3.1-chimera-controlled-phase07",
|
|
"reproducible": True,
|
|
"sha256": "8fecf8241a46246eddbd21e8bb4d875f5d76f1f4f4c6a11384df1f131aa5e5b1",
|
|
"size": 99560,
|
|
}:
|
|
fail("Phase-0.7 Payload Manager identity changed")
|
|
classifications = {
|
|
effect["classification"] for effect in runtime_profile["effects"]
|
|
}
|
|
if {"PERSISTENT_WRITE", "UNBOUNDED_OR_UNKNOWN"} & classifications:
|
|
fail("Phase-0.7 retains a hard effect classification")
|
|
if runtime_profile["hard_blockers"]:
|
|
fail("Phase-0.7 hard blockers are present")
|
|
expected_volatile = sorted(
|
|
effect["id"]
|
|
for effect in runtime_profile["effects"]
|
|
if effect["classification"] == "EXPECTED_VOLATILE_RUNTIME_EFFECT"
|
|
)
|
|
if sorted(runtime_profile["expected_volatile_effects"]) != expected_volatile:
|
|
fail("Phase-0.7 expected volatile effects are inconsistent")
|
|
|
|
if phase06_audit.get("decision") != "BLOCKED_VERSION_OR_UNBOUNDED_EFFECT":
|
|
fail("Phase-0.6 machine audit decision changed")
|
|
if phase06_audit["artifact"] != {
|
|
"built": False,
|
|
"execution_eligible": False,
|
|
"filename": None,
|
|
"sha256": None,
|
|
"size": None,
|
|
}:
|
|
fail("Phase-0.6 machine audit unexpectedly contains an artifact")
|
|
if any(phase06_audit["no_console_actions"].values()):
|
|
fail("Phase-0.6 machine audit claims a forbidden console action")
|
|
binary = phase06_audit["binary_evidence"]
|
|
if binary["dt_needed"] != [
|
|
"libSceLibcInternal.sprx",
|
|
"libSceNet.sprx",
|
|
"libkernel_web.sprx",
|
|
]:
|
|
fail("exact elfldr DT_NEEDED evidence changed")
|
|
if binary["relocations"] != {"relative": 140, "total": 164}:
|
|
fail("exact elfldr relocation evidence changed")
|
|
if binary["tls_present"] is not False:
|
|
fail("exact elfldr TLS evidence changed")
|
|
|
|
phase07_decision = "READY_FOR_HARDENED_RUNTIME_DEPLOYMENT"
|
|
if phase07_audit.get("decision") != phase07_decision:
|
|
fail("Phase-0.7 offline-audit decision changed")
|
|
if any(phase07_audit["ps5_actions"].values()):
|
|
fail("Phase-0.7 audit claims a forbidden PS5 action")
|
|
if phase07_proof.get("decision") != phase07_decision:
|
|
fail("Phase-0.7 proof-matrix decision changed")
|
|
if phase07_proof.get("kernelwrite_free_claim") is not False:
|
|
fail("Phase-0.7 proof matrix incorrectly claims kernelwrite-free")
|
|
phase07_reviews = {
|
|
item["component"]: item["status"] for item in phase07_proof["reviews"]
|
|
}
|
|
if phase07_reviews.get("sdk_patch_init") != "UNSAFE":
|
|
fail("Phase-0.7 proof matrix hides normal CRT startup writes")
|
|
if phase07_reviews.get("firmware_9_60_runtime_behavior") != "UNPROVEN":
|
|
fail("Phase-0.7 proof matrix claims hardware evidence")
|
|
|
|
print(
|
|
f"validated {len(names)} discovery-only symbols, upstream locks, "
|
|
f"{len(packages)} SBOM packages, runtime policy, Phase-0.7 audit, "
|
|
"and permanent denylist"
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
raise SystemExit(main())
|
|
except (KeyError, TypeError, ValueError, json.JSONDecodeError) as error:
|
|
print(f"manifest verification failed: {error}")
|
|
raise SystemExit(1) from error
|