laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/40236?usp=email )
Change subject: docs: Better python doc-strings for better pySim.esim manual ......................................................................
docs: Better python doc-strings for better pySim.esim manual
Change-Id: I7be6264c665a2a25105681bb5e72d0f6715bbef8 --- M pySim/esim/__init__.py M pySim/esim/bsp.py M pySim/esim/es8p.py M pySim/esim/rsp.py M pySim/esim/saip/__init__.py M pySim/esim/saip/oid.py M pySim/esim/saip/personalization.py M pySim/esim/saip/templates.py M pySim/esim/saip/validation.py M pySim/esim/x509_cert.py 10 files changed, 87 insertions(+), 50 deletions(-)
Approvals: laforge: Looks good to me, approved fixeria: Looks good to me, but someone else must approve Jenkins Builder: Verified
diff --git a/pySim/esim/__init__.py b/pySim/esim/__init__.py index 29e2bf4..35b733e 100644 --- a/pySim/esim/__init__.py +++ b/pySim/esim/__init__.py @@ -61,8 +61,8 @@ 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 @@
@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('$') diff --git a/pySim/esim/bsp.py b/pySim/esim/bsp.py index 862502f..fb4c0b3 100644 --- a/pySim/esim/bsp.py +++ b/pySim/esim/bsp.py @@ -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 @@ 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 @@ 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 @@ """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 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 @@ """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
diff --git a/pySim/esim/es8p.py b/pySim/esim/es8p.py index dd0c4bc..8bd7e14 100644 --- a/pySim/esim/es8p.py +++ b/pySim/esim/es8p.py @@ -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 diff --git a/pySim/esim/rsp.py b/pySim/esim/rsp.py index 4b189e1..3dccf2d 100644 --- a/pySim/esim/rsp.py +++ b/pySim/esim/rsp.py @@ -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 diff --git a/pySim/esim/saip/__init__.py b/pySim/esim/saip/__init__.py index 21f44ef..305a373 100644 --- a/pySim/esim/saip/__init__.py +++ b/pySim/esim/saip/__init__.py @@ -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 @@ 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 @@ 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 @@ 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 @@ 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 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 @@ # 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 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 ProfileElementTelecom(FsProfileElement): + """Class representing the ProfileElement for DF.TELECOM""" type = 'telecom'
def __init__(self, decoded: Optional[dict] = None, **kwargs): @@ -841,6 +848,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 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 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 @@ 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 @@ 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 @@ 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 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 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 @@ 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) diff --git a/pySim/esim/saip/oid.py b/pySim/esim/saip/oid.py index 02ef23b..a9c23e2 100644 --- a/pySim/esim/saip/oid.py +++ b/pySim/esim/saip/oid.py @@ -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 diff --git a/pySim/esim/saip/personalization.py b/pySim/esim/saip/personalization.py index cd23944..053ea0c 100644 --- a/pySim/esim/saip/personalization.py +++ b/pySim/esim/saip/personalization.py @@ -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 diff --git a/pySim/esim/saip/templates.py b/pySim/esim/saip/templates.py index 3ec0b9b..2c0a8f0 100644 --- a/pySim/esim/saip/templates.py +++ b/pySim/esim/saip/templates.py @@ -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 @@ # 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 @@ ]
-# 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(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 @@ ]
-# 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 @@ ]
-# 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 @@ 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 @@ ]
-# 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 @@
# 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 @@ 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 @@ 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 @@ ]
-# 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 @@ ]
-# 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 @@ ]
-# 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 @@ 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 @@ ]
-# 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 @@ 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 @@ 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 @@ 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 @@ ]
-# 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 @@ ]
-# 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 @@ # 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 = [ diff --git a/pySim/esim/saip/validation.py b/pySim/esim/saip/validation.py index c253b7a..c115554 100644 --- a/pySim/esim/saip/validation.py +++ b/pySim/esim/saip/validation.py @@ -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 @@ # 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 @@ 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 @@ 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 @@ 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: diff --git a/pySim/esim/x509_cert.py b/pySim/esim/x509_cert.py index e951de7..c8a0386 100644 --- a/pySim/esim/x509_cert.py +++ b/pySim/esim/x509_cert.py @@ -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