mirror of
https://gitea.osmocom.org/sim-card/pysim.git
synced 2026-03-16 18:38:32 +03:00
personalization: refactor AlgorithmID, K, Opc
Refactor AlgorithmID, K, Opc to the new ConfigurableParameter implementation style. K and Opc use a common abstract BinaryParam. Note from the future: AlgorithmID so far takes "raw" int values, but will turn to be an "enum" parameter with predefined meaningful strings in I71c2ec1b753c66cb577436944634f32792353240 Change-Id: I6296fdcfd5d2ed313c4aade57ff43cc362375848
This commit is contained in:
@@ -254,6 +254,51 @@ class DecimalHexParam(DecimalParam):
|
|||||||
# a DecimalHexParam subclass expects the apply_val() input to be a bytes instance ready for the pes
|
# a DecimalHexParam subclass expects the apply_val() input to be a bytes instance ready for the pes
|
||||||
return h2b(val)
|
return h2b(val)
|
||||||
|
|
||||||
|
class IntegerParam(ConfigurableParameter):
|
||||||
|
allow_types = (str, int)
|
||||||
|
allow_chars = '0123456789'
|
||||||
|
|
||||||
|
# two integers, if the resulting int should be range limited
|
||||||
|
min_val = None
|
||||||
|
max_val = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_val(cls, val):
|
||||||
|
val = super().validate_val(val)
|
||||||
|
val = int(val)
|
||||||
|
exceeds_limits = False
|
||||||
|
if cls.min_val is not None:
|
||||||
|
if val < cls.min_val:
|
||||||
|
exceeds_limits = True
|
||||||
|
if cls.max_val is not None:
|
||||||
|
if val > cls.max_val:
|
||||||
|
exceeds_limits = True
|
||||||
|
if exceeds_limits:
|
||||||
|
raise ValueError(f'Value {val} is out of range, must be [{cls.min_val}..{cls.max_val}]')
|
||||||
|
return val
|
||||||
|
|
||||||
|
class BinaryParam(ConfigurableParameter):
|
||||||
|
allow_types = (str, io.BytesIO, bytes, bytearray)
|
||||||
|
allow_chars = '0123456789abcdefABCDEF'
|
||||||
|
strip_chars = ' \t\r\n'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate_val(cls, val):
|
||||||
|
# take care that min_len and max_len are applied to the binary length by converting to bytes first
|
||||||
|
if isinstance(val, str):
|
||||||
|
if cls.strip_chars is not None:
|
||||||
|
val = ''.join(c for c in val if c not in cls.strip_chars)
|
||||||
|
if len(val) & 1:
|
||||||
|
raise ValueError('Invalid hexadecimal string, must have even number of digits:'
|
||||||
|
f' {val!r} {len(val)=}')
|
||||||
|
try:
|
||||||
|
val = h2b(val)
|
||||||
|
except ValueError as e:
|
||||||
|
raise ValueError(f'Invalid hexadecimal string: {val!r} {len(val)=}') from e
|
||||||
|
|
||||||
|
val = super().validate_val(val)
|
||||||
|
return bytes(val)
|
||||||
|
|
||||||
|
|
||||||
class Iccid(DecimalParam):
|
class Iccid(DecimalParam):
|
||||||
"""ICCID Parameter. Input: string of decimal digits.
|
"""ICCID Parameter. Input: string of decimal digits.
|
||||||
@@ -513,42 +558,60 @@ class Adm1(Pin):
|
|||||||
class Adm2(Pin):
|
class Adm2(Pin):
|
||||||
keyReference = 0x0B
|
keyReference = 0x0B
|
||||||
|
|
||||||
|
class AlgoConfig(ConfigurableParameter):
|
||||||
|
algo_config_key = None
|
||||||
|
|
||||||
class AlgoConfig(ConfigurableParameter, metaclass=ClassVarMeta):
|
@classmethod
|
||||||
"""Configurable Algorithm parameter."""
|
def apply_val(cls, pes: ProfileElementSequence, val):
|
||||||
key = None
|
found = 0
|
||||||
def validate(self):
|
|
||||||
if not isinstance(self.input_value, (io.BytesIO, bytes, bytearray)):
|
|
||||||
raise ValueError('Value must be of bytes-like type')
|
|
||||||
self.value = self.input_value
|
|
||||||
def apply(self, pes: ProfileElementSequence):
|
|
||||||
for pe in pes.get_pes_for_type('akaParameter'):
|
for pe in pes.get_pes_for_type('akaParameter'):
|
||||||
algoConfiguration = pe.decoded['algoConfiguration']
|
algoConfiguration = pe.decoded['algoConfiguration']
|
||||||
if algoConfiguration[0] != 'algoParameter':
|
if algoConfiguration[0] != 'algoParameter':
|
||||||
continue
|
continue
|
||||||
algoConfiguration[1][self.key] = self.value
|
algoConfiguration[1][cls.algo_config_key] = val
|
||||||
|
found += 1
|
||||||
|
if not found:
|
||||||
|
raise ValueError('input template UPP has unexpected structure:'
|
||||||
|
f' {cls.__name__} cannot find algoParameter with key={cls.algo_config_key}')
|
||||||
|
|
||||||
class K(AlgoConfig, key='key'):
|
class AlgorithmID(DecimalParam, AlgoConfig):
|
||||||
pass
|
algo_config_key = 'algorithmID'
|
||||||
class Opc(AlgoConfig, key='opc'):
|
allow_len = 1
|
||||||
pass
|
|
||||||
class AlgorithmID(AlgoConfig, key='algorithmID'):
|
@classmethod
|
||||||
def validate(self):
|
def validate_val(cls, val):
|
||||||
if self.input_value not in [1, 2, 3]:
|
val = super().validate_val(val)
|
||||||
raise ValueError('Invalid algorithmID %s' % (self.input_value))
|
val = int(val)
|
||||||
self.value = self.input_value
|
valid = (1, 2, 3)
|
||||||
class MilenageRotationConstants(AlgoConfig, key='rotationConstants'):
|
if val not in valid:
|
||||||
|
raise ValueError(f'Invalid algorithmID {val!r}, must be one of {valid}')
|
||||||
|
return val
|
||||||
|
|
||||||
|
class K(BinaryParam, AlgoConfig):
|
||||||
|
"""use validate_val() from BinaryParam, and apply_val() from AlgoConfig"""
|
||||||
|
algo_config_key = 'key'
|
||||||
|
allow_len = (128 // 8, 256 // 8) # length in bytes (from BinaryParam); TUAK also allows 256 bit
|
||||||
|
|
||||||
|
class Opc(K):
|
||||||
|
algo_config_key = 'opc'
|
||||||
|
|
||||||
|
class MilenageRotationConstants(BinaryParam, AlgoConfig):
|
||||||
"""rotation constants r1,r2,r3,r4,r5 of Milenage, Range 0..127. See 3GPP TS 35.206 Sections 2.3 + 5.3.
|
"""rotation constants r1,r2,r3,r4,r5 of Milenage, Range 0..127. See 3GPP TS 35.206 Sections 2.3 + 5.3.
|
||||||
Provided as octet-string concatenation of all 5 constants. Expects a bytes-like object of length 5, with
|
Provided as octet-string concatenation of all 5 constants. Expects a bytes-like object of length 5, with
|
||||||
each byte in the range of 0..127. The default value by 3GPP is '4000204060' (hex notation)"""
|
each byte in the range of 0..127. The default value by 3GPP is '4000204060' (hex notation)"""
|
||||||
def validate(self):
|
algo_config_key = 'rotationConstants'
|
||||||
super().validate()
|
allow_len = 5 # length in bytes (from BinaryParam)
|
||||||
if len(self.input_value) != 5:
|
|
||||||
raise ValueError('Length of value must be 5 octets')
|
@classmethod
|
||||||
for r in self.input_value:
|
def validate_val(cls, val):
|
||||||
if r > 127:
|
"allow_len checks the length, this in addition checks the value range"
|
||||||
raise ValueError('r values must be between 0 and 127')
|
val = super().validate_val(val)
|
||||||
class MilenageXoringConstants(AlgoConfig, key='xoringConstants'):
|
assert isinstance(val, bytes)
|
||||||
|
if any(r > 127 for r in val):
|
||||||
|
raise ValueError('r values must be in the range 0..127')
|
||||||
|
return val
|
||||||
|
|
||||||
|
class MilenageXoringConstants(BinaryParam, AlgoConfig):
|
||||||
"""XOR-ing constants c1,c2,c3,c4,c5 of Milenage, 128bit each. See 3GPP TS 35.206 Sections 2.3 + 5.3.
|
"""XOR-ing constants c1,c2,c3,c4,c5 of Milenage, 128bit each. See 3GPP TS 35.206 Sections 2.3 + 5.3.
|
||||||
Provided as octet-string concatenation of all 5 constants. The default value by 3GPP is the concetenation
|
Provided as octet-string concatenation of all 5 constants. The default value by 3GPP is the concetenation
|
||||||
of:
|
of:
|
||||||
@@ -558,16 +621,11 @@ class MilenageXoringConstants(AlgoConfig, key='xoringConstants'):
|
|||||||
00000000000000000000000000000004
|
00000000000000000000000000000004
|
||||||
00000000000000000000000000000008
|
00000000000000000000000000000008
|
||||||
"""
|
"""
|
||||||
def validate(self):
|
algo_config_key = 'xoringConstants'
|
||||||
super().validate()
|
allow_len = 80 # length in bytes (from BinaryParam)
|
||||||
if len(self.input_value) != 80:
|
|
||||||
raise ValueError('Length of value must be 80 octets')
|
class TuakNumberOfKeccak(IntegerParam, AlgoConfig):
|
||||||
class TuakNumberOfKeccak(AlgoConfig, key='numberOfKeccak'):
|
"""Number of iterations of Keccak-f[1600] permutation as recomended by Section 7.2 of 3GPP TS 35.231"""
|
||||||
"""Number of iterations of Keccak-f[1600] permutation as recomended by Section 7.2 of 3GPP TS 35.231.
|
algo_config_key = 'numberOfKeccak'
|
||||||
The default value by 3GPP is 1."""
|
min_val = 1
|
||||||
def validate(self):
|
max_val = 255
|
||||||
if not isinstance(self.input_value, int):
|
|
||||||
raise ValueError('Value must be an integer')
|
|
||||||
if self.input_value < 1 or self.input_value > 255:
|
|
||||||
raise ValueError('Value must be an integer between 1 and 255')
|
|
||||||
self.value = self.input_value
|
|
||||||
|
|||||||
Reference in New Issue
Block a user