personalization: make AlgorithmID a new EnumParam

The AlgorithmID has a few preset values, and hardly anyone knows which
is which. So instead of entering '1', '2' or '3', make it work with
prededined values 'Milenage', 'TUAK' and 'usim-test'.

Implement the enum value part abstractly in new EnumParam.

Make AlgorithmID a subclass of EnumParam and define the values as from
pySim/esim/asn1/saip/PE_Definitions-3.3.1.asn

Related: SYS#6768
Change-Id: I71c2ec1b753c66cb577436944634f32792353240
This commit is contained in:
Neels Hofmeyr
2025-03-06 22:26:45 +01:00
parent 6dd155edeb
commit 83411a547b

View File

@@ -18,6 +18,7 @@
import abc import abc
import io import io
import os import os
import re
from typing import List, Tuple, Generator, Optional from typing import List, Tuple, Generator, Optional
from osmocom.tlv import camel_to_snake from osmocom.tlv import camel_to_snake
@@ -352,6 +353,70 @@ class BinaryParam(ConfigurableParameter):
return bytes(val) return bytes(val)
class EnumParam(ConfigurableParameter):
value_map = {
# For example:
#'Meaningful label for value 23': 0x23,
# Where 0x23 is a valid value to use for apply_val().
}
_value_map_reverse = None
@classmethod
def validate_val(cls, val):
orig_val = val
enum_val = None
if isinstance(val, str):
enum_name = val
enum_val = cls.map_name_to_val(enum_name)
# if the str is not one of the known value_map.keys(), is it maybe one of value_map.keys()?
if enum_val is None and val in cls.value_map.values():
enum_val = val
if enum_val not in cls.value_map.values():
raise ValueError(f"{cls.get_name()}: invalid argument: {orig_val!r}. Valid arguments are:"
f" {', '.join(cls.value_map.keys())}")
return enum_val
@classmethod
def map_name_to_val(cls, name:str, strict=True):
val = cls.value_map.get(name)
if val is not None:
return val
clean_name = cls.clean_name_str(name)
for k, v in cls.value_map.items():
if clean_name == cls.clean_name_str(k):
return v
if strict:
raise ValueError(f"Problem in {cls.get_name()}: {name!r} is not a known value."
f" Known values are: {cls.value_map.keys()!r}")
return None
@classmethod
def map_val_to_name(cls, val, strict=False) -> str:
if cls._value_map_reverse is None:
cls._value_map_reverse = dict((v, k) for k, v in cls.value_map.items())
name = cls._value_map_reverse.get(val)
if name:
return name
if strict:
raise ValueError(f"Problem in {cls.get_name()}: {val!r} ({type(val)}) is not a known value."
f" Known values are: {cls.value_map.values()!r}")
return None
@classmethod
def name_normalize(cls, name:str) -> str:
return cls.map_val_to_name(cls.map_name_to_val(name))
@classmethod
def clean_name_str(cls, val):
return re.sub('[^0-9A-Za-z-_]', '', val).lower()
class Iccid(DecimalParam): class Iccid(DecimalParam):
"""ICCID Parameter. Input: string of decimal digits. """ICCID Parameter. Input: string of decimal digits.
If the string of digits is only 18 digits long, add a Luhn check digit.""" If the string of digits is only 18 digits long, add a Luhn check digit."""
@@ -757,21 +822,36 @@ class AlgoConfig(ConfigurableParameter):
# if it is an int (algorithmID), just pass thru as int # if it is an int (algorithmID), just pass thru as int
yield { cls.name: val } yield { cls.name: val }
class AlgorithmID(EnumParam, AlgoConfig):
'''use validate_val() from EnumParam, and apply_val() from AlgoConfig.
In get_values_from_pes(), return enum value names, not raw values.'''
name = "Algorithm"
class AlgorithmID(DecimalParam, AlgoConfig): # as in pySim/esim/asn1/saip/PE_Definitions-3.3.1.asn
algo_config_key = 'algorithmID' value_map = {
allow_len = 1 "Milenage" : 1,
example_input = 1 # Milenage "TUAK" : 2,
"usim-test" : 3,
}
example_input = "Milenage"
default_source = param_source.ConstantSource default_source = param_source.ConstantSource
algo_config_key = 'algorithmID'
# EnumParam.validate_val() returns the int values from value_map
@classmethod @classmethod
def validate_val(cls, val): def get_values_from_pes(cls, pes: ProfileElementSequence):
val = super().validate_val(val) # return enum names, not raw values.
val = int(val) # use of super(): this intends to call AlgoConfig.get_values_from_pes() so that the cls argument is this cls
valid = (1, 2, 3) # here (AlgorithmID); i.e. AlgoConfig.get_values_from_pes(pes) doesn't work, because AlgoConfig needs to look up
if val not in valid: # cls.algo_config_key.
raise ValueError(f'Invalid algorithmID {val!r}, must be one of {valid}') for d in super(cls, cls).get_values_from_pes(pes):
return val if cls.name in d:
# convert int to value string
val = d[cls.name]
d[cls.name] = cls.map_val_to_name(val, strict=True)
yield d
class K(BinaryParam, AlgoConfig): class K(BinaryParam, AlgoConfig):
"""use validate_val() from BinaryParam, and apply_val() from AlgoConfig""" """use validate_val() from BinaryParam, and apply_val() from AlgoConfig"""