53 lines
1.8 KiB
Python
Executable file
53 lines
1.8 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Extract signing entitlements from an Apple provisioning profile."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import plistlib
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
def decode_profile(path: Path) -> dict:
|
|
commands = [
|
|
["security", "cms", "-D", "-i", str(path)],
|
|
["openssl", "smime", "-inform", "DER", "-verify", "-noverify", "-in", str(path)],
|
|
["openssl", "cms", "-inform", "DER", "-verify", "-noverify", "-in", str(path)],
|
|
]
|
|
errors: list[str] = []
|
|
for command in commands:
|
|
try:
|
|
proc = subprocess.run(
|
|
command,
|
|
check=False,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
)
|
|
except FileNotFoundError:
|
|
continue
|
|
if proc.returncode == 0 and proc.stdout:
|
|
return plistlib.loads(proc.stdout)
|
|
errors.append(f"{command[0]} exited {proc.returncode}: {proc.stderr.decode(errors='replace').strip()}")
|
|
raise RuntimeError(f"unable to decode provisioning profile {path}: {'; '.join(errors)}")
|
|
|
|
|
|
def main(argv: list[str]) -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("profile", type=Path)
|
|
parser.add_argument("--output", "-o", type=Path, required=True)
|
|
args = parser.parse_args(argv)
|
|
|
|
profile = decode_profile(args.profile)
|
|
entitlements = profile.get("Entitlements")
|
|
if not isinstance(entitlements, dict):
|
|
raise RuntimeError(f"profile has no Entitlements dictionary: {args.profile}")
|
|
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
args.output.write_bytes(plistlib.dumps(entitlements, fmt=plistlib.FMT_XML))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main(sys.argv[1:]))
|