Files
chimera-gfx-Public/tools/validate_phase09er_provenance.py
Chimera GFX release export a6037502d7
phase0-ci / build-and-audit (push) Successful in 2m14s
Publish Chimera GFX source
2026-09-03 03:27:14 +02:00

622 lines
25 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-3.0-or-later
"""Validate the host-only Phase-0.9E-R official Y2JB correlation."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
import re
import subprocess
from typing import Any
import zipfile
BASELINE = "3d3151bd12ff0f786c1e1b9af75d7174408e3d2f"
BRANCH = "codex/chimera-gfx-phase09e-r-y2jb-correlation"
PHASE = "PHASE_0_9E_R_OFFICIAL_Y2JB_CORRELATION"
OUTER_NAME = "Y2JB-Autoloader-403-1240.zip"
OUTER_SIZE = 504159435
OUTER_SHA256 = "805e3f87f0c371223619ffc7d3a7b3c0d41a1fae8a8b1171d9e2f162659e8291"
INNER_NAME = "PS5/EXPORT/BACKUP/202606102126_00/archive.dat"
INNER_SIZE = 504365056
INNER_SHA256 = "6439834e8856d45b6d6fe699b74c35ca6985a199ea8ecf3e398c018d37be2d55"
DENYLIST_SHA256 = "e9603b0e3792781ad5b511afb22ef61e3d4fd4c5a16bf928f8609193bcd97783"
UPSTREAM_COMMIT = "0dbbf4e7e0203af7e5d101a3256c634edf4e3ba2"
UPSTREAM_TREE = "c4344f43af7c268337437e6419548dba6f6bc211"
UPSTREAM_REMOTE = "https://github.com/Gezine/Y2JB.git"
PORT_9020_CLASSIFICATION = "PORT_9020_REFERENCE_ONLY"
FINAL_CLASSIFICATION = "LOCAL_BACKUP_NOT_CORRELATED"
PROVENANCE_FIELDS = (
"logical_name",
"artifact_role",
"local_relative_path",
"size",
"sha256",
"file_type",
"source_repository",
"source_commit",
"build_identity",
"version",
"obtained_from",
"evidence_that_it_is_deployed_or_used",
"confidence",
"immutable",
"executable",
"persistent_on_device",
"transferred_per_session",
"required_for_bootstrap",
"required_for_recovery",
)
AUTHORIZATION_FIELDS = (
"device_action_authorized",
"target_build_authorized",
"transfer_authorized",
"execution_authorized",
"installation_authorized",
"lifecycle_authorized",
"autoload_authorized",
"device_write_authorized",
"automatic_retry",
)
IMMUTABLE_HASHES = {
"manifests/runtime/phase-0.8-read-only-preflight.json":
"47d7f452f8799979fe99b3e6d56859f03544112725bf7e5b349eba5ed81b3322",
"manifests/runtime/phase-0.8-remediation.json":
"a9dafed8c83722c43709dcf90ea117c21d6f996ff8fe233810ed08aa20cbe071",
"manifests/runtime/phase-0.9-anti-brick-design.json":
"39fd7c70cae998d9d74e7caf1ff3c19f9f76f7de2e5a50bc09baede4ba53e9e9",
"manifests/runtime/phase-0.9b-observer.json":
"104c4a667ad17f9827fc7276852c6faeef96effb8e3561a5ebc19a62c7d51634",
"manifests/runtime/phase-0.9c-feasibility.json":
"84eb737ff3486d0c5d8b5ecf06809f93bc573497da4919595b4c1817fd34247c",
"manifests/runtime/phase-0.9d-existing-stack-readback.json":
"86e5aaf034685dbe058b71ffeec645b682f0a8cc7d249e8ac0397155233991de",
"manifests/runtime/phase-0.9e-bootstrap-provenance.json":
"5dfa9bfe2ae751b2f0ea0e03c60c1a4471a35452389cf471456e6c54eb4601cf",
"manifests/runtime/phase-0.9e-loader-protocol.json":
"a7ca8b4e8072cb60ad4cd4869f6c508e8014059c8d81ad3b94a8d9e8cd0db4cb",
}
UPSTREAM_FILE_HASHES = {
"README.md": (7500, "16bfdaa624b8b04f4a6a4a7d512ca8473ad2db73e80df39a974267403e34f751"),
"payload_sender.py": (1064, "8c87920c41dbdbd66b9f36ca9509f0d6bef9170f351dd97ff831cfb98e642ec6"),
"log_server.py": (929, "463114fd46479a7286706de13beb3f52221f36f3cbe37dc5a27bdb6104787a98"),
"appinfo_editor.py": (2008, "c1bcb453660f597cbc9026dba76519a4929fc3e967c183deec8a1ca56912e2e8"),
"download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/remotejsloader.js":
(7132, "30cc6d1535549b2a49b47a9e0c85a3444cf84177be54398691694b7c6505f38e"),
"download0/cache/splash_screen/aHR0cHM6Ly93d3cueW91dHViZS5jb20vdHY=/elfldr-ps5-1340.elf":
(397000, "30478bcadb6439e1247451c4ac706b6e1385044dd0f12d6486b6d7057929453b"),
}
DELIVERABLES = (
"docs/runtime/phase-0.9e-r-official-release-correlation.md",
"docs/runtime/phase-0.9e-r-release-source-binding.md",
"docs/runtime/phase-0.9e-r-port9020-source-audit.md",
"docs/runtime/phase-0.9e-r-official-hostsender-audit.md",
"docs/runtime/phase-0.9e-r-provenance-gaps.md",
"docs/approvals/phase-0.9e-r-y2jb-deployed-use-attestation.md",
"manifests/runtime/phase-0.9e-r-release-correlation.json",
"manifests/runtime/phase-0.9e-r-release-correlation.schema.json",
"manifests/runtime/phase-0.9e-r-port9020-audit.json",
"manifests/runtime/phase-0.9e-r-port9020-audit.schema.json",
"tools/validate_phase09er_provenance.py",
"tests/test_phase09er_provenance.py",
"packaging/phase09er/SHA256SUMS.txt",
)
FORBIDDEN_SUFFIXES = {
".c", ".cc", ".cpp", ".s", ".asm", ".ld", ".elf", ".self", ".sprx",
".pkg", ".bin", ".wasm",
}
FORBIDDEN_PREFIXES = ("include/", "src/", "adapters/", "samples/")
def load_json(path: Path) -> dict[str, Any]:
with path.open("r", encoding="utf-8") as stream:
value = json.load(stream)
if not isinstance(value, dict):
raise ValueError(f"{path} does not contain a JSON object")
return value
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def git(root: Path, *args: str) -> str:
result = subprocess.run(
["git", *args],
cwd=root,
check=False,
capture_output=True,
text=True,
)
if result.returncode != 0:
raise RuntimeError(result.stderr.strip() or "git command failed")
return result.stdout.strip()
def candidate_archive_path(root: Path) -> Path:
direct = Path.home() / "Downloads" / OUTER_NAME
if direct.is_file():
return direct
for ancestor in (root, *root.parents):
if ancestor.parent.name.lower() == "users":
return ancestor / "Downloads" / OUTER_NAME
return direct
def official_byte_match(
*,
official_source: bool,
local_name: str,
local_size: int,
local_sha256: str,
asset_name: str,
asset_size: int,
asset_sha256: str,
) -> bool:
"""Only exact official name, byte count, and hash establish a match."""
return (
official_source
and local_name == asset_name
and local_size == asset_size
and local_sha256 == asset_sha256
)
def source_binding(
*, release_associated: bool, inner_bytes_matched: bool, inner_opaque: bool
) -> str:
if inner_bytes_matched:
return "REPRODUCIBLE_CONTENT_BINDING"
if release_associated and inner_opaque:
return "SOURCE_ONLY_ASSOCIATION"
if release_associated:
return "OFFICIAL_RELEASE_ASSOCIATION"
return "NO_LOCAL_RELEASE_ASSOCIATION"
def port_implementation_sufficient(classification: str) -> bool:
return classification in {
"PORT_9020_IMPLEMENTATION_FOUND",
"PORT_9020_IMPLEMENTATION_PARTIAL",
}
def phase09f_design_allowed(
*,
correlation: str,
release_commit_known: bool,
upstream_clean: bool,
port_classification: str,
sender_identified: bool,
attestation_available: bool,
all_authorizations_false: bool,
) -> bool:
return all(
(
correlation == "OFFICIAL_RELEASE_BYTE_MATCH",
release_commit_known,
upstream_clean,
port_implementation_sufficient(port_classification),
sender_identified,
attestation_available,
all_authorizations_false,
)
)
def sender_duplex(*, sends: bool, receives: bool) -> bool:
return sends and receives
def sender_short_send_deficiency(*, uses_sendall: bool, explicit_send_loop: bool) -> bool:
return not (uses_sendall or explicit_send_loop)
def attestation_is_runtime_proof(*, attested: bool, hardware_observed: bool) -> bool:
return attested and hardware_observed
def _type_matches(value: Any, expected: str | list[str]) -> bool:
if isinstance(expected, list):
return any(_type_matches(value, item) for item in expected)
checks = {
"object": isinstance(value, dict),
"array": isinstance(value, list),
"string": isinstance(value, str),
"boolean": isinstance(value, bool),
"integer": isinstance(value, int) and not isinstance(value, bool),
"null": value is None,
}
return checks.get(expected, True)
def validate_schema_instance(
schema: dict[str, Any], instance: Any, path: str = "$"
) -> list[str]:
errors: list[str] = []
expected_type = schema.get("type")
if expected_type is not None and not _type_matches(instance, expected_type):
return [f"{path}: type mismatch"]
if "const" in schema and instance != schema["const"]:
errors.append(f"{path}: const mismatch")
if "enum" in schema and instance not in schema["enum"]:
errors.append(f"{path}: outside enum")
if isinstance(instance, dict):
for required in schema.get("required", []):
if required not in instance:
errors.append(f"{path}: missing {required}")
properties = schema.get("properties", {})
for key, value in instance.items():
if key in properties:
errors.extend(
validate_schema_instance(properties[key], value, f"{path}.{key}")
)
elif isinstance(schema.get("additionalProperties"), dict):
errors.extend(
validate_schema_instance(
schema["additionalProperties"], value, f"{path}.{key}"
)
)
if isinstance(instance, list) and len(instance) < schema.get("minItems", 0):
errors.append(f"{path}: too few items")
return errors
def validate_release_manifest(manifest: dict[str, Any]) -> list[str]:
errors: list[str] = []
expected = {
"phase": PHASE,
"status": "DESIGN_ONLY",
"baseline_commit": BASELINE,
"release_correlation": "OFFICIAL_RELEASE_NO_MATCH",
"final_classification": FINAL_CLASSIFICATION,
"identified_release": None,
"identified_release_asset": None,
"inner_content_binding": "SIECAF_OPAQUE_UNBOUND",
"phase09f_offline_design_allowed": False,
}
for field, value in expected.items():
if manifest.get(field) != value:
errors.append(f"{field} changed")
for field in AUTHORIZATION_FIELDS:
if manifest.get("authorization", {}).get(field) is not False:
errors.append(f"authorization.{field} must be false")
local = manifest.get("local_candidate", {})
if (
local.get("file_name") != OUTER_NAME
or local.get("size") != OUTER_SIZE
or local.get("sha256") != OUTER_SHA256
):
errors.append("local candidate identity changed")
inner = manifest.get("inner_archive", {})
if (
inner.get("size") != INNER_SIZE
or inner.get("sha256") != INNER_SHA256
or inner.get("classification") != "OPAQUE_UNBOUND"
or inner.get("further_reverse_engineering_performed") is not False
):
errors.append("inner SIECAF boundary changed")
assets = manifest.get("assets", [])
if len(assets) != 7:
errors.append("official asset inventory must contain seven assets")
for asset in assets:
if not re.fullmatch(r"[0-9a-f]{64}", str(asset.get("sha256", ""))):
errors.append(f"official asset digest malformed: {asset.get('name')}")
if asset.get("classification") != "NO_MATCH" or asset.get("downloaded"):
errors.append(f"asset was not metadata-excluded: {asset.get('name')}")
if official_byte_match(
official_source=True,
local_name=OUTER_NAME,
local_size=OUTER_SIZE,
local_sha256=OUTER_SHA256,
asset_name=str(asset.get("name")),
asset_size=int(asset.get("size", -1)),
asset_sha256=str(asset.get("sha256")),
):
errors.append(f"unrecorded official byte match: {asset.get('name')}")
tags = manifest.get("tags", [])
if len(tags) != 5:
errors.append("official tag inventory must contain five tags")
for tag in tags:
if not re.fullmatch(r"[0-9a-f]{40}", str(tag.get("commit", ""))):
errors.append(f"tag commit is not exact: {tag.get('tag')}")
if not re.fullmatch(r"[0-9a-f]{64}", str(tag.get("source_archive_sha256", ""))):
errors.append(f"source archive hash missing: {tag.get('tag')}")
if len(manifest.get("release_inventory", [])) != 6:
errors.append("release inventory must cover versions 1.2 through 1.6")
provenance = manifest.get("artifact_provenance", [])
if len(provenance) < 6:
errors.append("artifact provenance inventory is incomplete")
for artifact in provenance:
missing = [field for field in PROVENANCE_FIELDS if field not in artifact]
if missing:
errors.append(
f"{artifact.get('logical_name')}: missing provenance fields {missing}"
)
if artifact.get("confidence") == "EXACT_USED":
errors.append(
f"{artifact.get('logical_name')}: deployed-use provenance overclaimed"
)
if artifact.get("logical_name") == "official_embedded_elfldr_1_6":
if artifact.get("source_commit") is not None:
errors.append("embedded elfldr received an invented source commit")
worktree = manifest.get("upstream_worktree", {})
if (
worktree.get("commit") != UPSTREAM_COMMIT
or worktree.get("tree") != UPSTREAM_TREE
or worktree.get("clean") is not True
or worktree.get("submodules") != 0
or worktree.get("git_lfs_pointers") != 0
):
errors.append("official upstream worktree record changed")
sender = manifest.get("official_host_sender", {})
if (
sender.get("response_read") is not False
or sender.get("duplex") is not False
or sender.get("deployed_use_attested") is not False
):
errors.append("official sender was overclaimed")
if manifest.get("operator_attestation", {}).get("attested") is not False:
errors.append("operator attestation must remain empty")
actions = manifest.get("actions", {})
expected_false_actions = (
"downloaded_code_executed",
"dependency_installed",
"ps5_connected",
"ps5_ip_used",
"device_request_performed",
"files_transferred_to_or_from_ps5",
"target_build_performed",
"target_code_created",
"target_artifact_created",
"payload_created",
"device_client_created",
)
for field in expected_false_actions:
if actions.get(field) is not False:
errors.append(f"actions.{field} must be false")
if (
actions.get("large_release_assets_downloaded") != 0
or actions.get("large_release_asset_bytes_downloaded") != 0
):
errors.append("large release asset download was recorded")
if manifest.get("blocking_state", {}).get("payload_manager_backup") != (
"HARD_BLOCKER_FOR_INSTALLATION"
):
errors.append("Payload Manager installation blocker changed")
if phase09f_design_allowed(
correlation=str(manifest.get("release_correlation")),
release_commit_known=False,
upstream_clean=True,
port_classification=PORT_9020_CLASSIFICATION,
sender_identified=True,
attestation_available=True,
all_authorizations_false=True,
):
errors.append("Phase 0.9F incorrectly passed")
return errors
def validate_port_manifest(manifest: dict[str, Any]) -> list[str]:
errors: list[str] = []
if manifest.get("phase") != PHASE or manifest.get("status") != "DESIGN_ONLY":
errors.append("port manifest phase/status changed")
if manifest.get("release_commit") != UPSTREAM_COMMIT:
errors.append("port manifest release commit changed")
if manifest.get("port_9020_classification") != PORT_9020_CLASSIFICATION:
errors.append("port-9020 implementation was overclaimed")
if (
manifest.get("port_9020_listener_found") is not False
or manifest.get("port_9020_parser_found") is not False
or manifest.get("port_9020_mapping_found") is not False
):
errors.append("port-9020 source was invented")
loader = manifest.get("official_remote_js_loader", {})
if (
loader.get("desired_dynamic_port") != 50000
or loader.get("maximum_receive_bytes") != 512000
or loader.get("framing") != "CONNECTION_EOF_OR_BUFFER_LIMIT"
or loader.get("declared_length") is not False
or loader.get("timeout") is not False
or loader.get("native_elf_mapping") is not False
):
errors.append("Remote JS Loader contract changed")
if loader.get("automatic_retry_authorized") is not False:
errors.append("automatic retry was authorized")
relation = manifest.get("port_9021_relation", {})
if relation.get("listener_source_in_y2jb") is not False:
errors.append("embedded 9021 listener source was invented")
if relation.get("source_commit") is not None:
errors.append("embedded elfldr received an invented source commit")
sender = manifest.get("official_sender", {})
if sender.get("response_read") is not False or sender.get("duplex") is not False:
errors.append("one-way sender was classified duplex")
for field in AUTHORIZATION_FIELDS:
if manifest.get("authorization", {}).get(field) is not False:
errors.append(f"port authorization.{field} must be false")
if manifest.get("phase09f_offline_design_allowed") is not False:
errors.append("Phase 0.9F was authorized")
return errors
def changed_paths(root: Path) -> set[str]:
paths = set(git(root, "diff", "--name-only", BASELINE).splitlines())
for line in git(root, "status", "--porcelain=v1", "--untracked-files=all").splitlines():
path = line[3:]
if " -> " in path:
path = path.split(" -> ", 1)[1]
paths.add(path.replace("\\", "/"))
return {path for path in paths if path}
def phase09er_path_errors(paths: set[str]) -> list[str]:
errors: list[str] = []
for path in paths:
normalized = path.replace("\\", "/")
lowered = normalized.lower()
if normalized.startswith(FORBIDDEN_PREFIXES):
errors.append(f"target/production path changed: {normalized}")
if Path(normalized).suffix.lower() in FORBIDDEN_SUFFIXES:
errors.append(f"target artifact/source added: {normalized}")
if any(
token in lowered
for token in (
"rescue.elf", "observer.elf", "readback.elf", "executionpackage",
"installpackage", "transferpackage",
)
):
errors.append(f"forbidden Phase-0.9E-R output: {normalized}")
return errors
def validate_checksum_file(root: Path) -> list[str]:
checksum_path = root / "packaging/phase09er/SHA256SUMS.txt"
if not checksum_path.is_file():
return ["missing packaging/phase09er/SHA256SUMS.txt"]
errors: list[str] = []
for line in checksum_path.read_text(encoding="utf-8").splitlines():
if not line or line.startswith("#"):
continue
parts = line.split(" ", 1)
if len(parts) != 2 or not re.fullmatch(r"[0-9a-f]{64}", parts[0]):
errors.append(f"invalid checksum line: {line}")
continue
expected, relative = parts
path = root / relative
if not path.is_file() or sha256_file(path) != expected:
errors.append(f"checksum mismatch or missing: {relative}")
return errors
def collect_errors(root: Path) -> list[str]:
errors: list[str] = []
release_path = root / "manifests/runtime/phase-0.9e-r-release-correlation.json"
port_path = root / "manifests/runtime/phase-0.9e-r-port9020-audit.json"
release_schema_path = (
root / "manifests/runtime/phase-0.9e-r-release-correlation.schema.json"
)
port_schema_path = (
root / "manifests/runtime/phase-0.9e-r-port9020-audit.schema.json"
)
try:
release = load_json(release_path)
port = load_json(port_path)
release_schema = load_json(release_schema_path)
port_schema = load_json(port_schema_path)
except (OSError, ValueError, json.JSONDecodeError) as error:
return [str(error)]
errors.extend(validate_schema_instance(release_schema, release))
errors.extend(validate_schema_instance(port_schema, port))
errors.extend(validate_release_manifest(release))
errors.extend(validate_port_manifest(port))
for relative in DELIVERABLES:
if not (root / relative).is_file():
errors.append(f"missing deliverable: {relative}")
if git(root, "rev-parse", "--abbrev-ref", "HEAD") != BRANCH:
errors.append("current branch is not the Phase-0.9E-R branch")
if git(root, "merge-base", BASELINE, "HEAD") != BASELINE:
errors.append("branch no longer descends from the canonical baseline")
if sha256_file(root / "manifests/artifact-denylist.json") != DENYLIST_SHA256:
errors.append("permanent artifact denylist changed")
for relative, expected in IMMUTABLE_HASHES.items():
path = root / relative
if not path.is_file() or sha256_file(path) != expected:
errors.append(f"immutable evidence changed: {relative}")
outer = candidate_archive_path(root)
if not outer.is_file():
errors.append(f"local Y2JB candidate missing: {outer}")
elif outer.stat().st_size != OUTER_SIZE or sha256_file(outer) != OUTER_SHA256:
errors.append("local Y2JB outer identity changed")
else:
try:
with zipfile.ZipFile(outer) as archive:
info = archive.getinfo(INNER_NAME)
if (
len(archive.infolist()) != 5
or archive.comment != b""
or info.file_size != INNER_SIZE
or info.compress_size != 504158629
or info.CRC != int("522808c8", 16)
):
errors.append("local ZIP metadata changed")
digest = hashlib.sha256()
magic = b""
with archive.open(info) as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
if not magic:
magic = chunk[:6]
digest.update(chunk)
if magic != b"SIECAF" or digest.hexdigest() != INNER_SHA256:
errors.append("inner SIECAF identity changed")
except (OSError, KeyError, zipfile.BadZipFile) as error:
errors.append(f"local ZIP validation failed: {error}")
upstream = root / "work/upstream/Y2JB-official"
if not upstream.is_dir():
errors.append("official immutable upstream worktree missing")
else:
if git(upstream, "rev-parse", "HEAD") != UPSTREAM_COMMIT:
errors.append("official upstream HEAD changed")
if git(upstream, "rev-parse", "HEAD^{tree}") != UPSTREAM_TREE:
errors.append("official upstream tree changed")
if git(upstream, "status", "--porcelain=v1"):
errors.append("official upstream worktree is dirty")
remotes = git(upstream, "remote", "-v")
if UPSTREAM_REMOTE not in remotes:
errors.append("official upstream remote changed")
if git(upstream, "submodule", "status"):
errors.append("unexpected official upstream submodule")
if len(git(upstream, "ls-files").splitlines()) != 19:
errors.append("official upstream tracked file inventory changed")
for relative, (expected_size, expected_hash) in UPSTREAM_FILE_HASHES.items():
path = upstream / relative
if (
not path.is_file()
or path.stat().st_size != expected_size
or sha256_file(path) != expected_hash
):
errors.append(f"official upstream file identity changed: {relative}")
errors.extend(phase09er_path_errors(changed_paths(root)))
for path in root.rglob("*"):
if (
path.is_file()
and ".git" not in path.parts
and "work" not in path.parts
and path.stat().st_size > 50 * 1024 * 1024
):
errors.append(f"large release-like object present in repository: {path}")
errors.extend(validate_checksum_file(root))
return errors
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--root", type=Path, required=True)
args = parser.parse_args()
errors = collect_errors(args.root.resolve())
if errors:
for error in errors:
print(f"ERROR: {error}")
return 1
print("Phase-0.9E-R official Y2JB provenance validation: PASS")
print(f"Release correlation: OFFICIAL_RELEASE_NO_MATCH")
print(f"Port 9020: {PORT_9020_CLASSIFICATION}")
print(f"Decision: {FINAL_CLASSIFICATION}")
print("Device actions: NONE")
return 0
if __name__ == "__main__":
raise SystemExit(main())