laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/38195?usp=email )
Change subject: filesystem: pass total_len to construct of when encoding file contents ......................................................................
filesystem: pass total_len to construct of when encoding file contents
In our construct models we frequently use a context parameter "total_len", we also pass this parameter to construct when we decode files, but we do not pass it when we generate files. This is a problem, because when total_len is used in the construct model, this parameter must be known also when decoding the file.
Let's make sure that the total_len is properly determined and and passed to construct (via pyosmocom).
Related: OS#5714 Change-Id: I1b7a51594fbc5d9fe01132c39354a2fa88d53f9b --- M pySim/filesystem.py M pySim/gsm_r.py M pySim/runtime.py M pySim/sysmocom_sja2.py M pySim/ts_31_102.py M pySim/ts_31_102_telecom.py M pySim/ts_51_011.py M tests/pySim-shell_test/file_content/test_record_uicc.ok M tests/unittests/test_files.py 9 files changed, 348 insertions(+), 41 deletions(-)
Approvals: laforge: Looks good to me, approved Jenkins Builder: Verified
diff --git a/pySim/filesystem.py b/pySim/filesystem.py index c55bcbe..6e8852a 100644 --- a/pySim/filesystem.py +++ b/pySim/filesystem.py @@ -743,7 +743,26 @@ return t.to_dict() return {'raw': raw_bin_data.hex()}
- def encode_bin(self, abstract_data: dict) -> bytearray: + def __get_size(self, total_len: Optional[int] = None) -> Optional[int]: + """Get the size (total length) of the file""" + + # Caller has provided the actual total length of the file, this should be the default case + if total_len is not None: + return total_len + + if self.size is None: + return None + + # Alternatively use the recommended size from the specification + if self.size[1] is not None: + return self.size[1] + # In case no recommended size is specified, use the minimum size + if self.size[0] is not None: + return self.size[0] + + return None + + def encode_bin(self, abstract_data: dict, total_len: Optional[int] = None) -> bytearray: """Encode abstract representation into raw (binary) data.
A derived class would typically provide an _encode_bin() or _encode_hex() method @@ -752,17 +771,18 @@
Args: abstract_data : dict representing the decoded data + total_len : expected total length of the encoded data (file size) Returns: binary encoded data """ method = getattr(self, '_encode_bin', None) if callable(method): - return method(abstract_data) + return method(abstract_data, total_len = self.__get_size(total_len)) method = getattr(self, '_encode_hex', None) if callable(method): - return h2b(method(abstract_data)) + return h2b(method(abstract_data, total_len = self.__get_size(total_len))) if self._construct: - return build_construct(self._construct, abstract_data) + return build_construct(self._construct, abstract_data, {'total_len' : self.__get_size(total_len)}) if self._tlv: t = self._tlv() if inspect.isclass(self._tlv) else self._tlv t.from_dict(abstract_data) @@ -770,7 +790,7 @@ raise NotImplementedError( "%s encoder not yet implemented. Patches welcome." % self)
- def encode_hex(self, abstract_data: dict) -> str: + def encode_hex(self, abstract_data: dict, total_len: Optional[int] = None) -> str: """Encode abstract representation into raw (hex string) data.
A derived class would typically provide an _encode_bin() or _encode_hex() method @@ -779,18 +799,19 @@
Args: abstract_data : dict representing the decoded data + total_len : expected total length of the encoded data (file size) Returns: hex string encoded data """ method = getattr(self, '_encode_hex', None) if callable(method): - return method(abstract_data) + return method(abstract_data, total_len = self.__get_size(total_len)) method = getattr(self, '_encode_bin', None) if callable(method): - raw_bin_data = method(abstract_data) + raw_bin_data = method(abstract_data, total_len = self.__get_size(total_len)) return b2h(raw_bin_data) if self._construct: - return b2h(build_construct(self._construct, abstract_data)) + return b2h(build_construct(self._construct, abstract_data, {'total_len':self.__get_size(total_len)})) if self._tlv: t = self._tlv() if inspect.isclass(self._tlv) else self._tlv t.from_dict(abstract_data) @@ -1030,7 +1051,26 @@ return t.to_dict() return {'raw': raw_hex_data}
- def encode_record_hex(self, abstract_data: dict, record_nr: int) -> str: + def __get_rec_len(self, total_len: Optional[int] = None) -> Optional[int]: + """Get the length (total length) of the file record""" + + # Caller has provided the actual total length of the record, this should be the default case + if total_len is not None: + return total_len + + if self.rec_len is None: + return None + + # Alternatively use the recommended length from the specification + if self.rec_len[1] is not None: + return self.rec_len[1] + # In case no recommended length is specified, use the minimum length + if self.rec_len[0] is not None: + return self.rec_len[0] + + return None + + def encode_record_hex(self, abstract_data: dict, record_nr: int, total_len: Optional[int] = None) -> str: """Encode abstract representation into raw (hex string) data.
A derived class would typically provide an _encode_record_bin() or _encode_record_hex() @@ -1040,18 +1080,19 @@ Args: abstract_data : dict representing the decoded data record_nr : record number (1 for first record, ...) + total_len : expected total length of the encoded data (record length) Returns: hex string encoded data """ method = getattr(self, '_encode_record_hex', None) if callable(method): - return method(abstract_data, record_nr=record_nr) + return method(abstract_data, record_nr=record_nr, total_len = self.__get_rec_len(total_len)) method = getattr(self, '_encode_record_bin', None) if callable(method): - raw_bin_data = method(abstract_data, record_nr=record_nr) + raw_bin_data = method(abstract_data, record_nr=record_nr, total_len = self.__get_rec_len(total_len)) return b2h(raw_bin_data) if self._construct: - return b2h(build_construct(self._construct, abstract_data)) + return b2h(build_construct(self._construct, abstract_data, {'total_len':self.__get_rec_len(total_len)})) if self._tlv: t = self._tlv() if inspect.isclass(self._tlv) else self._tlv t.from_dict(abstract_data) @@ -1059,7 +1100,7 @@ raise NotImplementedError( "%s encoder not yet implemented. Patches welcome." % self)
- def encode_record_bin(self, abstract_data: dict, record_nr : int) -> bytearray: + def encode_record_bin(self, abstract_data: dict, record_nr : int, total_len: Optional[int] = None) -> bytearray: """Encode abstract representation into raw (binary) data.
A derived class would typically provide an _encode_record_bin() or _encode_record_hex() @@ -1069,17 +1110,18 @@ Args: abstract_data : dict representing the decoded data record_nr : record number (1 for first record, ...) + total_len : expected total length of the encoded data (record length) Returns: binary encoded data """ method = getattr(self, '_encode_record_bin', None) if callable(method): - return method(abstract_data, record_nr=record_nr) + return method(abstract_data, record_nr=record_nr, total_len = self.__get_rec_len(total_len)) method = getattr(self, '_encode_record_hex', None) if callable(method): - return h2b(method(abstract_data, record_nr=record_nr)) + return h2b(method(abstract_data, record_nr=record_nr, total_len = self.__get_rec_len(total_len))) if self._construct: - return build_construct(self._construct, abstract_data) + return build_construct(self._construct, abstract_data, {'total_len':self.__get_rec_len(total_len)}) if self._tlv: t = self._tlv() if inspect.isclass(self._tlv) else self._tlv t.from_dict(abstract_data) @@ -1224,7 +1266,20 @@ return t.to_dict() return {'raw': raw_hex_data}
- def encode_record_hex(self, abstract_data: dict) -> str: + def __get_rec_len(self, total_len: Optional[int] = None) -> Optional[int]: + """Get the length (total length) of the file record""" + + # Caller has provided the actual total length of the record, this should be the default case + if total_len is not None: + return total_len + + # Alternatively use the record length from the specification + if self.rec_len: + return self.rec_len + + return None + + def encode_record_hex(self, abstract_data: dict, total_len: Optional[int] = None) -> str: """Encode abstract representation into raw (hex string) data.
A derived class would typically provide an _encode_record_bin() or _encode_record_hex() @@ -1233,17 +1288,19 @@
Args: abstract_data : dict representing the decoded data + total_len : expected total length of the encoded data (record length) Returns: hex string encoded data """ method = getattr(self, '_encode_record_hex', None) if callable(method): - return method(abstract_data) + return method(abstract_data, total_len = self.__get_rec_len(total_len)) method = getattr(self, '_encode_record_bin', None) if callable(method): - return b2h(method(abstract_data)) + return b2h(method(abstract_data, total_len = self.__get_rec_len(total_len))) if self._construct: - return b2h(filter_dict(build_construct(self._construct, abstract_data))) + return b2h(filter_dict(build_construct(self._construct, abstract_data, + {'total_len':self.__get_rec_len(total_len)}))) if self._tlv: t = self._tlv() if inspect.isclass(self._tlv) else self._tlv t.from_dict(abstract_data) @@ -1251,7 +1308,7 @@ 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, total_len: Optional[int] = None) -> bytearray: """Encode abstract representation into raw (binary) data.
A derived class would typically provide an _encode_record_bin() or _encode_record_hex() @@ -1260,17 +1317,19 @@
Args: abstract_data : dict representing the decoded data + total_len : expected total length of the encoded data (record length) Returns: binary encoded data """ method = getattr(self, '_encode_record_bin', None) if callable(method): - return method(abstract_data) + return method(abstract_data, total_len = self.__get_rec_len(total_len)) method = getattr(self, '_encode_record_hex', None) if callable(method): - return h2b(method(abstract_data)) + return h2b(method(abstract_data, total_len = self.__get_rec_len(total_len))) if self._construct: - return filter_dict(build_construct(self._construct, abstract_data)) + return filter_dict(build_construct(self._construct, abstract_data, + {'total_len':self.__get_rec_len(total_len)})) if self._tlv: t = self._tlv() if inspect.isclass(self._tlv) else self._tlv t.from_dict(abstract_data) @@ -1283,8 +1342,8 @@ for i in range(0, len(raw_bin_data), self.rec_len)] return [self.decode_record_bin(x) for x in chunks]
- def _encode_bin(self, abstract_data) -> bytes: - chunks = [self.encode_record_bin(x) for x in abstract_data] + def _encode_bin(self, abstract_data, **kwargs) -> bytes: + chunks = [self.encode_record_bin(x, total_len = kwargs.get('total_len', None)) for x in abstract_data] # FIXME: pad to file size return b''.join(chunks)
diff --git a/pySim/gsm_r.py b/pySim/gsm_r.py index b29fbc4..bc54c72 100644 --- a/pySim/gsm_r.py +++ b/pySim/gsm_r.py @@ -290,7 +290,7 @@ else: return parse_construct(self.construct_others, raw_bin_data)
- def _encode_record_bin(self, abstract_data : dict, record_nr : int) -> bytearray: + def _encode_record_bin(self, abstract_data : dict, record_nr : int, **kwargs) -> bytearray: r = None if record_nr == 1: r = self.construct_first.build(abstract_data) diff --git a/pySim/runtime.py b/pySim/runtime.py index 3714832..0ee15d0 100644 --- a/pySim/runtime.py +++ b/pySim/runtime.py @@ -524,7 +524,7 @@ Args: data : abstract data which is to be encoded and written """ - data_hex = self.selected_file.encode_hex(data) + data_hex = self.selected_file.encode_hex(data, self.selected_file_size()) return self.update_binary(data_hex)
def read_record(self, rec_nr: int = 0): @@ -574,7 +574,7 @@ rec_nr : Record number to read data_hex : Abstract data to be written """ - data_hex = self.selected_file.encode_record_hex(data, rec_nr) + data_hex = self.selected_file.encode_record_hex(data, rec_nr, self.selected_file_record_len()) return self.update_record(rec_nr, data_hex)
def retrieve_data(self, tag: int = 0): diff --git a/pySim/sysmocom_sja2.py b/pySim/sysmocom_sja2.py index 2c6d66a..bef955a 100644 --- a/pySim/sysmocom_sja2.py +++ b/pySim/sysmocom_sja2.py @@ -252,7 +252,7 @@ else: return parse_construct(self._construct, raw_bin_data)
- def _encode_bin(self, abstract_data: dict) -> bytearray: + def _encode_bin(self, abstract_data: dict, **kwargs) -> bytearray: if abstract_data['cfg']['algorithm'] == 'tuak': return build_construct(self._constr_tuak, abstract_data) else: diff --git a/pySim/ts_31_102.py b/pySim/ts_31_102.py index e27d2db..ab5ee54 100644 --- a/pySim/ts_31_102.py +++ b/pySim/ts_31_102.py @@ -344,7 +344,7 @@ out.append({k: v}) return out
- def _encode_hex(self, in_json): + def _encode_hex(self, in_json, **kwargs): out_bytes = self._encode_prot_scheme_id_list( in_json['prot_scheme_id_list']) d = self._expand_pubkey_list(in_json['hnet_pubkey_list']) @@ -396,8 +396,8 @@ 'hnet_pubkey_list': hnet_pubkey_list }
- def _encode_bin(self, in_json): - return h2b(self._encode_hex(in_json)) + def _encode_bin(self, in_json, **kwargs): + return h2b(self._encode_hex(in_json, **kwargs))
class EF_LI(TransRecEF): diff --git a/pySim/ts_31_102_telecom.py b/pySim/ts_31_102_telecom.py index 93154bb..a949070 100644 --- a/pySim/ts_31_102_telecom.py +++ b/pySim/ts_31_102_telecom.py @@ -59,7 +59,7 @@ ret[service_nr]['description'] = self.table[service_nr] return ret
- def _encode_bin(self, in_json): + def _encode_bin(self, in_json, **kwargs): # compute the required binary size bin_len = 0 for srv in in_json.keys(): diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py index 26dce8b..1070b42 100644 --- a/pySim/ts_51_011.py +++ b/pySim/ts_51_011.py @@ -358,7 +358,7 @@ def _decode_hex(self, raw_hex): return {'imsi': dec_imsi(raw_hex)}
- def _encode_hex(self, abstract): + def _encode_hex(self, abstract, **kwargs): return enc_imsi(abstract['imsi'])
@with_default_category('File-Specific Commands') @@ -446,7 +446,7 @@ } return ret
- def _encode_bin(self, in_json): + def _encode_bin(self, in_json, **kwargs): # compute the required binary size bin_len = 0 for srv in in_json.keys(): @@ -969,7 +969,7 @@ def _decode_hex(self, raw_hex): return {'iccid': dec_iccid(raw_hex)}
- def _encode_hex(self, abstract): + def _encode_hex(self, abstract, **kwargs): return enc_iccid(abstract['iccid'])
# TS 102 221 Section 13.3 / TS 31.101 Secction 13 / TS 51.011 Section 10.1.2 diff --git a/tests/pySim-shell_test/file_content/test_record_uicc.ok b/tests/pySim-shell_test/file_content/test_record_uicc.ok index 268740e..14183eb 100644 --- a/tests/pySim-shell_test/file_content/test_record_uicc.ok +++ b/tests/pySim-shell_test/file_content/test_record_uicc.ok @@ -12,7 +12,7 @@ 010 0123456789abcdef0123456789abcdef0123456789abcdef0123456789 [ { - "alpha_id": null, + "alpha_id": "", "len_of_bcd": 7, "ton_npi": { "ext": true, @@ -60,7 +60,7 @@ "ext4_record_id": 255 }, { - "alpha_id": null, + "alpha_id": "", "len_of_bcd": 7, "ton_npi": { "ext": true, @@ -120,7 +120,7 @@ "ext4_record_id": 255 }, { - "alpha_id": null, + "alpha_id": "", "len_of_bcd": 7, "ton_npi": { "ext": true, @@ -133,7 +133,7 @@ } ] { - "alpha_id": null, + "alpha_id": "", "len_of_bcd": 7, "ton_npi": { "ext": true, diff --git a/tests/unittests/test_files.py b/tests/unittests/test_files.py index 2bd3aea..a3ccf06 100755 --- a/tests/unittests/test_files.py +++ b/tests/unittests/test_files.py @@ -30,6 +30,9 @@ import pySim.gsm_r import pySim.cdma_ruim
+from construct import Int8ub, Struct, Padding, this +from osmocom.tlv import BER_TLV_IE + def get_qualified_name(c): """return the qualified (by module) name of a class.""" return "%s.%s" % (c.__module__, c.__name__) @@ -288,6 +291,251 @@ re_dec = inst.decode_hex(encoded) self.assertEqual(decoded, re_dec)
+ +class filesystem_enc_dec_test(unittest.TestCase): + """ The following set of tests is to verify the code paths in filesystem.py. There are several methods to encode + or decode a file. Depending on which methods (encode_hex, decode_hex, etc.) or structs (_construct, _tlv) are + define in the related file object, the encoding/decoding will take a different code path. In this test we will + try out all of the different encoding/decoding variants by defining one test file for each variant. Then we will + run an encoding/decoding cycle on each of the test files. + + The test files will also include a padding that is dependent on the total_len keyword argument that is passed + via the construct context or via **kwargs in case the hand written encoding methods (encode_hex, encode_record_hex, + etc.) are used. This will ensure that total_len is passed correctly in all possible variants. + """ + + def test_encode_TransparentEF(self): + + class TransparentEF_construct(TransparentEF): + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', size=(2, 2), + desc='dummy TransparentEF file to test encoding/decoding via _construct'): + super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size) + self._construct = Struct('test'/Int8ub, Padding(this._.total_len-1)) + + class TransparentEF_encode_hex(TransparentEF): + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', size=(2, 2), + desc='dummy TransparentEF file to test manual encoding/decoding via _encode/decode_hex'): + super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size) + def _encode_hex(self, in_json, **kwargs): + return "%02x" % in_json['test'] + "00" * (kwargs.get('total_len') -1) + def _decode_hex(self, raw_hex): + return {'test': int(raw_hex[0:2],16)} + + class TransparentEF_encode_bin(TransparentEF): + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', size=(2, 2), + desc='dummy TransparentEF file to test manual encoding/decoding via _encode/decode_bin'): + super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size) + def _encode_bin(self, in_json, **kwargs): + return h2b("%02x" % in_json['test'] + "00" * (kwargs.get('total_len') -1)) + def _decode_bin(self, raw_bin_data: bytearray): + return {'test': int(b2h(raw_bin_data[0:1]),16)} + + class TransparentEF_tlv(TransparentEF): + class TestTlv(BER_TLV_IE, tag=0x81): + _construct = Int8ub + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', size=(1, 1), + desc='dummy TransparentEF file to test encoding/decoding via _tlv'): + super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size) + self._tlv = TransparentEF_tlv.TestTlv + + class TransparentEF_raw(TransparentEF): + class TestTlv(BER_TLV_IE, tag=0x81): + _construct = Int8ub + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', size=(1, 1), + desc='dummy TransparentEF file to test raw encoding/decoding'): + super().__init__(fid, sfid=sfid, name=name, desc=desc, size=size) + + def do_encdec_test(file): + res = file.encode_hex({'test':0x41}) + self.assertEqual(res,hexstr("4100")) + res = file.encode_bin({'test':0x41}) + self.assertEqual(b2h(res),hexstr("4100")) + res = file.encode_hex({'test':0x41}, total_len=3) + self.assertEqual(res,hexstr("410000")) + res = file.encode_bin({'test':0x41}, total_len=3) + self.assertEqual(b2h(res),hexstr("410000")) + res = file.decode_hex("4100") + self.assertEqual(res,{'test':0x41}) + res = file.decode_bin(b'\x41\x01') + self.assertEqual(res,{'test':0x41}) + + def do_encdec_test_tlv(file): + res = file.encode_hex({'test_tlv':0x41}) + self.assertEqual(res,hexstr("810141")) + res = file.encode_bin({'test_tlv':0x41}) + self.assertEqual(b2h(res),hexstr("810141")) + res = file.decode_hex(hexstr("810141")) + self.assertEqual(res,{'test_tlv':0x41}) + res = file.decode_bin(h2b("810141")) + self.assertEqual(res,{'test_tlv':0x41}) + + def do_encdec_test_raw(file): + res = file.decode_hex("41") + self.assertEqual(res,{'raw':'41'}) + res = file.decode_bin(b'\x41') + self.assertEqual(res,{'raw':'41'}) + + do_encdec_test(TransparentEF_construct()) + do_encdec_test(TransparentEF_encode_hex()) + do_encdec_test(TransparentEF_encode_bin()) + do_encdec_test_tlv(TransparentEF_tlv()) + do_encdec_test_raw(TransparentEF_raw()) + + def test_encode_LinFixedEF(self): + + class LinFixedEF_construct(LinFixedEF): + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', + desc='dummy LinFixedEF file to test encoding/decoding via _construct', **kwargs): + super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=(2, 2), **kwargs) + self._construct = Struct('test'/Int8ub, Padding(this._.total_len-1)) + + class LinFixedEF_encode_hex(LinFixedEF): + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', + desc='dummy LinFixedEF file to test manual encoding/decoding via _encode/decode_hex', **kwargs): + super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=(2, 2), **kwargs) + def _encode_record_hex(self, in_json, **kwargs): + return "%02x" % in_json['test'] + "00" * (kwargs.get('total_len') -1) + def _decode_record_hex(self, in_hex, **kwargs): + return {'test': int(in_hex[0:2],16)} + + class LinFixedEF_encode_bin(LinFixedEF): + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', + desc='dummy LinFixedEF file to test manual encoding/decoding via _encode/decode_bin', **kwargs): + super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=(2, 2), **kwargs) + def _encode_record_bin(self, in_json, **kwargs): + return h2b("%02x" % in_json['test'] + "00" * (kwargs.get('total_len') -1)) + def _decode_record_bin(self, in_bin, **kwargs): + return {'test': int(b2h(in_bin[0:1]),16)} + + class LinFixedEF_tlv(LinFixedEF): + class TestTlv(BER_TLV_IE, tag=0x81): + _construct = Int8ub + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', + desc='dummy LinFixedEF file to test encoding/decoding via _tlv', **kwargs): + super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=(1, 1), **kwargs) + self._tlv = LinFixedEF_tlv.TestTlv + + class LinFixedEF_raw(LinFixedEF): + class TestTlv(BER_TLV_IE, tag=0x81): + _construct = Int8ub + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', + desc='dummy LinFixedEF file to test raw encoding/decoding', **kwargs): + super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, rec_len=(1, 1), **kwargs) + + def do_encdec_test(file): + res = file.encode_record_hex({'test':0x41}, 1) + self.assertEqual(res,hexstr("4100")) + res = file.encode_record_bin({'test':0x41}, 1) + self.assertEqual(b2h(res),hexstr("4100")) + res = file.encode_record_hex({'test':0x41}, 1, total_len=3) + self.assertEqual(res,hexstr("410000")) + res = file.encode_record_bin({'test':0x41}, 1, total_len=3) + self.assertEqual(b2h(res),hexstr("410000")) + res = file.decode_record_hex("4100", 1) + self.assertEqual(res,{'test':0x41}) + res = file.decode_record_bin(b'\x41\x00', 1) + self.assertEqual(res,{'test':0x41}) + + def do_encdec_test_tlv(file): + res = file.encode_record_hex({'test_tlv':0x41}, 1) + self.assertEqual(res,hexstr("810141")) + res = file.encode_record_bin({'test_tlv':0x41}, 1) + self.assertEqual(b2h(res),hexstr("810141")) + res = file.decode_record_hex(hexstr("810141"), 1) + self.assertEqual(res,{'test_tlv':0x41}) + res = file.decode_record_bin(h2b("810141"), 1) + self.assertEqual(res,{'test_tlv':0x41}) + + def do_encdec_test_raw(file): + res = file.decode_record_hex("41", 1) + self.assertEqual(res,{'raw':'41'}) + res = file.decode_record_bin(b'\x41', 1) + self.assertEqual(res,{'raw':'41'}) + + do_encdec_test(LinFixedEF_construct()) + do_encdec_test(LinFixedEF_encode_hex()) + do_encdec_test(LinFixedEF_encode_bin()) + do_encdec_test_tlv(LinFixedEF_tlv()) + do_encdec_test_raw(LinFixedEF_raw()) + + def test_encode_TransRecEF(self): + + class TransRecEF_construct(TransRecEF): + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', size=(2, 2), rec_len=2, + desc='dummy TransRecEF file to test encoding/decoding via _construct', **kwargs): + super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs) + self._construct = Struct('test'/Int8ub, Padding(this._.total_len-1)) + + class TransRecEF_encode_hex(TransRecEF): + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', size=(2, 2), rec_len=2, + desc='dummy TransRecEF file to test manual encoding/decoding via _encode/decode_hex', **kwargs): + super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs) + def _encode_record_hex(self, in_json, **kwargs): + return "%02x" % in_json['test'] + "00" * (kwargs.get('total_len') -1) + def _decode_record_hex(self, in_hex, **kwargs): + return {'test': int(in_hex[0:2],16)} + + class TransRecEF_encode_bin(TransRecEF): + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', size=(2, 2), rec_len=2, + desc='dummy TransRecEF file to test manual encoding/decoding via _encode/decode_bin', **kwargs): + super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs) + def _encode_record_bin(self, in_json, **kwargs): + return h2b("%02x" % in_json['test'] + "00" * (kwargs.get('total_len') -1)) + def _decode_record_bin(self, in_bin, **kwargs): + return {'test': int(b2h(in_bin[0:1]),16)} + + class TransRecEF_tlv(TransRecEF): + class TestTlv(BER_TLV_IE, tag=0x81): + _construct = Int8ub + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', size=(1, 1), rec_len=1, + desc='dummy TransRecEF file to test encoding/decoding via _tlv', **kwargs): + super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs) + self._tlv = TransRecEF_tlv.TestTlv + + class TransRecEF_raw(TransRecEF): + class TestTlv(BER_TLV_IE, tag=0x81): + _construct = Int8ub + def __init__(self, fid='0000', sfid=None, name='EF.DUMMY', size=(1, 1), rec_len=1, + desc='dummy TransRecEF file to test raw encoding/decoding', **kwargs): + super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, rec_len=rec_len, **kwargs) + + def do_encdec_test(file): + res = file.encode_record_hex({'test':0x41}) + self.assertEqual(res,hexstr("4100")) + res = file.encode_record_bin({'test':0x41}) + self.assertEqual(b2h(res),hexstr("4100")) + res = file.encode_record_hex({'test':0x41}, total_len=3) + self.assertEqual(res,hexstr("410000")) + res = file.encode_record_bin({'test':0x41}, total_len=3) + self.assertEqual(b2h(res),hexstr("410000")) + res = file.decode_record_hex("4100") + self.assertEqual(res,{'test':0x41}) + res = file.decode_record_bin(b'\x41\x00') + self.assertEqual(res,{'test':0x41}) + + def do_encdec_test_tlv(file): + res = file.encode_record_hex({'test_tlv':0x41}) + self.assertEqual(res,hexstr("810141")) + res = file.encode_record_bin({'test_tlv':0x41}) + self.assertEqual(b2h(res),hexstr("810141")) + res = file.decode_record_hex(hexstr("810141")) + self.assertEqual(res,{'test_tlv':0x41}) + res = file.decode_record_bin(h2b("810141")) + self.assertEqual(res,{'test_tlv':0x41}) + + def do_encdec_test_raw(file): + res = file.decode_record_hex("41") + self.assertEqual(res,{'raw':'41'}) + res = file.decode_record_bin(b'\x41') + self.assertEqual(res,{'raw':'41'}) + + do_encdec_test(TransRecEF_construct()) + do_encdec_test(TransRecEF_encode_hex()) + do_encdec_test(TransRecEF_encode_bin()) + do_encdec_test_tlv(TransRecEF_tlv()) + do_encdec_test_raw(TransRecEF_raw()) + + if __name__ == '__main__': logger = logging.getLogger() logger.setLevel(logging.DEBUG)