docs: Better python doc-strings for better pySim.esim manual
Change-Id: I7be6264c665a2a25105681bb5e72d0f6715bbef8
This commit is contained in:
@@ -61,8 +61,8 @@ def compile_asn1_subdir(subdir_name:str, codec='der'):
|
||||
return asn1tools.compile_string(asn_txt, codec=codec)
|
||||
|
||||
|
||||
# SGP.22 section 4.1 Activation Code
|
||||
class ActivationCode:
|
||||
"""SGP.22 section 4.1 Activation Code"""
|
||||
def __init__(self, hostname:str, token:str, oid: Optional[str] = None, cc_required: Optional[bool] = False):
|
||||
if '$' in hostname:
|
||||
raise ValueError('$ sign not permitted in hostname')
|
||||
@@ -78,6 +78,7 @@ class ActivationCode:
|
||||
|
||||
@staticmethod
|
||||
def decode_str(ac: str) -> dict:
|
||||
"""decode an activation code from its string representation."""
|
||||
if ac[0] != '1':
|
||||
raise ValueError("Unsupported AC_Format '%s'!" % ac[0])
|
||||
ac_elements = ac.split('$')
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
# Early proof-of-concept implementation of
|
||||
# GSMA eSIM RSP (Remote SIM Provisioning BSP (BPP Protection Protocol),
|
||||
# where BPP is the Bound Profile Package. So the full expansion is the
|
||||
# "GSMA eSIM Remote SIM Provisioning Bound Profile Packate Protection Protocol"
|
||||
#
|
||||
# Originally (SGP.22 v2.x) this was called SCP03t, but it has since been
|
||||
# renamed to BSP.
|
||||
#
|
||||
"""Implementation of GSMA eSIM RSP (Remote SIM Provisioning BSP (BPP Protection Protocol),
|
||||
where BPP is the Bound Profile Package. So the full expansion is the
|
||||
"GSMA eSIM Remote SIM Provisioning Bound Profile Packate Protection Protocol"
|
||||
|
||||
Originally (SGP.22 v2.x) this was called SCP03t, but it has since been renamed to BSP."""
|
||||
|
||||
# (C) 2023 by Harald Welte <laforge@osmocom.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -45,6 +43,7 @@ logger.addHandler(logging.NullHandler())
|
||||
MAX_SEGMENT_SIZE = 1020
|
||||
|
||||
class BspAlgo(abc.ABC):
|
||||
"""Base class representing a cryptographic algorithm within the BSP (BPP Security Protocol)."""
|
||||
blocksize: int
|
||||
|
||||
def _get_padding(self, in_len: int, multiple: int, padding: int = 0) -> bytes:
|
||||
@@ -62,6 +61,7 @@ class BspAlgo(abc.ABC):
|
||||
return self.__class__.__name__
|
||||
|
||||
class BspAlgoCrypt(BspAlgo, abc.ABC):
|
||||
"""Base class representing an encryption/decryption algorithm within the BSP (BPP Security Protocol)."""
|
||||
|
||||
def __init__(self, s_enc: bytes):
|
||||
self.s_enc = s_enc
|
||||
@@ -93,6 +93,7 @@ class BspAlgoCrypt(BspAlgo, abc.ABC):
|
||||
"""Actual implementation, to be implemented by derived class."""
|
||||
|
||||
class BspAlgoCryptAES128(BspAlgoCrypt):
|
||||
"""AES-CBC-128 implementation of the BPP Security Protocol for GSMA SGP.22 eSIM."""
|
||||
name = 'AES-CBC-128'
|
||||
blocksize = 16
|
||||
|
||||
@@ -133,6 +134,7 @@ class BspAlgoCryptAES128(BspAlgoCrypt):
|
||||
|
||||
|
||||
class BspAlgoMac(BspAlgo, abc.ABC):
|
||||
"""Base class representing a message authentication code algorithm within the BSP (BPP Security Protocol)."""
|
||||
l_mac = 0 # must be overridden by derived class
|
||||
|
||||
def __init__(self, s_mac: bytes, initial_mac_chaining_value: bytes):
|
||||
@@ -167,6 +169,7 @@ class BspAlgoMac(BspAlgo, abc.ABC):
|
||||
"""To be implemented by algorithm specific derived class."""
|
||||
|
||||
class BspAlgoMacAES128(BspAlgoMac):
|
||||
"""AES-CMAC-128 implementation of the BPP Security Protocol for GSMA SGP.22 eSIM."""
|
||||
name = 'AES-CMAC-128'
|
||||
l_mac = 8
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Implementation of GSMA eSIM RSP (Remote SIM Provisioning) ES8+
|
||||
# as per SGP22 v3.0 Section 5.5
|
||||
#
|
||||
"""Implementation of GSMA eSIM RSP (Remote SIM Provisioning) ES8+ as per SGP22 v3.0 Section 5.5"""
|
||||
|
||||
# (C) 2023-2024 by Harald Welte <laforge@osmocom.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Implementation of GSMA eSIM RSP (Remote SIM Provisioning)
|
||||
# as per SGP22 v3.0
|
||||
#
|
||||
"""Implementation of GSMA eSIM RSP (Remote SIM Provisioning) as per SGP22 v3.0"""
|
||||
|
||||
# (C) 2023-2024 by Harald Welte <laforge@osmocom.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Implementation of SimAlliance/TCA Interoperable Profile handling
|
||||
#
|
||||
"""Implementation of SimAlliance/TCA Interoperable Profile handling"""
|
||||
|
||||
# (C) 2023-2024 by Harald Welte <laforge@osmocom.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -45,7 +45,7 @@ asn1 = compile_asn1_subdir('saip')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class Naa:
|
||||
"""A class defining a Network Access Application (NAA)."""
|
||||
"""A class defining a Network Access Application (NAA)"""
|
||||
name = None
|
||||
# AID prefix, as used for ADF and EF.DIR
|
||||
aid = None
|
||||
@@ -61,6 +61,7 @@ class Naa:
|
||||
return 'adf-' + cls.mandatory_services[0]
|
||||
|
||||
class NaaCsim(Naa):
|
||||
"""A class representing the CSIM (CDMA) Network Access Application (NAA)"""
|
||||
name = "csim"
|
||||
aid = h2b("")
|
||||
mandatory_services = ["csim"]
|
||||
@@ -68,6 +69,7 @@ class NaaCsim(Naa):
|
||||
templates = [oid.ADF_CSIM_by_default, oid.ADF_CSIMopt_not_by_default]
|
||||
|
||||
class NaaUsim(Naa):
|
||||
"""A class representing the USIM Network Access Application (NAA)"""
|
||||
name = "usim"
|
||||
aid = h2b("a0000000871002")
|
||||
mandatory_services = ["usim"]
|
||||
@@ -80,6 +82,7 @@ class NaaUsim(Naa):
|
||||
adf = ADF_USIM()
|
||||
|
||||
class NaaIsim(Naa):
|
||||
"""A class representing the ISIM Network Access Application (NAA)"""
|
||||
name = "isim"
|
||||
aid = h2b("a0000000871004")
|
||||
mandatory_services = ["isim"]
|
||||
@@ -750,6 +753,7 @@ class ProfileElementGFM(ProfileElement):
|
||||
|
||||
|
||||
class ProfileElementMF(FsProfileElement):
|
||||
"""Class representing the ProfileElement for the MF (Master File)"""
|
||||
type = 'mf'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -763,6 +767,7 @@ class ProfileElementMF(FsProfileElement):
|
||||
# TODO: resize EF.DIR?
|
||||
|
||||
class ProfileElementPuk(ProfileElement):
|
||||
"""Class representing the ProfileElement for a PUK (PIN Unblocking Code)"""
|
||||
type = 'pukCodes'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -793,6 +798,7 @@ class ProfileElementPuk(ProfileElement):
|
||||
|
||||
|
||||
class ProfileElementPin(ProfileElement):
|
||||
"""Class representing the ProfileElement for a PIN (Personal Identification Number)"""
|
||||
type = 'pinCodes'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -829,6 +835,7 @@ class ProfileElementPin(ProfileElement):
|
||||
|
||||
|
||||
class ProfileElementTelecom(FsProfileElement):
|
||||
"""Class representing the ProfileElement for DF.TELECOM"""
|
||||
type = 'telecom'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -841,6 +848,7 @@ class ProfileElementTelecom(FsProfileElement):
|
||||
self.decoded[fname] = []
|
||||
|
||||
class ProfileElementCD(FsProfileElement):
|
||||
"""Class representing the ProfileElement for DF.CD"""
|
||||
type = 'cd'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -853,6 +861,7 @@ class ProfileElementCD(FsProfileElement):
|
||||
self.decoded[fname] = []
|
||||
|
||||
class ProfileElementPhonebook(FsProfileElement):
|
||||
"""Class representing the ProfileElement for DF.PHONEBOOK"""
|
||||
type = 'phonebook'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -865,6 +874,7 @@ class ProfileElementPhonebook(FsProfileElement):
|
||||
self.decoded[fname] = []
|
||||
|
||||
class ProfileElementGsmAccess(FsProfileElement):
|
||||
"""Class representing the ProfileElement for ADF.USIM/DF.GSM-ACCESS"""
|
||||
type = 'gsm-access'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -877,6 +887,7 @@ class ProfileElementGsmAccess(FsProfileElement):
|
||||
self.decoded[fname] = []
|
||||
|
||||
class ProfileElementDf5GS(FsProfileElement):
|
||||
"""Class representing the ProfileElement for ADF.USIM/DF.5GS"""
|
||||
type = 'df-5gs'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -889,6 +900,7 @@ class ProfileElementDf5GS(FsProfileElement):
|
||||
self.decoded[fname] = []
|
||||
|
||||
class ProfileElementEAP(FsProfileElement):
|
||||
"""Class representing the ProfileElement for DF.EAP"""
|
||||
type = 'eap'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -901,6 +913,7 @@ class ProfileElementEAP(FsProfileElement):
|
||||
self.decoded[fname] = []
|
||||
|
||||
class ProfileElementDfSAIP(FsProfileElement):
|
||||
"""Class representing the ProfileElement for DF.SAIP"""
|
||||
type = 'df-saip'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -913,6 +926,7 @@ class ProfileElementDfSAIP(FsProfileElement):
|
||||
self.decoded[fname] = []
|
||||
|
||||
class ProfileElementDfSNPN(FsProfileElement):
|
||||
"""Class representing the ProfileElement for DF.SNPN"""
|
||||
type = 'df-snpn'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -925,6 +939,7 @@ class ProfileElementDfSNPN(FsProfileElement):
|
||||
self.decoded[fname] = []
|
||||
|
||||
class ProfileElementDf5GProSe(FsProfileElement):
|
||||
"""Class representing the ProfileElement for DF.5GProSe"""
|
||||
type = 'df-5gprose'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -1218,6 +1233,7 @@ class ProfileElementApplication(ProfileElement):
|
||||
|
||||
|
||||
class ProfileElementRFM(ProfileElement):
|
||||
"""Class representing the ProfileElement for RFM (Remote File Management)."""
|
||||
type = 'rfm'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None,
|
||||
@@ -1243,6 +1259,7 @@ class ProfileElementRFM(ProfileElement):
|
||||
}
|
||||
|
||||
class ProfileElementUSIM(FsProfileElement):
|
||||
"""Class representing the ProfileElement for ADF.USIM Mandatory Files"""
|
||||
type = 'usim'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -1265,6 +1282,7 @@ class ProfileElementUSIM(FsProfileElement):
|
||||
return dec_imsi(b2h(f.body))
|
||||
|
||||
class ProfileElementOptUSIM(FsProfileElement):
|
||||
"""Class representing the ProfileElement for ADF.USIM Optional Files"""
|
||||
type = 'opt-usim'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -1275,6 +1293,7 @@ class ProfileElementOptUSIM(FsProfileElement):
|
||||
self.decoded['templateID'] = str(oid.ADF_USIMopt_not_by_default_v2)
|
||||
|
||||
class ProfileElementISIM(FsProfileElement):
|
||||
"""Class representing the ProfileElement for ADF.ISIM Mandatory Files"""
|
||||
type = 'isim'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -1291,6 +1310,7 @@ class ProfileElementISIM(FsProfileElement):
|
||||
return b2h(self.decoded['adf-isim'][0][1]['dfName'])
|
||||
|
||||
class ProfileElementOptISIM(FsProfileElement):
|
||||
"""Class representing the ProfileElement for ADF.ISIM Optional Files"""
|
||||
type = 'opt-isim'
|
||||
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
@@ -1302,6 +1322,7 @@ class ProfileElementOptISIM(FsProfileElement):
|
||||
|
||||
|
||||
class ProfileElementAKA(ProfileElement):
|
||||
"""Class representing the ProfileElement for Authentication and Key Agreement (AKA)."""
|
||||
type = 'akaParameter'
|
||||
# TODO: RES size for USIM test algorithm can be set to 32, 64 or 128 bits. This value was
|
||||
# previously limited to 128 bits. Recommendation: Avoid using RES size 32 or 64 in Profiles
|
||||
@@ -1381,6 +1402,7 @@ class ProfileElementAKA(ProfileElement):
|
||||
})
|
||||
|
||||
class ProfileElementHeader(ProfileElement):
|
||||
"""Class representing the ProfileElement for the Header of the PE-Sequence."""
|
||||
type = 'header'
|
||||
def __init__(self, decoded: Optional[dict] = None,
|
||||
ver_major: Optional[int] = 2, ver_minor: Optional[int] = 3,
|
||||
@@ -1421,6 +1443,7 @@ class ProfileElementHeader(ProfileElement):
|
||||
raise ValueError("service not in eUICC-Mandatory-services list, cannot remove")
|
||||
|
||||
class ProfileElementEnd(ProfileElement):
|
||||
"""Class representing the ProfileElement for the End of the PE-Sequence."""
|
||||
type = 'end'
|
||||
def __init__(self, decoded: Optional[dict] = None, **kwargs):
|
||||
super().__init__(decoded, **kwargs)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Implementation of SimAlliance/TCA Interoperable Profile OIDs
|
||||
#
|
||||
"""Implementation of SimAlliance/TCA Interoperable Profile OIDs"""
|
||||
|
||||
# (C) 2023-2024 by Harald Welte <laforge@osmocom.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Implementation of SimAlliance/TCA Interoperable Profile handling
|
||||
#
|
||||
"""Implementation of Personalization of eSIM profiles in SimAlliance/TCA Interoperable Profile."""
|
||||
|
||||
# (C) 2023-2024 by Harald Welte <laforge@osmocom.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Implementation of SimAlliance/TCA Interoperable Profile Template handling
|
||||
#
|
||||
"""Implementation of SimAlliance/TCA Interoperable Profile Templates."""
|
||||
|
||||
# (C) 2024 by Harald Welte <laforge@osmocom.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -224,8 +224,8 @@ class ProfileTemplateRegistry:
|
||||
# below are transcribed template definitions from "ANNEX A (Normative): File Structure Templates Definition"
|
||||
# of "Profile interoperability specification V3.3.1 Final" (unless other version explicitly specified).
|
||||
|
||||
# Section 9.2
|
||||
class FilesAtMF(ProfileTemplate):
|
||||
"""Files at MF as per Section 9.2"""
|
||||
created_by_default = True
|
||||
oid = OID.MF
|
||||
files = [
|
||||
@@ -238,8 +238,8 @@ class FilesAtMF(ProfileTemplate):
|
||||
]
|
||||
|
||||
|
||||
# Section 9.3
|
||||
class FilesCD(ProfileTemplate):
|
||||
"""Files at DF.CD as per Section 9.3"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_CD
|
||||
files = [
|
||||
@@ -287,8 +287,8 @@ for i in range(0x90, 0x98):
|
||||
for i in range(0x98, 0xa0):
|
||||
df_pb_files.append(FileTemplate(0x4f00+i, 'EF.CCP1', 'LF', None, None, 5, None, 'FF...FF', False, ['nb_rec','size','sfi'], ppath=[0x5f3a]))
|
||||
|
||||
# Section 9.4 v2.3.1
|
||||
class FilesTelecom(ProfileTemplate):
|
||||
"""Files at DF.TELECOM as per Section 9.4 v2.3.1"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_TELECOM
|
||||
base_path = Path('MF')
|
||||
@@ -328,8 +328,8 @@ class FilesTelecom(ProfileTemplate):
|
||||
]
|
||||
|
||||
|
||||
# Section 9.4
|
||||
class FilesTelecomV2(ProfileTemplate):
|
||||
"""Files at DF.TELECOM as per Section 9.4"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_TELECOM_v2
|
||||
base_path = Path('MF')
|
||||
@@ -379,8 +379,8 @@ class FilesTelecomV2(ProfileTemplate):
|
||||
]
|
||||
|
||||
|
||||
# Section 9.5.1 v2.3.1
|
||||
class FilesUsimMandatory(ProfileTemplate):
|
||||
"""Mandatory Files at ADF.USIM as per Section 9.5.1 v2.3.1"""
|
||||
created_by_default = True
|
||||
oid = OID.ADF_USIM_by_default
|
||||
files = [
|
||||
@@ -410,8 +410,8 @@ class FilesUsimMandatory(ProfileTemplate):
|
||||
FileTemplate(0x6fe4, 'EF.EPSNSC', 'LF', 1, 80, 5, 0x18, 'FF...FF', False, ass_serv=[85], high_update=True),
|
||||
]
|
||||
|
||||
# Section 9.5.1
|
||||
class FilesUsimMandatoryV2(ProfileTemplate):
|
||||
"""Mandatory Files at ADF.USIM as per Section 9.5.1"""
|
||||
created_by_default = True
|
||||
oid = OID.ADF_USIM_by_default_v2
|
||||
files = [
|
||||
@@ -442,8 +442,8 @@ class FilesUsimMandatoryV2(ProfileTemplate):
|
||||
]
|
||||
|
||||
|
||||
# Section 9.5.2 v2.3.1
|
||||
class FilesUsimOptional(ProfileTemplate):
|
||||
"""Optional Files at ADF.USIM as per Section 9.5.2 v2.3.1"""
|
||||
created_by_default = False
|
||||
optional = True
|
||||
oid = OID.ADF_USIMopt_not_by_default
|
||||
@@ -529,6 +529,7 @@ class FilesUsimOptional(ProfileTemplate):
|
||||
|
||||
# Section 9.5.2
|
||||
class FilesUsimOptionalV2(ProfileTemplate):
|
||||
"""Optional Files at ADF.USIM as per Section 9.5.2"""
|
||||
created_by_default = False
|
||||
optional = True
|
||||
oid = OID.ADF_USIMopt_not_by_default_v2
|
||||
@@ -622,8 +623,8 @@ class FilesUsimOptionalV2(ProfileTemplate):
|
||||
FileTemplate(0x6ffd, 'EF.MudMidCfgdata','BT', None, None,2, None, None, True, ['size'], ass_serv=[134]),
|
||||
]
|
||||
|
||||
# Section 9.5.2.3 v3.3.1
|
||||
class FilesUsimOptionalV3(ProfileTemplate):
|
||||
"""Optional Files at ADF.USIM as per Section 9.5.2.3 v3.3.1"""
|
||||
created_by_default = False
|
||||
optional = True
|
||||
oid = OID.ADF_USIMopt_not_by_default_v3
|
||||
@@ -633,16 +634,16 @@ class FilesUsimOptionalV3(ProfileTemplate):
|
||||
FileTemplate(0x6f01, 'EF.eAKA', 'TR', None, 1, 3, None, None, True, ['size'], ass_serv=[134]),
|
||||
]
|
||||
|
||||
# Section 9.5.3
|
||||
class FilesUsimDfPhonebook(ProfileTemplate):
|
||||
"""DF.PHONEBOOK Files at ADF.USIM as per Section 9.5.3"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_PHONEBOOK_ADF_USIM
|
||||
base_path = Path('ADF.USIM')
|
||||
files = df_pb_files
|
||||
|
||||
|
||||
# Section 9.5.4
|
||||
class FilesUsimDfGsmAccess(ProfileTemplate):
|
||||
"""DF.GSM-ACCESS Files at ADF.USIM as per Section 9.5.4"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_GSM_ACCESS_ADF_USIM
|
||||
base_path = Path('ADF.USIM')
|
||||
@@ -656,8 +657,8 @@ class FilesUsimDfGsmAccess(ProfileTemplate):
|
||||
]
|
||||
|
||||
|
||||
# Section 9.5.11 v2.3.1
|
||||
class FilesUsimDf5GS(ProfileTemplate):
|
||||
"""DF.5GS Files at ADF.USIM as per Section 9.5.11 v2.3.1"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_5GS
|
||||
base_path = Path('ADF.USIM')
|
||||
@@ -677,8 +678,8 @@ class FilesUsimDf5GS(ProfileTemplate):
|
||||
]
|
||||
|
||||
|
||||
# Section 9.5.11.2
|
||||
class FilesUsimDf5GSv2(ProfileTemplate):
|
||||
"""DF.5GS Files at ADF.USIM as per Section 9.5.11.2"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_5GS_v2
|
||||
base_path = Path('ADF.USIM')
|
||||
@@ -700,8 +701,8 @@ class FilesUsimDf5GSv2(ProfileTemplate):
|
||||
]
|
||||
|
||||
|
||||
# Section 9.5.11.3
|
||||
class FilesUsimDf5GSv3(ProfileTemplate):
|
||||
"""DF.5GS Files at ADF.USIM as per Section 9.5.11.3"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_5GS_v3
|
||||
base_path = Path('ADF.USIM')
|
||||
@@ -724,8 +725,8 @@ class FilesUsimDf5GSv3(ProfileTemplate):
|
||||
FileTemplate(0x4f0c, 'EF.TN3GPPSNN', 'TR', None, 1, 2, 0x0c, '00', False, ass_serv=[135]),
|
||||
]
|
||||
|
||||
# Section 9.5.11.4
|
||||
class FilesUsimDf5GSv4(ProfileTemplate):
|
||||
"""DF.5GS Files at ADF.USIM as per Section 9.5.11.4"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_5GS_v4
|
||||
base_path = Path('ADF.USIM')
|
||||
@@ -756,8 +757,8 @@ class FilesUsimDf5GSv4(ProfileTemplate):
|
||||
]
|
||||
|
||||
|
||||
# Section 9.5.12
|
||||
class FilesUsimDfSaip(ProfileTemplate):
|
||||
"""DF.SAIP Files at ADF.USIM as per Section 9.5.12"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_SAIP
|
||||
base_path = Path('ADF.USIM')
|
||||
@@ -767,8 +768,8 @@ class FilesUsimDfSaip(ProfileTemplate):
|
||||
FileTemplate(0x4f01, 'EF.SUCICalcInfo','TR', None, None, 3, None, 'FF...FF', False, ['size'], ass_serv=[125], pe_name='ef-suci-calc-info-usim'),
|
||||
]
|
||||
|
||||
# Section 9.5.13
|
||||
class FilesDfSnpn(ProfileTemplate):
|
||||
"""DF.SNPN Files at ADF.USIM as per Section 9.5.13"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_SNPN
|
||||
base_path = Path('ADF.USIM')
|
||||
@@ -778,8 +779,8 @@ class FilesDfSnpn(ProfileTemplate):
|
||||
FileTemplate(0x4f01, 'EF.PWS_SNPN', 'TR', None, 1, 10, None, None, True, ass_serv=[143]),
|
||||
]
|
||||
|
||||
# Section 9.5.14
|
||||
class FilesDf5GProSe(ProfileTemplate):
|
||||
"""DF.ProSe Files at ADF.USIM as per Section 9.5.14"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_5GProSe
|
||||
base_path = Path('ADF.USIM')
|
||||
@@ -794,8 +795,8 @@ class FilesDf5GProSe(ProfileTemplate):
|
||||
FileTemplate(0x4f06, 'EF.5G_PROSE_UIR', 'TR', None, 32, 2, 0x06, None, True, ass_serv=[139,1005]),
|
||||
]
|
||||
|
||||
# Section 9.6.1
|
||||
class FilesIsimMandatory(ProfileTemplate):
|
||||
"""Mandatory Files at ADF.ISIM as per Section 9.6.1"""
|
||||
created_by_default = True
|
||||
oid = OID.ADF_ISIM_by_default
|
||||
files = [
|
||||
@@ -809,8 +810,8 @@ class FilesIsimMandatory(ProfileTemplate):
|
||||
]
|
||||
|
||||
|
||||
# Section 9.6.2 v2.3.1
|
||||
class FilesIsimOptional(ProfileTemplate):
|
||||
"""Optional Files at ADF.ISIM as per Section 9.6.2 of v2.3.1"""
|
||||
created_by_default = False
|
||||
optional = True
|
||||
oid = OID.ADF_ISIMopt_not_by_default
|
||||
@@ -829,8 +830,8 @@ class FilesIsimOptional(ProfileTemplate):
|
||||
]
|
||||
|
||||
|
||||
# Section 9.6.2
|
||||
class FilesIsimOptionalv2(ProfileTemplate):
|
||||
"""Optional Files at ADF.ISIM as per Section 9.6.2"""
|
||||
created_by_default = False
|
||||
optional = True
|
||||
oid = OID.ADF_ISIMopt_not_by_default_v2
|
||||
@@ -857,8 +858,8 @@ class FilesIsimOptionalv2(ProfileTemplate):
|
||||
# TODO: CSIM
|
||||
|
||||
|
||||
# Section 9.8
|
||||
class FilesEap(ProfileTemplate):
|
||||
"""Files at DF.EAP as per Section 9.8"""
|
||||
created_by_default = False
|
||||
oid = OID.DF_EAP
|
||||
files = [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Implementation of SimAlliance/TCA Interoperable Profile handling
|
||||
#
|
||||
"""Implementation of SimAlliance/TCA Interoperable Profile validation."""
|
||||
|
||||
# (C) 2023-2024 by Harald Welte <laforge@osmocom.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -19,16 +19,21 @@
|
||||
from pySim.esim.saip import *
|
||||
|
||||
class ProfileError(Exception):
|
||||
"""Raised when a ProfileConstraintChecker finds an error in a file [structure]."""
|
||||
pass
|
||||
|
||||
class ProfileConstraintChecker:
|
||||
"""Base class of a constraint checker for a ProfileElementSequence."""
|
||||
def check(self, pes: ProfileElementSequence):
|
||||
"""Execute all the check_* methods of the ProfileConstraintChecker against the given
|
||||
ProfileElementSequence"""
|
||||
for name in dir(self):
|
||||
if name.startswith('check_'):
|
||||
method = getattr(self, name)
|
||||
method(pes)
|
||||
|
||||
class CheckBasicStructure(ProfileConstraintChecker):
|
||||
"""ProfileConstraintChecker for the basic profile structure constraints."""
|
||||
def _is_after_if_exists(self, pes: ProfileElementSequence, opt:str, after:str):
|
||||
opt_pe = pes.get_pe_for_type(opt)
|
||||
if opt_pe:
|
||||
@@ -38,6 +43,7 @@ class CheckBasicStructure(ProfileConstraintChecker):
|
||||
# FIXME: check order
|
||||
|
||||
def check_start_and_end(self, pes: ProfileElementSequence):
|
||||
"""Check for mandatory header and end ProfileElements at the right position."""
|
||||
if pes.pe_list[0].type != 'header':
|
||||
raise ProfileError('first element is not header')
|
||||
if pes.pe_list[1].type != 'mf':
|
||||
@@ -47,6 +53,7 @@ class CheckBasicStructure(ProfileConstraintChecker):
|
||||
raise ProfileError('last element is not end')
|
||||
|
||||
def check_number_of_occurrence(self, pes: ProfileElementSequence):
|
||||
"""Check The number of occurrence of various ProfileElements."""
|
||||
# check for invalid number of occurrences
|
||||
if len(pes.get_pes_for_type('header')) != 1:
|
||||
raise ProfileError('multiple ProfileHeader')
|
||||
@@ -60,6 +67,7 @@ class CheckBasicStructure(ProfileConstraintChecker):
|
||||
raise ProfileError('multiple PE-%s' % tn.upper())
|
||||
|
||||
def check_optional_ordering(self, pes: ProfileElementSequence):
|
||||
"""Check the ordering of optional PEs following the respective mandatory ones."""
|
||||
# ordering and required depenencies
|
||||
self._is_after_if_exists(pes,'opt-usim', 'usim')
|
||||
self._is_after_if_exists(pes,'opt-isim', 'isim')
|
||||
@@ -104,17 +112,21 @@ class CheckBasicStructure(ProfileConstraintChecker):
|
||||
FileChoiceList = List[Tuple]
|
||||
|
||||
class FileError(ProfileError):
|
||||
"""Raised when a FileConstraintChecker finds an error in a file [structure]."""
|
||||
pass
|
||||
|
||||
class FileConstraintChecker:
|
||||
def check(self, l: FileChoiceList):
|
||||
"""Execute all the check_* methods of the FileConstraintChecker against the given FileChoiceList"""
|
||||
for name in dir(self):
|
||||
if name.startswith('check_'):
|
||||
method = getattr(self, name)
|
||||
method(l)
|
||||
|
||||
class FileCheckBasicStructure(FileConstraintChecker):
|
||||
"""Validator for the basic structure of a decoded file."""
|
||||
def check_seqence(self, l: FileChoiceList):
|
||||
"""Check the sequence/ordering."""
|
||||
by_type = {}
|
||||
for k, v in l:
|
||||
if k in by_type:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Implementation of X.509 certificate handling in GSMA eSIM
|
||||
# as per SGP22 v3.0
|
||||
#
|
||||
"""Implementation of X.509 certificate handling in GSMA eSIM as per SGP22 v3.0"""
|
||||
|
||||
# (C) 2024 by Harald Welte <laforge@osmocom.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
||||
Reference in New Issue
Block a user