This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""Verify an artifact manifest and, when supplied, its local artifact."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def hash_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 fail(message: str) -> None:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--manifest", type=Path, required=True)
|
||||
parser.add_argument("--artifact", type=Path)
|
||||
args = parser.parse_args()
|
||||
|
||||
document = json.loads(args.manifest.read_text(encoding="utf-8"))
|
||||
if document.get("schema_version") != 1:
|
||||
fail("unsupported artifact-manifest schema")
|
||||
artifact_record = document["artifact"]
|
||||
if not re.fullmatch(r"[0-9a-f]{64}", artifact_record["sha256"]):
|
||||
fail("invalid artifact SHA-256")
|
||||
if not re.fullmatch(r"[0-9a-f]{40}", document["source"]["commit"]):
|
||||
fail("invalid source commit")
|
||||
if document["source"].get("dirty") is not False:
|
||||
fail("artifact source must be clean")
|
||||
if not isinstance(document["execution"].get("execution_eligible"), bool):
|
||||
fail("artifact execution eligibility must be an explicit boolean")
|
||||
if any(document["execution"].get(key) is not False for key in
|
||||
("authorized", "transferred", "executed")):
|
||||
fail("artifact manifest claims execution authority or activity")
|
||||
if document["safety"].get("direct_gnm_imports") != 0:
|
||||
fail("artifact manifest claims direct GNM imports")
|
||||
gate = document["firmware_gate"]
|
||||
if gate["embedded_identifier"] == "NONE" and gate["allowlisted"] is not False:
|
||||
fail("NONE firmware gate cannot be allowlisted")
|
||||
|
||||
if args.artifact is not None:
|
||||
artifact = args.artifact.resolve(strict=True)
|
||||
if artifact.name != artifact_record["filename"]:
|
||||
fail("artifact filename mismatch")
|
||||
if artifact.stat().st_size != artifact_record["size"]:
|
||||
fail("artifact size mismatch")
|
||||
if hash_file(artifact) != artifact_record["sha256"]:
|
||||
fail("artifact digest mismatch")
|
||||
|
||||
print(f"verified artifact manifest: {artifact_record['id']}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except (KeyError, TypeError, ValueError, json.JSONDecodeError) as error:
|
||||
print(f"artifact verification failed: {error}")
|
||||
raise SystemExit(1) from error
|
||||
Reference in New Issue
Block a user