[1/6] personalization: refactor: drop ClassVarMeta use

Drop the ClassVarMeta/metaclass/ABCMeta stuff -- it doesn't seem to
serve any purpose that is not similarly achieved with plain python
inheritance.

Upcoming patches will use normal inheritance a lot more.

Note that most use of the ClassVarMeta was in the SdKey subclasses, and
that these currently don't actually work. See the fix in patch
I07dfc378705eba1318e9e8652796cbde106c6a52 .

name: set a default name from the python class, as ClassVarMeta did.
Also allow setting an explicit string as name instead, per subclass
implementation (see I31f390d634e58c384589c50a33ca45d6f86d4e10).

Related: SYS#6768
Change-Id: I60ea8fd11fb438ec90ddb08b17b658cbb789c051
This commit is contained in:
Neels Hofmeyr
2025-03-01 00:53:15 +01:00
parent 572a81f2af
commit 8e6a19d9f0

View File

@@ -34,24 +34,34 @@ def file_replace_content(file: List[Tuple], new_content: bytes):
file.append(('fillFileContent', new_content)) file.append(('fillFileContent', new_content))
return file return file
class ClassVarMeta(abc.ABCMeta): class ConfigurableParameter:
"""Metaclass that puts all additional keyword-args into the class. We use this to have one
class definition for something like a PIN, and then have derived classes for PIN1, PIN2, ..."""
def __new__(metacls, name, bases, namespace, **kwargs):
#print("Meta_new_(metacls=%s, name=%s, bases=%s, namespace=%s, kwargs=%s)" % (metacls, name, bases, namespace, kwargs))
x = super().__new__(metacls, name, bases, namespace)
for k, v in kwargs.items():
setattr(x, k, v)
setattr(x, 'name', camel_to_snake(name))
return x
class ConfigurableParameter(abc.ABC, metaclass=ClassVarMeta):
"""Base class representing a part of the eSIM profile that is configurable during the """Base class representing a part of the eSIM profile that is configurable during the
personalization process (with dynamic data from elsewhere).""" personalization process (with dynamic data from elsewhere)."""
# 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
def __init__(self, input_value): def __init__(self, input_value):
self.input_value = input_value # the raw input value as given by caller self.input_value = input_value # the raw input value as given by caller
self.value = None # the processed input value (e.g. with check digit) as produced by validate() self.value = None # the processed input value (e.g. with check digit) as produced by validate()
# set the instance's name to either an explicit name string, or to a name derived from the class name.
if self.name is None:
self.name = self.get_name()
@classmethod
def get_name(cls):
"""Return cls.name when it is set, otherwise return the python class name converted from 'CamelCase' to
'snake_case'.
When using class *instances*, you can just use my_instance.name.
When using *classes*, cls.get_name() returns the same name a class instance would have.
"""
if cls.name:
return cls.name
return camel_to_snake(cls.__name__)
def validate(self): def validate(self):
"""Optional validation method. Can be used by derived classes to perform validation """Optional validation method. Can be used by derived classes to perform validation
of the input value (self.value). Will raise an exception if validation fails.""" of the input value (self.value). Will raise an exception if validation fails."""
@@ -105,7 +115,7 @@ class Imsi(ConfigurableParameter):
# TODO: DF.GSM_ACCESS if not linked? # TODO: DF.GSM_ACCESS if not linked?
class SdKey(ConfigurableParameter, metaclass=ClassVarMeta): class SdKey(ConfigurableParameter):
"""Configurable Security Domain (SD) Key. Value is presented as bytes.""" """Configurable Security Domain (SD) Key. Value is presented as bytes."""
# these will be set by derived classes # these will be set by derived classes
key_type = None key_type = None
@@ -144,59 +154,108 @@ class SdKey(ConfigurableParameter, metaclass=ClassVarMeta):
for pe in pes.get_pes_for_type('securityDomain'): for pe in pes.get_pes_for_type('securityDomain'):
self._apply_sd(pe) self._apply_sd(pe)
class SdKeyScp80_01(SdKey, kvn=0x01, key_type=0x88, permitted_len=[16,24,32]): # AES key type class SdKeyScp80_01(SdKey):
pass kvn = 0x01
class SdKeyScp80_01Kic(SdKeyScp80_01, key_id=0x01, key_usage_qual=0x18): # FIXME: ordering? key_type = 0x88 # AES key type
pass allow_len = (16,24,32)
class SdKeyScp80_01Kid(SdKeyScp80_01, key_id=0x02, key_usage_qual=0x14):
pass
class SdKeyScp80_01Kik(SdKeyScp80_01, key_id=0x03, key_usage_qual=0x48):
pass
class SdKeyScp81_01(SdKey, kvn=0x81): # FIXME class SdKeyScp80_01Kic(SdKeyScp80_01):
pass key_id = 0x01
class SdKeyScp81_01Psk(SdKeyScp81_01, key_id=0x01, key_type=0x85, key_usage_qual=0x3C): key_usage_qual = 0x18 # FIXME: ordering?
pass
class SdKeyScp81_01Dek(SdKeyScp81_01, key_id=0x02, key_type=0x88, key_usage_qual=0x48):
pass
class SdKeyScp02_20(SdKey, kvn=0x20, key_type=0x88, permitted_len=[16,24,32]): # AES key type class SdKeyScp80_01Kid(SdKeyScp80_01):
pass key_id = 0x02
class SdKeyScp02_20Enc(SdKeyScp02_20, key_id=0x01, key_usage_qual=0x18): key_usage_qual = 0x14
pass
class SdKeyScp02_20Mac(SdKeyScp02_20, key_id=0x02, key_usage_qual=0x14):
pass
class SdKeyScp02_20Dek(SdKeyScp02_20, key_id=0x03, key_usage_qual=0x48):
pass
class SdKeyScp03_30(SdKey, kvn=0x30, key_type=0x88, permitted_len=[16,24,32]): # AES key type class SdKeyScp80_01Kik(SdKeyScp80_01):
pass key_id = 0x03
class SdKeyScp03_30Enc(SdKeyScp03_30, key_id=0x01, key_usage_qual=0x18): key_usage_qual = 0x48
pass
class SdKeyScp03_30Mac(SdKeyScp03_30, key_id=0x02, key_usage_qual=0x14):
pass
class SdKeyScp03_30Dek(SdKeyScp03_30, key_id=0x03, key_usage_qual=0x48):
pass
class SdKeyScp03_31(SdKey, kvn=0x31, key_type=0x88, permitted_len=[16,24,32]): # AES key type
pass
class SdKeyScp03_31Enc(SdKeyScp03_31, key_id=0x01, key_usage_qual=0x18):
pass
class SdKeyScp03_31Mac(SdKeyScp03_31, key_id=0x02, key_usage_qual=0x14):
pass
class SdKeyScp03_31Dek(SdKeyScp03_31, key_id=0x03, key_usage_qual=0x48):
pass
class SdKeyScp03_32(SdKey, kvn=0x32, key_type=0x88, permitted_len=[16,24,32]): # AES key type
pass
class SdKeyScp03_32Enc(SdKeyScp03_32, key_id=0x01, key_usage_qual=0x18):
pass
class SdKeyScp03_32Mac(SdKeyScp03_32, key_id=0x02, key_usage_qual=0x14):
pass
class SdKeyScp03_32Dek(SdKeyScp03_32, key_id=0x03, key_usage_qual=0x48):
pass
class SdKeyScp81_01(SdKey):
kvn = 0x81 # FIXME
class SdKeyScp81_01Psk(SdKeyScp81_01):
key_id = 0x01
key_type = 0x85
key_usage_qual = 0x3C
class SdKeyScp81_01Dek(SdKeyScp81_01):
key_id = 0x02
key_type = 0x88
key_usage_qual = 0x48
class SdKeyScp02_20(SdKey):
kvn = 0x20
key_type = 0x88 # AES key type
allow_len = (16,24,32)
class SdKeyScp02_20Enc(SdKeyScp02_20):
key_id = 0x01
key_usage_qual = 0x18
class SdKeyScp02_20Mac(SdKeyScp02_20):
key_id = 0x02
key_usage_qual = 0x14
class SdKeyScp02_20Dek(SdKeyScp02_20):
key_id = 0x03
key_usage_qual = 0x48
class SdKeyScp03_30(SdKey):
kvn = 0x30
key_type = 0x88 # AES key type
allow_len = (16,24,32)
class SdKeyScp03_30Enc(SdKeyScp03_30):
key_id = 0x01
key_usage_qual = 0x18
class SdKeyScp03_30Mac(SdKeyScp03_30):
key_id = 0x02
key_usage_qual = 0x14
class SdKeyScp03_30Dek(SdKeyScp03_30):
key_id = 0x03
key_usage_qual = 0x48
class SdKeyScp03_31(SdKey):
kvn = 0x31
key_type = 0x88 # AES key type
allow_len = (16,24,32)
class SdKeyScp03_31Enc(SdKeyScp03_31):
key_id = 0x01
key_usage_qual = 0x18
class SdKeyScp03_31Mac(SdKeyScp03_31):
key_id = 0x02
key_usage_qual = 0x14
class SdKeyScp03_31Dek(SdKeyScp03_31):
key_id = 0x03
key_usage_qual = 0x48
class SdKeyScp03_32(SdKey):
kvn = 0x32
key_type = 0x88 # AES key type
allow_len = (16,24,32)
class SdKeyScp03_32Enc(SdKeyScp03_32):
key_id = 0x01
key_usage_qual = 0x18
class SdKeyScp03_32Mac(SdKeyScp03_32):
key_id = 0x02
key_usage_qual = 0x14
class SdKeyScp03_32Dek(SdKeyScp03_32):
key_id = 0x03
key_usage_qual = 0x48
def obtain_singleton_pe_from_pelist(l: List[ProfileElement], wanted_type: str) -> ProfileElement: def obtain_singleton_pe_from_pelist(l: List[ProfileElement], wanted_type: str) -> ProfileElement:
@@ -208,7 +267,7 @@ def obtain_first_pe_from_pelist(l: List[ProfileElement], wanted_type: str) -> Pr
filtered = list(filter(lambda x: x.type == wanted_type, l)) filtered = list(filter(lambda x: x.type == wanted_type, l))
return filtered[0] return filtered[0]
class Puk(ConfigurableParameter, metaclass=ClassVarMeta): class Puk(ConfigurableParameter):
"""Configurable PUK (Pin Unblock Code). String ASCII-encoded digits.""" """Configurable PUK (Pin Unblock Code). String ASCII-encoded digits."""
keyReference = None keyReference = None
def validate(self): def validate(self):
@@ -230,12 +289,14 @@ class Puk(ConfigurableParameter, metaclass=ClassVarMeta):
pukCode['pukValue'] = h2b(padded_puk) pukCode['pukValue'] = h2b(padded_puk)
return return
raise ValueError('cannot find pukCode') raise ValueError('cannot find pukCode')
class Puk1(Puk, keyReference=0x01):
pass
class Puk2(Puk, keyReference=0x81):
pass
class Pin(ConfigurableParameter, metaclass=ClassVarMeta): class Puk1(Puk):
keyReference = 0x01
class Puk2(Puk):
keyReference = 0x81
class Pin(ConfigurableParameter):
"""Configurable PIN (Personal Identification Number). String of digits.""" """Configurable PIN (Personal Identification Number). String of digits."""
keyReference = None keyReference = None
def validate(self): def validate(self):
@@ -259,7 +320,8 @@ class Pin(ConfigurableParameter, metaclass=ClassVarMeta):
pinCode['pinValue'] = h2b(padded_pin) pinCode['pinValue'] = h2b(padded_pin)
return return
raise ValueError('cannot find pinCode') raise ValueError('cannot find pinCode')
class AppPin(ConfigurableParameter, metaclass=ClassVarMeta):
class AppPin(ConfigurableParameter):
"""Configurable PIN (Personal Identification Number). String of digits.""" """Configurable PIN (Personal Identification Number). String of digits."""
keyReference = None keyReference = None
def validate(self): def validate(self):
@@ -288,18 +350,21 @@ class AppPin(ConfigurableParameter, metaclass=ClassVarMeta):
continue continue
for instance in pes.pes_by_naa[naa]: for instance in pes.pes_by_naa[naa]:
self._apply_one(instance) self._apply_one(instance)
class Pin1(Pin, keyReference=0x01): class Pin1(Pin):
pass keyReference = 0x01
# PIN2 is special: telecom + usim + isim + csim # PIN2 is special: telecom + usim + isim + csim
class Pin2(AppPin, keyReference=0x81): class Pin2(AppPin):
pass keyReference = 0x81
class Adm1(Pin, keyReference=0x0A):
pass class Adm1(Pin):
class Adm2(Pin, keyReference=0x0B): keyReference = 0x0A
pass
class Adm2(Pin):
keyReference = 0x0B
class AlgoConfig(ConfigurableParameter, metaclass=ClassVarMeta): class AlgoConfig(ConfigurableParameter):
"""Configurable Algorithm parameter.""" """Configurable Algorithm parameter."""
key = None key = None
def validate(self): def validate(self):
@@ -313,11 +378,13 @@ class AlgoConfig(ConfigurableParameter, metaclass=ClassVarMeta):
continue continue
algoConfiguration[1][self.key] = self.value algoConfiguration[1][self.key] = self.value
class K(AlgoConfig, key='key'): class K(AlgoConfig):
pass key = 'key'
class Opc(AlgoConfig, key='opc'): class Opc(AlgoConfig):
pass key = 'opc'
class AlgorithmID(AlgoConfig, key='algorithmID'):
class AlgorithmID(AlgoConfig):
key = 'algorithmID'
def validate(self): def validate(self):
if self.input_value not in [1, 2, 3]: if self.input_value not in [1, 2, 3]:
raise ValueError('Invalid algorithmID %s' % (self.input_value)) raise ValueError('Invalid algorithmID %s' % (self.input_value))