mirror of https://gerrit.osmocom.org/pysim
Prepare for decoding/encoding records differently based on record number
In their infinite wisdom, the authors of the EIRENE FFFIS for GSM-R SIM cards invented yet a new way of encoding data in SIM card files: The first record of a file may be encoded differently than further records of files. Let's add the required infrastructure to pySim so that the encode and decode methods for record-oriented files get passed in the current record number. Change-Id: I02d6942016dd0631b21d1fd301711c13cb27962b Related: OS#5784changes/55/31055/2
parent
8dbf714e96
commit
f6b37af721
|
@ -201,7 +201,7 @@ class ReadRecord(ApduCommand, n='READ RECORD', ins=0xB2, cla=['0X', '4X', '6X'])
|
|||
return b2h(self.rsp_data)
|
||||
method = getattr(self.file, 'decode_record_bin', None)
|
||||
if self.successful and callable(method):
|
||||
return method(self.rsp_data)
|
||||
return method(self.rsp_data, self.cmd_dict['record_number'])
|
||||
|
||||
# TS 102 221 Section 11.1.6
|
||||
class UpdateRecord(ApduCommand, n='UPDATE RECORD', ins=0xDC, cla=['0X', '4X', '6X']):
|
||||
|
@ -217,7 +217,7 @@ class UpdateRecord(ApduCommand, n='UPDATE RECORD', ins=0xDC, cla=['0X', '4X', '6
|
|||
return b2h(self.cmd_data)
|
||||
method = getattr(self.file, 'decode_record_bin', None)
|
||||
if self.successful and callable(method):
|
||||
return method(self.cmd_data)
|
||||
return method(self.cmd_data, self.cmd_dict['record_number'])
|
||||
|
||||
# TS 102 221 Section 11.1.7
|
||||
class SearchRecord(ApduCommand, n='SEARCH RECORD', ins=0xA2, cla=['0X', '4X', '6X']):
|
||||
|
|
|
@ -945,7 +945,7 @@ class LinFixedEF(CardEF):
|
|||
self._construct = None
|
||||
self._tlv = None
|
||||
|
||||
def decode_record_hex(self, raw_hex_data: str) -> dict:
|
||||
def decode_record_hex(self, raw_hex_data: str, record_nr: int) -> dict:
|
||||
"""Decode raw (hex string) data into abstract representation.
|
||||
|
||||
A derived class would typically provide a _decode_record_bin() or _decode_record_hex()
|
||||
|
@ -954,16 +954,17 @@ class LinFixedEF(CardEF):
|
|||
|
||||
Args:
|
||||
raw_hex_data : hex-encoded data
|
||||
record_nr : record number (1 for first record, ...)
|
||||
Returns:
|
||||
abstract_data; dict representing the decoded data
|
||||
"""
|
||||
method = getattr(self, '_decode_record_hex', None)
|
||||
if callable(method):
|
||||
return method(raw_hex_data)
|
||||
return method(raw_hex_data, record_nr=record_nr)
|
||||
raw_bin_data = h2b(raw_hex_data)
|
||||
method = getattr(self, '_decode_record_bin', None)
|
||||
if callable(method):
|
||||
return method(raw_bin_data)
|
||||
return method(raw_bin_data, record_nr=record_nr)
|
||||
if self._construct:
|
||||
return parse_construct(self._construct, raw_bin_data)
|
||||
elif self._tlv:
|
||||
|
@ -972,7 +973,7 @@ class LinFixedEF(CardEF):
|
|||
return t.to_dict()
|
||||
return {'raw': raw_bin_data.hex()}
|
||||
|
||||
def decode_record_bin(self, raw_bin_data: bytearray) -> dict:
|
||||
def decode_record_bin(self, raw_bin_data: bytearray, record_nr: int) -> dict:
|
||||
"""Decode raw (binary) data into abstract representation.
|
||||
|
||||
A derived class would typically provide a _decode_record_bin() or _decode_record_hex()
|
||||
|
@ -981,16 +982,17 @@ class LinFixedEF(CardEF):
|
|||
|
||||
Args:
|
||||
raw_bin_data : binary encoded data
|
||||
record_nr : record number (1 for first record, ...)
|
||||
Returns:
|
||||
abstract_data; dict representing the decoded data
|
||||
"""
|
||||
method = getattr(self, '_decode_record_bin', None)
|
||||
if callable(method):
|
||||
return method(raw_bin_data)
|
||||
return method(raw_bin_data, record_nr=record_nr)
|
||||
raw_hex_data = b2h(raw_bin_data)
|
||||
method = getattr(self, '_decode_record_hex', None)
|
||||
if callable(method):
|
||||
return method(raw_hex_data)
|
||||
return method(raw_hex_data, record_nr=record_nr)
|
||||
if self._construct:
|
||||
return parse_construct(self._construct, raw_bin_data)
|
||||
elif self._tlv:
|
||||
|
@ -999,7 +1001,7 @@ class LinFixedEF(CardEF):
|
|||
return t.to_dict()
|
||||
return {'raw': raw_hex_data}
|
||||
|
||||
def encode_record_hex(self, abstract_data: dict) -> str:
|
||||
def encode_record_hex(self, abstract_data: dict, record_nr: int) -> str:
|
||||
"""Encode abstract representation into raw (hex string) data.
|
||||
|
||||
A derived class would typically provide an _encode_record_bin() or _encode_record_hex()
|
||||
|
@ -1008,15 +1010,16 @@ class LinFixedEF(CardEF):
|
|||
|
||||
Args:
|
||||
abstract_data : dict representing the decoded data
|
||||
record_nr : record number (1 for first record, ...)
|
||||
Returns:
|
||||
hex string encoded data
|
||||
"""
|
||||
method = getattr(self, '_encode_record_hex', None)
|
||||
if callable(method):
|
||||
return method(abstract_data)
|
||||
return method(abstract_data, record_nr=record_nr)
|
||||
method = getattr(self, '_encode_record_bin', None)
|
||||
if callable(method):
|
||||
raw_bin_data = method(abstract_data)
|
||||
raw_bin_data = method(abstract_data, record_nr=record_nr)
|
||||
return b2h(raw_bin_data)
|
||||
if self._construct:
|
||||
return b2h(self._construct.build(abstract_data))
|
||||
|
@ -1027,7 +1030,7 @@ class LinFixedEF(CardEF):
|
|||
raise NotImplementedError(
|
||||
"%s encoder not yet implemented. Patches welcome." % self)
|
||||
|
||||
def encode_record_bin(self, abstract_data: dict) -> bytearray:
|
||||
def encode_record_bin(self, abstract_data: dict, record_nr : int) -> bytearray:
|
||||
"""Encode abstract representation into raw (binary) data.
|
||||
|
||||
A derived class would typically provide an _encode_record_bin() or _encode_record_hex()
|
||||
|
@ -1036,15 +1039,16 @@ class LinFixedEF(CardEF):
|
|||
|
||||
Args:
|
||||
abstract_data : dict representing the decoded data
|
||||
record_nr : record number (1 for first record, ...)
|
||||
Returns:
|
||||
binary encoded data
|
||||
"""
|
||||
method = getattr(self, '_encode_record_bin', None)
|
||||
if callable(method):
|
||||
return method(abstract_data)
|
||||
return method(abstract_data, record_nr=record_nr)
|
||||
method = getattr(self, '_encode_record_hex', None)
|
||||
if callable(method):
|
||||
return h2b(method(abstract_data))
|
||||
return h2b(method(abstract_data, record_nr=record_nr))
|
||||
if self._construct:
|
||||
return self._construct.build(abstract_data)
|
||||
elif self._tlv:
|
||||
|
@ -1681,7 +1685,7 @@ class RuntimeLchan:
|
|||
abstract data contained in record
|
||||
"""
|
||||
(data, sw) = self.read_record(rec_nr)
|
||||
return (self.selected_file.decode_record_hex(data), sw)
|
||||
return (self.selected_file.decode_record_hex(data, rec_nr), sw)
|
||||
|
||||
def update_record(self, rec_nr: int, data_hex: str):
|
||||
"""Update a record with given binary data
|
||||
|
@ -1702,7 +1706,7 @@ class RuntimeLchan:
|
|||
rec_nr : Record number to read
|
||||
data_hex : Abstract data to be written
|
||||
"""
|
||||
data_hex = self.selected_file.encode_record_hex(data)
|
||||
data_hex = self.selected_file.encode_record_hex(data, rec_nr)
|
||||
return self.update_record(rec_nr, data_hex)
|
||||
|
||||
def retrieve_data(self, tag: int = 0):
|
||||
|
|
|
@ -87,7 +87,7 @@ class EF_0348_KEY(LinFixedEF):
|
|||
def __init__(self, fid='6f22', name='EF.0348_KEY', desc='TS 03.48 OTA Keys'):
|
||||
super().__init__(fid, name=name, desc=desc, rec_len=(27, 35))
|
||||
|
||||
def _decode_record_bin(self, raw_bin_data):
|
||||
def _decode_record_bin(self, raw_bin_data, **kwargs):
|
||||
u = unpack('!BBB', raw_bin_data[0:3])
|
||||
key_algo = (u[2] >> 6) & 1
|
||||
key_length = ((u[2] >> 3) & 3) * 8
|
||||
|
@ -105,7 +105,7 @@ class EF_0348_COUNT(LinFixedEF):
|
|||
def __init__(self, fid='6f23', name='EF.0348_COUNT', desc='TS 03.48 OTA Counters'):
|
||||
super().__init__(fid, name=name, desc=desc, rec_len=(7, 7))
|
||||
|
||||
def _decode_record_bin(self, raw_bin_data):
|
||||
def _decode_record_bin(self, raw_bin_data, **kwargs):
|
||||
u = unpack('!BB5s', raw_bin_data)
|
||||
return {'sec_domain': u[0], 'key_set_version': u[1], 'counter': u[2]}
|
||||
|
||||
|
@ -120,7 +120,7 @@ class EF_GP_COUNT(LinFixedEF):
|
|||
def __init__(self, fid='6f26', name='EF.GP_COUNT', desc='GP SCP02 Counters'):
|
||||
super().__init__(fid, name=name, desc=desc, rec_len=(5, 5))
|
||||
|
||||
def _decode_record_bin(self, raw_bin_data):
|
||||
def _decode_record_bin(self, raw_bin_data, **kwargs):
|
||||
u = unpack('!BBHB', raw_bin_data)
|
||||
return {'sec_domain': u[0], 'key_set_version': u[1], 'counter': u[2], 'rfu': u[3]}
|
||||
|
||||
|
@ -129,7 +129,7 @@ class EF_GP_DIV_DATA(LinFixedEF):
|
|||
def __init__(self, fid='6f27', name='EF.GP_DIV_DATA', desc='GP SCP02 key diversification data'):
|
||||
super().__init__(fid, name=name, desc=desc, rec_len=(12, 12))
|
||||
|
||||
def _decode_record_bin(self, raw_bin_data):
|
||||
def _decode_record_bin(self, raw_bin_data, **kwargs):
|
||||
u = unpack('!BB8s', raw_bin_data)
|
||||
return {'sec_domain': u[0], 'key_set_version': u[1], 'key_div_data': u[2].hex()}
|
||||
|
||||
|
|
|
@ -608,13 +608,13 @@ class EF_PL(TransRecEF):
|
|||
super().__init__(fid, sfid=sfid, name=name,
|
||||
desc=desc, rec_len=2, size=(2, None))
|
||||
|
||||
def _decode_record_bin(self, bin_data):
|
||||
def _decode_record_bin(self, bin_data, **kwargs):
|
||||
if bin_data == b'\xff\xff':
|
||||
return None
|
||||
else:
|
||||
return bin_data.decode('ascii')
|
||||
|
||||
def _encode_record_bin(self, in_json):
|
||||
def _encode_record_bin(self, in_json, **kwargs):
|
||||
if in_json is None:
|
||||
return b'\xff\xff'
|
||||
else:
|
||||
|
@ -665,7 +665,7 @@ class EF_ARR(LinFixedEF):
|
|||
raise ValueError
|
||||
return by_mode
|
||||
|
||||
def _decode_record_bin(self, raw_bin_data):
|
||||
def _decode_record_bin(self, raw_bin_data, **kwargs):
|
||||
# we can only guess if we should decode for EF or DF here :(
|
||||
arr_seq = DataObjectSequence('arr', sequence=[AM_DO_EF, SC_DO])
|
||||
dec = arr_seq.decode_multi(raw_bin_data)
|
||||
|
@ -673,7 +673,7 @@ class EF_ARR(LinFixedEF):
|
|||
# 'un-flattening' decoder, and hence would be unable to encode :(
|
||||
return dec[0]
|
||||
|
||||
def _encode_record_bin(self, in_json):
|
||||
def _encode_record_bin(self, in_json, **kwargs):
|
||||
# we can only guess if we should decode for EF or DF here :(
|
||||
arr_seq = DataObjectSequence('arr', sequence=[AM_DO_EF, SC_DO])
|
||||
return arr_seq.encode_multi(in_json)
|
||||
|
|
|
@ -515,14 +515,14 @@ class EF_LI(TransRecEF):
|
|||
desc='Language Indication'):
|
||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
|
||||
|
||||
def _decode_record_bin(self, in_bin):
|
||||
def _decode_record_bin(self, in_bin, **kwargs):
|
||||
if in_bin == b'\xff\xff':
|
||||
return None
|
||||
else:
|
||||
# officially this is 7-bit GSM alphabet with one padding bit in each byte
|
||||
return in_bin.decode('ascii')
|
||||
|
||||
def _encode_record_bin(self, in_json):
|
||||
def _encode_record_bin(self, in_json, **kwargs):
|
||||
if in_json == None:
|
||||
return b'\xff\xff'
|
||||
else:
|
||||
|
@ -604,7 +604,7 @@ class EF_ECC(LinFixedEF):
|
|||
desc='Emergency Call Codes'):
|
||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=(4, 20))
|
||||
|
||||
def _decode_record_bin(self, in_bin):
|
||||
def _decode_record_bin(self, in_bin, **kwargs):
|
||||
# mandatory parts
|
||||
code = in_bin[:3]
|
||||
if code == b'\xff\xff\xff':
|
||||
|
@ -618,7 +618,7 @@ class EF_ECC(LinFixedEF):
|
|||
ret['alpha_id'] = parse_construct(EF_ECC.alpha_construct, alpha_id)
|
||||
return ret
|
||||
|
||||
def _encode_record_bin(self, in_json):
|
||||
def _encode_record_bin(self, in_json, **kwargs):
|
||||
if in_json is None:
|
||||
return b'\xff\xff\xff\xff'
|
||||
code = EF_ECC.cc_construct.build(in_json['call_code'])
|
||||
|
@ -753,7 +753,7 @@ class EF_RPLMNAcT(TransRecEF):
|
|||
def __init__(self, fid='6f65', sfid=None, name='EF.RPLMNAcTD', size=(2, 4), rec_len=2,
|
||||
desc='RPLMN Last used Access Technology', **kwargs):
|
||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs)
|
||||
def _decode_record_hex(self, in_hex):
|
||||
def _decode_record_hex(self, in_hex, **kwargs):
|
||||
return dec_act(in_hex)
|
||||
# TODO: Encode
|
||||
|
||||
|
|
|
@ -142,11 +142,11 @@ class EF_PCSCF(LinFixedEF):
|
|||
def __init__(self, fid='6f09', sfid=None, name='EF.P-CSCF', desc='P-CSCF Address', **kwargs):
|
||||
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs)
|
||||
|
||||
def _decode_record_hex(self, raw_hex):
|
||||
def _decode_record_hex(self, raw_hex, **kwargs):
|
||||
addr, addr_type = dec_addr_tlv(raw_hex)
|
||||
return {"addr": addr, "addr_type": addr_type}
|
||||
|
||||
def _encode_record_hex(self, json_in):
|
||||
def _encode_record_hex(self, json_in, **kwargs):
|
||||
addr = json_in['addr']
|
||||
addr_type = json_in['addr_type']
|
||||
return enc_addr_tlv(addr, addr_type)
|
||||
|
|
|
@ -356,7 +356,7 @@ class EF_SMS(LinFixedEF):
|
|||
def __init__(self, fid='6f3c', sfid=None, name='EF.SMS', desc='Short messages', **kwargs):
|
||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=(176, 176), **kwargs)
|
||||
|
||||
def _decode_record_bin(self, raw_bin_data):
|
||||
def _decode_record_bin(self, raw_bin_data, **kwargs):
|
||||
def decode_status(status):
|
||||
if status & 0x01 == 0x00:
|
||||
return (None, 'free_space')
|
||||
|
@ -387,10 +387,10 @@ class EF_MSISDN(LinFixedEF):
|
|||
def __init__(self, fid='6f40', sfid=None, name='EF.MSISDN', desc='MSISDN', **kwargs):
|
||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=(15, 34), **kwargs)
|
||||
|
||||
def _decode_record_hex(self, raw_hex_data):
|
||||
def _decode_record_hex(self, raw_hex_data, **kwargs):
|
||||
return {'msisdn': dec_msisdn(raw_hex_data)}
|
||||
|
||||
def _encode_record_hex(self, abstract):
|
||||
def _encode_record_hex(self, abstract, **kwargs):
|
||||
msisdn = abstract['msisdn']
|
||||
if type(msisdn) == str:
|
||||
encoded_msisdn = enc_msisdn(msisdn)
|
||||
|
@ -516,10 +516,10 @@ class EF_LP(TransRecEF):
|
|||
desc='Language Preference'):
|
||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len)
|
||||
|
||||
def _decode_record_bin(self, in_bin):
|
||||
def _decode_record_bin(self, in_bin, **kwargs):
|
||||
return b2h(in_bin)
|
||||
|
||||
def _encode_record_bin(self, in_json):
|
||||
def _encode_record_bin(self, in_json, **kwargs):
|
||||
return h2b(in_json)
|
||||
|
||||
# TS 51.011 Section 10.3.2
|
||||
|
@ -566,13 +566,13 @@ class EF_PLMNsel(TransRecEF):
|
|||
size=(24, None), rec_len=3, **kwargs):
|
||||
super().__init__(fid, name=name, sfid=sfid, desc=desc, size=size, rec_len=rec_len, **kwargs)
|
||||
|
||||
def _decode_record_hex(self, in_hex):
|
||||
def _decode_record_hex(self, in_hex, **kwargs):
|
||||
if in_hex[:6] == "ffffff":
|
||||
return None
|
||||
else:
|
||||
return dec_plmn(in_hex)
|
||||
|
||||
def _encode_record_hex(self, in_json):
|
||||
def _encode_record_hex(self, in_json, **kwargs):
|
||||
if in_json == None:
|
||||
return "ffffff"
|
||||
else:
|
||||
|
@ -772,7 +772,7 @@ class EF_CNL(TransRecEF):
|
|||
desc='Co-operative Network List', **kwargs):
|
||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs)
|
||||
|
||||
def _decode_record_hex(self, in_hex):
|
||||
def _decode_record_hex(self, in_hex, **kwargs):
|
||||
(in_plmn, sub, svp, corp) = unpack('!3sBBB', h2b(in_hex))
|
||||
res = dec_plmn(b2h(in_plmn))
|
||||
res['network_subset'] = sub
|
||||
|
@ -780,7 +780,7 @@ class EF_CNL(TransRecEF):
|
|||
res['corporate_id'] = corp
|
||||
return res
|
||||
|
||||
def _encode_record_hex(self, in_json):
|
||||
def _encode_record_hex(self, in_json, **kwargs):
|
||||
plmn = enc_plmn(in_json['mcc'], in_json['mnc'])
|
||||
return b2h(pack('!3sBBB',
|
||||
h2b(plmn),
|
||||
|
@ -815,13 +815,13 @@ class EF_xPLMNwAcT(TransRecEF):
|
|||
def __init__(self, fid, sfid=None, name=None, desc=None, size=(40, None), rec_len=5, **kwargs):
|
||||
super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs)
|
||||
|
||||
def _decode_record_hex(self, in_hex):
|
||||
def _decode_record_hex(self, in_hex, **kwargs):
|
||||
if in_hex[:6] == "ffffff":
|
||||
return None
|
||||
else:
|
||||
return dec_xplmn_w_act(in_hex)
|
||||
|
||||
def _encode_record_hex(self, in_json):
|
||||
def _encode_record_hex(self, in_json, **kwargs):
|
||||
if in_json == None:
|
||||
return "ffffff0000"
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue