import pytest import yaml from server.app import create_app from server.profile_generator import ( derive_opc, enc_iccid, enc_imsi, generate_iccid, generate_ki, ) import server.saip_profile as saip _OP = bytes.fromhex("63bfa50ee6523365ff14c1f45f88737d") saip_required = pytest.mark.skipif( not saip.saip_available(), reason="SAIP unavailable (asn1tools + SAIP ASN.1 schema not installed)", ) def _write_config(tmp_path, profile_format="v2"): cfg = { "network": {"mcc": "302", "mnc": "720", "plmn": "302720", "op_key": "63bfa50ee6523365ff14c1f45f88737d"}, "wifi": {"ssid": "x", "channel": 6, "ip": "192.168.4.1", "subnet": "192.168.4.0/24", "dhcp_range": "a,b"}, "server": {"host": "0.0.0.0", "port": 5000, "debug": False}, "database": {"path": str(tmp_path / "subscribers.db")}, "rate_limiting": {"max_profiles_per_mac": 1, "window_hours": 1}, "core": {"type": "none"}, "profile": {"carrier_name": "Ad-Hoc", "profile_name": "Emergency 5G", "apn": "internet", "smdp_address": "esim.local", "format": profile_format}, } path = tmp_path / "config.yaml" path.write_text(yaml.safe_dump(cfg)) return str(path) class TestSaipEncoding: def test_unavailable_raises(self, monkeypatch): monkeypatch.setattr(saip, "_compiler", lambda: None) with pytest.raises(RuntimeError, match="unavailable"): saip.create_saip_profile("302720000000001", generate_ki(), b"\x00" * 16, generate_iccid()) @saip_required def test_produces_der_bytes(self): ki = generate_ki() opc = derive_opc(ki, _OP) iccid = generate_iccid() pkg = saip.create_saip_profile("302720000000001", ki, opc, iccid, "Emergency 5G", "C") assert isinstance(pkg, bytes) and len(pkg) > 0 # First byte is the context tag for the 'header' CHOICE alternative. assert pkg[0] == 0xA0 @saip_required def test_full_sequence_decodes(self): ki = generate_ki() opc = derive_opc(ki, _OP) iccid = generate_iccid() pkg = saip.create_saip_profile("302720000000001", ki, opc, iccid, "P", "C") pes = saip.decode_saip_profile(pkg) assert [choice for choice, _ in pes] == ["header", "usim", "akaParameter", "end"] header = dict(pes)["header"] assert header["iccid"] == enc_iccid(iccid) assert header["major-version"] == 3 @saip_required def test_imsi_embedded_in_usim(self): imsi = "302720123456789" pkg = saip.create_saip_profile(imsi, generate_ki(), b"\x11" * 16, generate_iccid()) usim = dict(saip.decode_saip_profile(pkg))["usim"] assert usim["templateID"] == saip.USIM_TEMPLATE_OID assert usim["ef-imsi"] == [("fillFileContent", enc_imsi(imsi))] @saip_required def test_credentials_embedded(self): ki = generate_ki() opc = derive_opc(ki, _OP) pkg = saip.create_saip_profile("302720000000001", ki, opc, generate_iccid(), "P", "C") assert ki in pkg # raw Ki bytes present in the AKA parameter assert opc in pkg class TestSaipAppIntegration: @saip_required def test_provision_emits_saip_when_configured(self, tmp_path): app = create_app(_write_config(tmp_path, profile_format="saip")) client = app.test_client() r = client.post("/api/provision", json={"mac_address": "aa:bb:cc:00:11:22"}) assert r.status_code == 200 profile = client.get(r.get_json()["profile_url"]).data assert profile[0] == 0xA0 # DER, not JSON def test_falls_back_to_v2_when_saip_unavailable(self, tmp_path, monkeypatch): import server.app as appmod monkeypatch.setattr(appmod, "saip_available", lambda: False) app = create_app(_write_config(tmp_path, profile_format="saip")) client = app.test_client() r = client.post("/api/provision", json={"mac_address": "aa:bb:cc:00:11:33"}) assert r.status_code == 200 profile = client.get(r.get_json()["profile_url"]).data assert profile.lstrip()[:1] == b"{" # JSON profile-package-v2