docs: Better python doc-strings for better pySim.esim manual

Change-Id: I7be6264c665a2a25105681bb5e72d0f6715bbef8
This commit is contained in:
Harald Welte
2025-05-06 22:01:14 +02:00
parent 89070a7c67
commit e37cdbcd3e
10 changed files with 87 additions and 50 deletions

View File

@@ -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('$')

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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 = [

View File

@@ -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:

View File

@@ -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