pySim-shell: move export code into filesystem class model

The code that generates the filesystem export lines for the various
different file structures can be moved into the filesystem class model.

This simplifies the code since we do not need any extra logic to
distinguish between the different file structures.

Related: OS#6092
Change-Id: Icc2ee60cfc4379411744ca1033d79a1ee9cff5a6
This commit is contained in:
Philipp Maier
2024-07-24 16:19:46 +02:00
committed by laforge
parent 34dce409b9
commit d29bdbc2c8
2 changed files with 103 additions and 58 deletions

View File

@@ -599,66 +599,14 @@ class PySimCommands(CommandSet):
self._cmd.poutput("# directory: %s (%s)" % (df_path, df_path_fid))
try:
fcp_dec = self._cmd.lchan.select(filename, self._cmd)
self._cmd.poutput("# file: %s (%s)" % (
self._cmd.lchan.selected_file.name, self._cmd.lchan.selected_file.fid))
structure = self._cmd.lchan.selected_file_structure()
self._cmd.poutput("# structure: %s" % str(structure))
self._cmd.poutput("# file: %s (%s)" %
(self._cmd.lchan.selected_file.name, self._cmd.lchan.selected_file.fid))
self._cmd.poutput("# structure: %s" % self._cmd.lchan.selected_file_structure())
self._cmd.poutput("# RAW FCP Template: %s" % str(self._cmd.lchan.selected_file_fcp_hex))
self._cmd.poutput("# Decoded FCP Template: %s" % str(self._cmd.lchan.selected_file_fcp))
self._cmd.poutput("select " + self._cmd.lchan.selected_file.fully_qualified_path_str())
self._cmd.poutput(self._cmd.lchan.selected_file.export(as_json, self._cmd.lchan))
for f in df_path_list:
self._cmd.poutput("select " + str(f))
self._cmd.poutput("select " + self._cmd.lchan.selected_file.name)
if structure == 'transparent':
if as_json:
result = self._cmd.lchan.read_binary_dec()
self._cmd.poutput("update_binary_decoded '%s'" % json.dumps(result[0], cls=JsonEncoder))
else:
result = self._cmd.lchan.read_binary()
self._cmd.poutput("update_binary " + str(result[0]))
elif structure == 'cyclic' or structure == 'linear_fixed':
# Use number of records specified in select response
num_of_rec = self._cmd.lchan.selected_file_num_of_rec()
if num_of_rec:
for r in range(1, num_of_rec + 1):
if as_json:
result = self._cmd.lchan.read_record_dec(r)
self._cmd.poutput("update_record_decoded %d '%s'" % (r, json.dumps(result[0], cls=JsonEncoder)))
else:
result = self._cmd.lchan.read_record(r)
self._cmd.poutput("update_record %d %s" % (r, str(result[0])))
# When the select response does not return the number of records, read until we hit the
# first record that cannot be read.
else:
r = 1
while True:
try:
if as_json:
result = self._cmd.lchan.read_record_dec(r)
self._cmd.poutput("update_record_decoded %d '%s'" % (r, json.dumps(result[0], cls=JsonEncoder)))
else:
result = self._cmd.lchan.read_record(r)
self._cmd.poutput("update_record %d %s" % (r, str(result[0])))
except SwMatchError as e:
# We are past the last valid record - stop
if e.sw_actual == "9402":
break
# Some other problem occurred
else:
raise e
r = r + 1
elif structure == 'ber_tlv':
tags = self._cmd.lchan.retrieve_tags()
for t in tags:
result = self._cmd.lchan.retrieve_data(t)
(tag, l, val, remainer) = bertlv_parse_one(h2b(result[0]))
self._cmd.poutput("set_data 0x%02x %s" % (t, b2h(val)))
else:
raise RuntimeError(
'Unsupported structure "%s" of file "%s"' % (structure, filename))
except Exception as e:
bad_file_str = df_path + "/" + str(filename) + ", " + str(e)
self._cmd.poutput("# bad file: %s" % bad_file_str)

View File

