SAIP: add PE-MF personalizing EF.ICCID
Prepend a PE-MF (referencing the MF "created by default" template, OID 2.23.143.1.2.1) that personalizes EF.ICCID, so the profile sequence is now header -> MF(EF.ICCID) -> USIM(EF.IMSI) -> AKA(Ki/OPc) -> End. 93 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
470498bf07
commit
a5fc9a3e91
|
|
@ -10,8 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Added
|
||||
- **Real SGP.22 SAIP profile generation** (`profile.format: saip`).
|
||||
`server/saip_profile.py` encodes a DER `ProfileElement` sequence — ProfileHeader
|
||||
(ICCID), PE-USIM personalizing EF.IMSI against the standard USIM template
|
||||
(`2.23.143.1.2.4`), and Milenage AKA parameters (Ki/OPc) — with `asn1tools`
|
||||
(ICCID), PE-MF personalizing EF.ICCID (`2.23.143.1.2.1`), PE-USIM personalizing
|
||||
EF.IMSI (`2.23.143.1.2.4`), and Milenage AKA parameters (Ki/OPc) — with `asn1tools`
|
||||
against the official `PE_Definitions` ASN.1 schema. No pySim Python imports (the
|
||||
schema is located from an installed pySim, or via `SAIP_ASN_PATH`), so it avoids
|
||||
the pyscard/smpp card-reader stack; the only runtime dep is `asn1tools`.
|
||||
|
|
|
|||
|
|
@ -103,8 +103,9 @@ Two formats are selectable via `profile.format`:
|
|||
nibble-swapped BCD per 3GPP TS 31.102) and an SGP.22 **LPA activation code**
|
||||
(`LPA:1$<smdp>$<matching-id>`).
|
||||
- **`saip`** — a real DER-encoded **SGP.22 SAIP `ProfileElement` sequence**:
|
||||
ProfileHeader (ICCID), PE-USIM personalizing EF.IMSI against the standard USIM
|
||||
template, and Milenage AKA parameters (Ki/OPc). Built with `asn1tools` against
|
||||
ProfileHeader (ICCID), PE-MF personalizing EF.ICCID, PE-USIM personalizing
|
||||
EF.IMSI (both against the standard "created by default" templates), and Milenage
|
||||
AKA parameters (Ki/OPc). Built with `asn1tools` against
|
||||
the official `PE_Definitions` ASN.1 schema. Enable by installing
|
||||
`requirements-saip.txt` (see that file for the schema source) and setting
|
||||
`profile.format: saip`. If prerequisites are missing the server logs a warning
|
||||
|
|
|
|||
|
|
@ -26,9 +26,11 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
_ASN_RELPATH = os.path.join("esim", "asn1", "saip", "PE_Definitions-3.3.1.asn")
|
||||
|
||||
# Standard "created by default" USIM ADF template (TCA eUICC prefix 2.23.143.1
|
||||
# + templates.2 + usim.4). The eUICC creates ADF.USIM and its default EFs from
|
||||
# this template; the profile only personalizes the EFs it fills (here EF.IMSI).
|
||||
# Standard "created by default" templates (TCA eUICC prefix 2.23.143.1 +
|
||||
# templates.2 + N). The eUICC creates the file structure from the template; the
|
||||
# profile only personalizes the EFs it fills (here EF.ICCID at MF, EF.IMSI at
|
||||
# ADF.USIM).
|
||||
MF_TEMPLATE_OID = "2.23.143.1.2.1"
|
||||
USIM_TEMPLATE_OID = "2.23.143.1.2.4"
|
||||
|
||||
|
||||
|
|
@ -81,8 +83,9 @@ def create_saip_profile(
|
|||
) -> bytes:
|
||||
"""Build a DER-encoded SAIP ProfileElement sequence.
|
||||
|
||||
Emits: ProfileHeader (with ICCID), PE-USIM (personalizing EF.IMSI against the
|
||||
standard USIM template), Milenage PE-AKAParameter (Ki/OPc), and PE-End.
|
||||
Emits: ProfileHeader (ICCID), PE-MF (personalizing EF.ICCID against the MF
|
||||
template), PE-USIM (personalizing EF.IMSI against the USIM template), Milenage
|
||||
PE-AKAParameter (Ki/OPc), and PE-End.
|
||||
"""
|
||||
asn = _compiler()
|
||||
if asn is None:
|
||||
|
|
@ -105,8 +108,16 @@ def create_saip_profile(
|
|||
"eUICC-Mandatory-services": {"usim": None, "milenage": None},
|
||||
"eUICC-Mandatory-GFSTEList": [],
|
||||
})
|
||||
mf = ("mf", {
|
||||
"mf-header": {"identification": 1},
|
||||
"templateID": MF_TEMPLATE_OID,
|
||||
"mf": [],
|
||||
"ef-iccid": [("fillFileContent", enc_iccid(iccid))],
|
||||
# Other mandatory files are left to the template (empty = no override).
|
||||
"ef-dir": [], "ef-arr": [],
|
||||
})
|
||||
usim = ("usim", {
|
||||
"usim-header": {"identification": 1},
|
||||
"usim-header": {"identification": 2},
|
||||
"templateID": USIM_TEMPLATE_OID,
|
||||
"adf-usim": [],
|
||||
"ef-imsi": [("fillFileContent", enc_imsi(imsi))],
|
||||
|
|
@ -115,7 +126,7 @@ def create_saip_profile(
|
|||
"ef-acc": [], "ef-ecc": [],
|
||||
})
|
||||
aka = ("akaParameter", {
|
||||
"aka-header": {"identification": 2},
|
||||
"aka-header": {"identification": 3},
|
||||
"algoConfiguration": ("algoParameter", {
|
||||
"algorithmID": 1, # Milenage
|
||||
"algorithmOptions": b"\x00",
|
||||
|
|
@ -123,10 +134,11 @@ def create_saip_profile(
|
|||
"opc": opc_bytes,
|
||||
}),
|
||||
})
|
||||
end = ("end", {"end-header": {"identification": 3}})
|
||||
end = ("end", {"end-header": {"identification": 4}})
|
||||
|
||||
logger.debug(f"Encoding SAIP profile for IMSI {imsi} (ICCID {iccid})")
|
||||
return b"".join(asn.encode("ProfileElement", pe) for pe in (header, usim, aka, end))
|
||||
pes = (header, mf, usim, aka, end)
|
||||
return b"".join(asn.encode("ProfileElement", pe) for pe in pes)
|
||||
|
||||
|
||||
def _split_der(data: bytes):
|
||||
|
|
|
|||
|
|
@ -58,10 +58,11 @@ class TestSaipEncoding:
|
|||
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
|
||||
assert [choice for choice, _ in pes] == ["header", "mf", "usim", "akaParameter", "end"]
|
||||
by = dict(pes)
|
||||
assert by["header"]["iccid"] == enc_iccid(iccid)
|
||||
assert by["header"]["major-version"] == 3
|
||||
assert by["mf"]["ef-iccid"] == [("fillFileContent", enc_iccid(iccid))]
|
||||
|
||||
@saip_required
|
||||
def test_imsi_embedded_in_usim(self):
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user