laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/27188 )
Change subject: ts_102_221: Implement File Descriptor using construct ......................................................................
ts_102_221: Implement File Descriptor using construct
This automatically adds encoding support, which is needed for upcoming CREATE FILE support.
Change-Id: Ia40dba4aab6ceb9d81fd170f7efa8dad1f9b43d0 --- M docs/shell.rst M pySim-shell.py M pySim/filesystem.py M pySim/ts_102_221.py M pySim/ts_51_011.py 5 files changed, 50 insertions(+), 43 deletions(-)
Approvals: Jenkins Builder: Verified laforge: Looks good to me, approved fixeria: Looks good to me, but someone else must approve
diff --git a/docs/shell.rst b/docs/shell.rst index 3ab1113..58588a2 100644 --- a/docs/shell.rst +++ b/docs/shell.rst @@ -80,9 +80,11 @@ pySIM-shell (MF)> select ADF.USIM { "file_descriptor": { - "shareable": true, - "file_type": "df", - "structure": "no_info_given" + "file_descriptor_byte": { + "shareable": true, + "file_type": "df", + "structure": "no_info_given" + } }, "df_name": "A0000000871002FFFFFFFF8907090000", "proprietary_info": { diff --git a/pySim-shell.py b/pySim-shell.py index a82d56f..e58151b 100755 --- a/pySim-shell.py +++ b/pySim-shell.py @@ -528,8 +528,7 @@ self._cmd.poutput("# file: %s (%s)" % ( self._cmd.rs.selected_file.name, self._cmd.rs.selected_file.fid))
- fd = fcp_dec['file_descriptor'] - structure = fd['structure'] + structure = self._cmd.rs.selected_file_structure() self._cmd.poutput("# structure: %s" % str(structure))
for f in df_path_list: @@ -545,8 +544,8 @@ self._cmd.poutput("update_binary " + str(result[0])) elif structure == 'cyclic' or structure == 'linear_fixed': # Use number of records specified in select response - if 'num_of_rec' in fd: - num_of_rec = fd['num_of_rec'] + num_of_rec = self._cmd.rs.selected_file_num_of_rec() + if num_of_rec: for r in range(1, num_of_rec + 1): if as_json: result = self._cmd.rs.read_record_dec(r) diff --git a/pySim/filesystem.py b/pySim/filesystem.py index bef9005..a354dfb 100644 --- a/pySim/filesystem.py +++ b/pySim/filesystem.py @@ -773,7 +773,7 @@ @cmd2.with_argparser(read_recs_parser) def do_read_records(self, opts): """Read all records from a record-oriented EF""" - num_of_rec = self._cmd.rs.selected_file_fcp['file_descriptor']['num_of_rec'] + num_of_rec = self._cmd.rs.selected_file_num_of_rec() for recnr in range(1, 1 + num_of_rec): (data, sw) = self._cmd.rs.read_record(recnr) if (len(data) > 0): @@ -789,7 +789,7 @@ @cmd2.with_argparser(read_recs_dec_parser) def do_read_records_decoded(self, opts): """Read + decode all records from a record-oriented EF""" - num_of_rec = self._cmd.rs.selected_file_fcp['file_descriptor']['num_of_rec'] + num_of_rec = self._cmd.rs.selected_file_num_of_rec() # collect all results in list so they are rendered as JSON list when printing data_list = [] for recnr in range(1, 1 + num_of_rec): @@ -1279,6 +1279,21 @@ pass return apps_taken
+ def selected_file_descriptor_byte(self) -> dict: + return self.selected_file_fcp['file_descriptor']['file_descriptor_byte'] + + def selected_file_shareable(self) -> bool: + return self.selected_file_descriptor_byte()['shareable'] + + def selected_file_structure(self) -> str: + return self.selected_file_descriptor_byte()['structure'] + + def selected_file_type(self) -> str: + return self.selected_file_descriptor_byte()['file_type'] + + def selected_file_num_of_rec(self) -> Optional[int]: + return self.selected_file_fcp['file_descriptor'].get('num_of_rec') + def reset(self, cmd_app=None) -> Hexstr: """Perform physical card reset and obtain ATR. Args: @@ -1350,11 +1365,11 @@ raise RuntimeError("%s: %s - %s" % (swm.sw_actual, k[0], k[1]))
select_resp = self.selected_file.decode_select_response(data) - if (select_resp['file_descriptor']['file_type'] == 'df'): + if (select_resp['file_descriptor']['file_descriptor_byte']['file_type'] == 'df'): f = CardDF(fid=fid, sfid=None, name="DF." + str(fid).upper(), desc="dedicated file, manually added at runtime") else: - if (select_resp['file_descriptor']['structure'] == 'transparent'): + if (select_resp['file_descriptor']['file_descriptor_byte']['structure'] == 'transparent'): f = TransparentEF(fid=fid, sfid=None, name="EF." + str(fid).upper(), desc="elementary file, manually added at runtime") else: diff --git a/pySim/ts_102_221.py b/pySim/ts_102_221.py index 82776a0..022a4a7 100644 --- a/pySim/ts_102_221.py +++ b/pySim/ts_102_221.py @@ -18,6 +18,7 @@ """
from construct import * +from construct import Optional as COptional from pySim.construct import * from pySim.utils import * from pySim.filesystem import * @@ -87,34 +88,22 @@
# ETSI TS 102 221 11.1.1.4.3 class FileDescriptor(BER_TLV_IE, tag=0x82): - def _from_bytes(self, in_bin: bytes): - out = {} - ft_dict = { - 0: 'working_ef', - 1: 'internal_ef', - 7: 'df' - } - fs_dict = { - 0: 'no_info_given', - 1: 'transparent', - 2: 'linear_fixed', - 6: 'cyclic', - 0x39: 'ber_tlv', - } - fdb = in_bin[0] - ftype = (fdb >> 3) & 7 - if fdb & 0xbf == 0x39: - fstruct = 0x39 - else: - fstruct = fdb & 7 - out['shareable'] = True if fdb & 0x40 else False - out['file_type'] = ft_dict[ftype] if ftype in ft_dict else ftype - out['structure'] = fs_dict[fstruct] if fstruct in fs_dict else fstruct - if len(in_bin) >= 5: - out['record_len'] = int.from_bytes(in_bin[2:4], 'big') - out['num_of_rec'] = int.from_bytes(in_bin[4:5], 'big') - self.decoded = out - return self.decoded + class BerTlvAdapter(Adapter): + def _parse(self, obj, context, path): + if obj == 0x39: + return 'ber_tlv' + raise ValidationError + def _build(self, obj, context, path): + if obj == 'ber_tlv': + return 0x39 + raise ValidationError + + FDB = Select(BitStruct(Const(0, Bit), 'shareable'/Flag, 'structure'/BerTlvAdapter(Const(0x39, BitsInteger(6)))), + BitStruct(Const(0, Bit), 'shareable'/Flag, 'file_type'/Enum(BitsInteger(3), working_ef=0, internal_ef=1, df=7), + 'structure'/Enum(BitsInteger(3), no_info_given=0, transparent=1, linear_fixed=2, cyclic=6)) + ) + _construct = Struct('file_descriptor_byte'/FDB, Const(b'\x21'), + 'record_len'/COptional(Int16ub), 'num_of_rec'/COptional(Int16ub))
# ETSI TS 102 221 11.1.1.4.4 class FileIdentifier(BER_TLV_IE, tag=0x83): @@ -668,7 +657,7 @@ @cmd2.with_argparser(LinFixedEF.ShellCommands.read_recs_dec_parser) def do_read_arr_records(self, opts): """Read + decode all EF.ARR records in flattened, human-friendly form.""" - num_of_rec = self._cmd.rs.selected_file_fcp['file_descriptor']['num_of_rec'] + num_of_rec = self._cmd.rs.selected_file_num_of_rec() # collect all results in list so they are rendered as JSON list when printing data_list = [] for recnr in range(1, 1 + num_of_rec): diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py index 5e430ea..ddfed95 100644 --- a/pySim/ts_51_011.py +++ b/pySim/ts_51_011.py @@ -1109,7 +1109,9 @@ 4: 'working_ef' } ret = { - 'file_descriptor': {}, + 'file_descriptor': { + 'file_descriptor_byte': {}, + }, 'proprietary_info': {}, } ret['file_id'] = b2h(resp_bin[4:6]) @@ -1117,7 +1119,7 @@ resp_bin[2:4], 'big') file_type = type_of_file_map[resp_bin[6] ] if resp_bin[6] in type_of_file_map else resp_bin[6] - ret['file_descriptor']['file_type'] = file_type + ret['file_descriptor']['file_descriptor_byte']['file_type'] = file_type if file_type in ['mf', 'df']: ret['file_characteristics'] = b2h(resp_bin[13:14]) ret['num_direct_child_df'] = resp_bin[14] @@ -1127,7 +1129,7 @@ elif file_type in ['working_ef']: file_struct = struct_of_file_map[resp_bin[13] ] if resp_bin[13] in struct_of_file_map else resp_bin[13] - ret['file_descriptor']['structure'] = file_struct + ret['file_descriptor']['file_descriptor_byte']['structure'] = file_struct ret['access_conditions'] = b2h(resp_bin[8:10]) if resp_bin[11] & 0x01 == 0: ret['life_cycle_status_int'] = 'operational_activated'