@@ -35,10 +35,13 @@ import cmd2
from cmd2 import CommandSet, with_default_category
from smartcard.util import toBytes
from pySim.utils import sw_match, h2b, b2h, is_hex, auto_int, auto_uint8, auto_uint16, is_hexstr
from pySim.utils import sw_match, h2b, b2h, is_hex, auto_int, auto_uint8, auto_uint16, is_hexstr, JsonEncoder
from pySim.utils import bertlv_parse_one
from pySim.construct import filter_dict, parse_construct, build_construct
from pySim.jsonpath import js_path_modify
from pySim.commands import SimCardCommands
from pySim.exceptions import SwMatchError
# int: a single service is associated with this file
# list: any of the listed services requires this file
@@ -774,6 +777,25 @@ class TransparentEF(CardEF):
raise NotImplementedError(
"%s encoder not yet implemented. Patches welcome." % self)
@staticmethod
def export(as_json: bool, lchan):
"""
Export the file contents of a TransparentEF. This method returns a shell command string (See also ShellCommand
definition in this class) that can be used to write the file contents back.
"""
if lchan.selected_file_structure() != 'transparent':
raise ValueError("selected file has structure type '%s', expecting a file with structure 'transparent'" %
lchan.selected_file_structure())
export_str = ""
if as_json:
result = lchan.read_binary_dec()
export_str += ("update_binary_decoded '%s'\n" % json.dumps(result[0], cls=JsonEncoder))
else:
result = lchan.read_binary()
export_str += ("update_binary %s\n" % str(result[0]))
return export_str.strip()
class LinFixedEF(CardEF):
"""Linear Fixed EF (Entry File) in the smart card filesystem.
@@ -1044,6 +1066,54 @@ class LinFixedEF(CardEF):
raise NotImplementedError(
"%s encoder not yet implemented. Patches welcome." % self)
@staticmethod
def export(as_json: bool, lchan):
"""
Export the file contents of a LinFixedEF (or a CyclicEF). This method returns a shell command string (See also
ShellCommand definition in this class) that can be used to write the file contents back.
"""
# A CyclicEF is a subclass of LinFixedEF.
if lchan.selected_file_structure() != 'linear_fixed' and lchan.selected_file_structure() != 'cyclic':
raise ValueError("selected file has structure type '%s', expecting a file with structure 'linear_fixed' or 'cyclic'" %
lchan.selected_file_structure())
export_str = ""
# Use number of records specified in select response
num_of_rec = lchan.selected_file_num_of_rec()
if num_of_rec:
for r in range(1, num_of_rec + 1):
if as_json:
result = lchan.read_record_dec(r)
export_str += ("update_record_decoded %d '%s'\n" % (r, json.dumps(result[0], cls=JsonEncoder)))
else:
result = lchan.read_record(r)
export_str += ("update_record %d %s\n" % (r, str(result[0])))
# In case the select response does not return the number of records, read until we hit the first record that
# cannot be read.
else:
r = 1
while True:
try:
if as_json:
result = lchan.read_record_dec(r)
export_str += ("update_record_decoded %d '%s'\n" % (r, json.dumps(result[0], cls=JsonEncoder)))
else:
result = lchan.read_record(r)
export_str += ("update_record %d %s\n" % (r, str(result[0])))
except SwMatchError as e:
# We are past the last valid record - stop
if e.sw_actual == "9402":
break
# Some other problem occurred
else:
raise e
r = r + 1
return export_str.strip()
class CyclicEF(LinFixedEF):
"""Cyclic EF (Entry File) in the smart card filesystem"""
@@ -1264,6 +1334,33 @@ class BerTlvEF(CardEF):
self.size = size
self.shell_commands = [self.ShellCommands()]
@staticmethod
def export(as_json: bool, lchan):
"""
Export the file contents of a BerTlvEF. This method returns a shell command string (See also ShellCommand
definition in this class) that can be used to write the file contents back.
"""
if lchan.selected_file_structure() != 'ber_tlv':
raise ValueError("selected file has structure type '%s', expecting a file with structure 'ber_tlv'" %
lchan.selected_file_structure())
# TODO: Add JSON output as soon as we have a set_data_decoded command and a retrieve_data_dec method.
if as_json:
raise NotImplementedError("BerTlvEF encoder not yet implemented. Patches welcome.")
export_str = ""
tags = lchan.retrieve_tags()
if tags == []:
export_str += "# empty file, no tags"
else:
for t in tags:
result = lchan.retrieve_data(t)
(tag, l, val, remainer) = bertlv_parse_one(h2b(result[0]))
export_str += ("set_data 0x%02x %s\n" % (t, b2h(val)))
return export_str.strip()
def interpret_sw(sw_data: dict, sw: str):
"""Interpret a given status word.