personalization: allow reading back multiple values from PES

Change-Id: Iecb68af7c216c6b9dc3add469564416b6f37f7b2
This commit is contained in:
Neels Hofmeyr
2025-03-25 22:49:51 +01:00
parent 5a42fab877
commit e0c6f8de64

View File

@@ -217,26 +217,24 @@ class ConfigurableParameter:
@classmethod @classmethod
def get_values_from_pes(cls, pes: ProfileElementSequence) -> Generator: def get_values_from_pes(cls, pes: ProfileElementSequence) -> Generator:
'''This is what subclasses implement: yield all values from a decoded profile package. """This is what subclasses implement: yield all values from a decoded profile package.
Find all values in the pes, and yield them decoded to a valid cls.input_value format. Find all values in the pes, and yield them decoded to a valid cls.input_value format.
Should be a generator function, i.e. use 'yield' instead of 'return'. Should be a generator function, i.e. use 'yield' instead of 'return'.
Usage example: Yielded value must be a dict(). Usually, an implementation will return only one key, like
cls = esim.saip.personalization.Iccid { "ICCID": "1234567890123456789" }
# use a set() to get a list of unique values from all results
vals = set( cls.get_values_from_pes(pes) ) Some implementations have more than one value to return, like
if len(vals) != 1:
raise ValueError(f'{cls.name}: need exactly one value, got {vals}') { "IMSI": "00101012345678", "IMSI-ACC" : "5" }
# the set contains a single value, return it
return vals.pop()
Implementation example: Implementation example:
for pe in pes: for pe in pes:
if my_condition(pe): if my_condition(pe):
yield b2h(my_bin_value_from(pe)) yield { cls.name: b2h(my_bin_value_from(pe)) }
''' """
pass pass
@classmethod @classmethod
@@ -365,12 +363,12 @@ class Iccid(DecimalParam):
def get_values_from_pes(cls, pes: ProfileElementSequence): def get_values_from_pes(cls, pes: ProfileElementSequence):
padded = b2h(pes.get_pe_for_type('header').decoded['iccid']) padded = b2h(pes.get_pe_for_type('header').decoded['iccid'])
iccid = unrpad(padded) iccid = unrpad(padded)
yield iccid yield { cls.name: iccid }
for pe in pes.get_pes_for_type('mf'): for pe in pes.get_pes_for_type('mf'):
iccid_pe = pe.decoded.get('ef-iccid', None) iccid_pe = pe.decoded.get('ef-iccid', None)
if iccid_pe: if iccid_pe:
yield dec_iccid(b2h(file_tuples_content_as_bytes(iccid_pe))) yield { cls.name: dec_iccid(b2h(file_tuples_content_as_bytes(iccid_pe))) }
class Imsi(DecimalParam): class Imsi(DecimalParam):
"""Configurable IMSI. Expects value to be a string of digits. Automatically sets the ACC to """Configurable IMSI. Expects value to be a string of digits. Automatically sets the ACC to
@@ -397,8 +395,13 @@ class Imsi(DecimalParam):
def get_values_from_pes(cls, pes: ProfileElementSequence): def get_values_from_pes(cls, pes: ProfileElementSequence):
for pe in pes.get_pes_for_type('usim'): for pe in pes.get_pes_for_type('usim'):
imsi_pe = pe.decoded.get('ef-imsi', None) imsi_pe = pe.decoded.get('ef-imsi', None)
acc_pe = pe.decoded.get('ef-acc', None)
y = {}
if imsi_pe: if imsi_pe:
yield dec_imsi(b2h(file_tuples_content_as_bytes(imsi_pe))) y[cls.name] = dec_imsi(b2h(file_tuples_content_as_bytes(imsi_pe)))
if acc_pe:
y[cls.name + '-ACC'] = b2h(file_tuples_content_as_bytes(acc_pe))
yield y
class SdKey(BinaryParam): class SdKey(BinaryParam):
@@ -439,7 +442,7 @@ class SdKey(BinaryParam):
for key in pe.decoded['keyList']: for key in pe.decoded['keyList']:
if key['keyIdentifier'][0] == cls.key_id and key['keyVersionNumber'][0] == cls.kvn: if key['keyIdentifier'][0] == cls.key_id and key['keyVersionNumber'][0] == cls.kvn:
if len(key['keyComponents']) >= 1: if len(key['keyComponents']) >= 1:
yield b2h(key['keyComponents'][0]['keyData']) yield { cls.name: b2h(key['keyComponents'][0]['keyData']) }
class SdKeyScp80_01(SdKey): class SdKeyScp80_01(SdKey):
kvn = 0x01 kvn = 0x01
@@ -583,7 +586,7 @@ class Puk(DecimalHexParam):
for pukCodes in obtain_all_pe_from_pelist(mf_pes, 'pukCodes'): for pukCodes in obtain_all_pe_from_pelist(mf_pes, 'pukCodes'):
for pukCode in pukCodes.decoded['pukCodes']: for pukCode in pukCodes.decoded['pukCodes']:
if pukCode['keyReference'] == cls.keyReference: if pukCode['keyReference'] == cls.keyReference:
yield cls.decimal_hex_to_str(pukCode['pukValue']) yield { cls.name: cls.decimal_hex_to_str(pukCode['pukValue']) }
class Puk1(Puk): class Puk1(Puk):
is_abstract = False is_abstract = False
@@ -624,13 +627,14 @@ class Pin(DecimalHexParam):
@classmethod @classmethod
def _read_all_pinvalues_from_pe(cls, pe: ProfileElement): def _read_all_pinvalues_from_pe(cls, pe: ProfileElement):
"This is a separate function because subclasses may feed different pe arguments."
for pinCodes in obtain_all_pe_from_pelist(pe, 'pinCodes'): for pinCodes in obtain_all_pe_from_pelist(pe, 'pinCodes'):
if pinCodes.decoded['pinCodes'][0] != 'pinconfig': if pinCodes.decoded['pinCodes'][0] != 'pinconfig':
continue continue
for pinCode in pinCodes.decoded['pinCodes'][1]: for pinCode in pinCodes.decoded['pinCodes'][1]:
if pinCode['keyReference'] == cls.keyReference: if pinCode['keyReference'] == cls.keyReference:
yield cls.decimal_hex_to_str(pinCode['pinValue']) yield { cls.name: cls.decimal_hex_to_str(pinCode['pinValue']) }
@classmethod @classmethod
def get_values_from_pes(cls, pes: ProfileElementSequence): def get_values_from_pes(cls, pes: ProfileElementSequence):
@@ -699,7 +703,11 @@ class AlgoConfig(ConfigurableParameter):
algoConfiguration = pe.decoded['algoConfiguration'] algoConfiguration = pe.decoded['algoConfiguration']
if algoConfiguration[0] != 'algoParameter': if algoConfiguration[0] != 'algoParameter':
continue continue
yield algoConfiguration[1][cls.algo_config_key] val = algoConfiguration[1][cls.algo_config_key]
if isinstance(val, bytes):
val = b2h(val)
# if it is an int (algorithmID), just pass thru as int
yield { cls.name: val }
class AlgorithmID(DecimalParam, AlgoConfig): class AlgorithmID(DecimalParam, AlgoConfig):