diff --git a/pySim/esim/saip/personalization.py b/pySim/esim/saip/personalization.py index 8ad52312..67d1cb3e 100644 --- a/pySim/esim/saip/personalization.py +++ b/pySim/esim/saip/personalization.py @@ -20,7 +20,7 @@ import io from typing import List, Tuple from osmocom.tlv import camel_to_snake -from pySim.utils import enc_iccid, enc_imsi, h2b, rpad, sanitize_iccid +from pySim.utils import enc_iccid, enc_imsi, h2b, rpad, sanitize_iccid, all_subclasses_of from pySim.esim.saip import ProfileElement, ProfileElementSequence from pySim.ts_51_011 import EF_SMSP @@ -105,10 +105,14 @@ class ConfigurableParameter: changed_der = pes.to_der() """ + # for get_all_implementations(), telling callers about all practically useful parameters + is_abstract = True + # A subclass can set an explicit string as name (like name = "PIN1"). # If name is left None, then __init__() will set self.name to a name derived from the python class name (like # "pin1"). See also the get_name() classmethod when you have no instance at hand. name = None + allow_types = (str, int, ) allow_chars = None strip_chars = None @@ -220,6 +224,15 @@ class ConfigurableParameter: return (None, None) return (min(vals), max(vals)) + @classmethod + def get_all_implementations(cls, blacklist=None, allow_abstract=False): + # return a set() so that multiple inheritance does not return dups + return set(c + for c in all_subclasses_of(cls) + if ((allow_abstract or not c.is_abstract) + and ((not blacklist) or (c not in blacklist))) + ) + class DecimalParam(ConfigurableParameter): """Decimal digits. The input value may be a string of decimal digits like '012345', or an int. The output of @@ -314,6 +327,7 @@ class BinaryParam(ConfigurableParameter): class Iccid(DecimalParam): """ICCID Parameter. Input: string of decimal digits. If the string of digits is only 18 digits long, add a Luhn check digit.""" + is_abstract = False name = 'ICCID' min_len = 18 max_len = 20 @@ -334,6 +348,7 @@ class Iccid(DecimalParam): class Imsi(DecimalParam): """Configurable IMSI. Expects value to be a string of digits. Automatically sets the ACC to the last digit of the IMSI.""" + is_abstract = False name = 'IMSI' min_len = 6 @@ -513,10 +528,12 @@ class Puk(DecimalHexParam): f" cannot find pukCode with keyReference={cls.keyReference}") class Puk1(Puk): + is_abstract = False name = 'PUK1' keyReference = 0x01 class Puk2(Puk): + is_abstract = False name = 'PUK2' keyReference = 0x81 @@ -548,11 +565,13 @@ class Pin(DecimalHexParam): + f' {cls.get_name()} cannot find pinCode with keyReference={cls.keyReference}') class Pin1(Pin): + is_abstract = False name = 'PIN1' default_value = '0' * 4 # PIN are usually 4 digits keyReference = 0x01 class Pin2(Pin1): + is_abstract = False name = 'PIN2' keyReference = 0x81 @@ -569,10 +588,12 @@ class Pin2(Pin1): + f' {cls.get_name()} cannot find pinCode with keyReference={cls.keyReference} in {naa=}') class Adm1(Pin): + is_abstract = False name = 'ADM1' keyReference = 0x0A class Adm2(Adm1): + is_abstract = False name = 'ADM2' keyReference = 0x0B @@ -593,6 +614,7 @@ class AlgoConfig(ConfigurableParameter): f' {cls.__name__} cannot find algoParameter with key={cls.algo_config_key}') class AlgorithmID(DecimalParam, AlgoConfig): + is_abstract = False algo_config_key = 'algorithmID' allow_len = 1 default_value = 1 # Milenage @@ -608,6 +630,7 @@ class AlgorithmID(DecimalParam, AlgoConfig): class K(BinaryParam, AlgoConfig): """use validate_val() from BinaryParam, and apply_val() from AlgoConfig""" + is_abstract = False name = 'K' algo_config_key = 'key' allow_len = 128 // 8 # length in bytes (from BinaryParam) diff --git a/pySim/utils.py b/pySim/utils.py index 7d8e3fa8..7d7e536a 100644 --- a/pySim/utils.py +++ b/pySim/utils.py @@ -1116,3 +1116,9 @@ class CardCommandSet: if cla and not cmd.match_cla(cla): return None return cmd + + +def all_subclasses_of(cls): + for subc in cls.__subclasses__(): + yield subc + yield from all_subclasses_of(subc)