Push Tracker
ria-ran--sim-drop/scripts/make_profile.py

102 lines
3.6 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""Generate or inspect an eSIM profile — a dev/ops utility.
Generates one profile (v2 JSON or real SGP.22 SAIP DER) using the network and
profile settings from config.yaml, with random or supplied credentials, and
writes it to a file or stdout. Can also decode an existing SAIP profile file.
Examples:
python scripts/make_profile.py --format saip --out /tmp/p.der
python scripts/make_profile.py --inspect /tmp/p.der
"""
import argparse
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from server import saip_profile
from server.profile_generator import (
create_esim_profile,
derive_opc,
generate_iccid,
generate_imsi,
generate_ki,
)
from server.utils import hex_to_bytes, load_config
def build_profile(cfg: dict, fmt: str = "v2", imsi=None, iccid=None):
"""Return (imsi, iccid, ki, opc, profile_bytes) for a freshly built profile."""
net = cfg["network"]
prof = cfg.get("profile", {})
imsi = imsi or generate_imsi(net["mcc"], net["mnc"])
iccid = iccid or generate_iccid()
ki = generate_ki()
opc = derive_opc(ki, hex_to_bytes(net["op_key"]))
if fmt == "saip":
if not saip_profile.saip_available():
raise SystemExit("SAIP unavailable: install requirements-saip.txt "
"(asn1tools + SAIP ASN.1 schema)")
data = saip_profile.create_saip_profile(
imsi, ki, opc, iccid,
prof.get("profile_name", ""), prof.get("carrier_name", ""),
)
else:
data = create_esim_profile(
imsi, ki, opc, iccid,
prof.get("profile_name", ""), prof.get("carrier_name", ""),
smdp_address=prof.get("smdp_address", "esim.local"),
apn=prof.get("apn", "internet"),
)
return imsi, iccid, ki, opc, data
def summarize_saip(data: bytes) -> str:
"""Return a human-readable summary of a SAIP profile's ProfileElements."""
pes = saip_profile.decode_saip_profile(data)
lines = [f"{len(data)} bytes, {len(pes)} ProfileElement(s):"]
for choice, value in pes:
detail = ""
if choice == "header":
detail = f" iccid={value.get('iccid', b'').hex()}"
elif choice in ("mf", "usim") and isinstance(value, dict):
detail = f" templateID={value.get('templateID')}"
lines.append(f" - {choice}{detail}")
return "\n".join(lines)
def main(argv=None):
ap = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--config", default=os.environ.get("CONFIG_PATH", "config.yaml"))
ap.add_argument("--format", choices=["v2", "saip"], default="v2")
ap.add_argument("--imsi", help="use this IMSI instead of a random one")
ap.add_argument("--iccid", help="use this ICCID instead of a random one")
ap.add_argument("--out", help="output file (default: stdout)")
ap.add_argument("--inspect", metavar="FILE",
help="decode a SAIP profile file and exit")
args = ap.parse_args(argv)
if args.inspect:
with open(args.inspect, "rb") as f:
print(summarize_saip(f.read()))
return 0
cfg = load_config(args.config)
imsi, iccid, _ki, _opc, data = build_profile(cfg, args.format, args.imsi, args.iccid)
if args.out:
with open(args.out, "wb") as f:
f.write(data)
print(f"Wrote {len(data)} bytes to {args.out} "
f"(format={args.format}, IMSI {imsi}, ICCID {iccid})", file=sys.stderr)
else:
sys.stdout.buffer.write(data)
return 0
if __name__ == "__main__":
sys.exit(main())