Files
SirHumza--orbisRPC/scripts/make_manifest.py
T

97 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""make_manifest.py - build + sign the release manifest for the updater.
Usage:
python3 scripts/make_manifest.py --version 1.0.1 \
--priv release_priv.pem \
--bin build/orbisrpc.elf \
--prx plugin/build/orbisrpc_plugin.prx \
--pkg build/pkg/OrbisRPC-1.0.1.pkg \
--out build/pkg/manifest.json
Outputs manifest.json + manifest.sig (raw 64-byte r||s, hex file) +
appends SHA256SUMS for back-compat. The daemon verifies manifest.sig
with orbisrpc/release_pubkey.h and refuses updates on mismatch
(ORX-UPDATE-002). Keep release_priv.pem OFFLINE, out of the repo.
"""
import argparse, hashlib, json, os, sys
try:
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature
HAVE_CRYPTO = True
except ImportError:
HAVE_CRYPTO = False
def sha256_file(path):
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(65536), b""):
h.update(chunk)
return h.hexdigest()
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--version", required=True)
ap.add_argument("--channel", default="stable")
ap.add_argument("--min-version", default="")
ap.add_argument("--platform", default="ps4-goldhen")
ap.add_argument("--priv", required=True, help="EC P-256 private key PEM (offline)")
ap.add_argument("--bin", default="")
ap.add_argument("--prx", default="")
ap.add_argument("--pkg", default="")
ap.add_argument("--out", required=True)
a = ap.parse_args()
if not HAVE_CRYPTO:
print("need python cryptography package: pip install cryptography", file=sys.stderr)
return 1
assets = []
for name, path in (("orbisrpc.bin", a.bin), ("orbisrpc_plugin.prx", a.prx)):
if path and os.path.isfile(path):
assets.append({"name": name, "sha256": sha256_file(path)})
pkg_name = ""
if a.pkg and os.path.isfile(a.pkg):
pkg_name = os.path.basename(a.pkg)
assets.append({"name": pkg_name, "sha256": sha256_file(a.pkg)})
if not assets:
print("no assets found (bin/prx/pkg missing)", file=sys.stderr)
return 1
manifest = {
"version": a.version,
"channel": a.channel,
"platform": a.platform,
"assets": assets,
}
if a.min_version:
manifest["minimum_version"] = a.min_version
body = json.dumps(manifest, sort_keys=True, separators=(",", ":")).encode()
priv = serialization.load_pem_private_key(open(a.priv, "rb").read(), password=None)
if not isinstance(priv, ec.EllipticCurvePrivateKey):
print("private key is not EC", file=sys.stderr)
return 1
der = priv.sign(body, ec.ECDSA(hashes.SHA256()))
r, s = decode_dss_signature(der)
raw = r.to_bytes(32, "big") + s.to_bytes(32, "big")
with open(a.out, "wb") as f:
f.write(body)
with open(a.out + ".sig", "wb") as f:
f.write(raw)
with open(a.out + ".sig.hex", "w") as f:
f.write(raw.hex())
# back-compat SHA256SUMS alongside
sums = os.path.join(os.path.dirname(a.out), "SHA256SUMS")
with open(sums, "w") as f:
for ent in assets:
f.write("%s %s\n" % (ent["sha256"], ent["name"]))
f.write("%s %s\n" % (hashlib.sha256(body).hexdigest(), "manifest.json"))
print("wrote %s (%d assets) + %s.sig + SHA256SUMS" % (a.out, len(assets), a.out))
return 0
if __name__ == "__main__":
sys.exit(main())