From 46255121e0fc5a6cffddc24c992e0dd6b24da425 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 21 Oct 2023 23:40:42 +0200 Subject: [PATCH] pySim-shell: Create + use per-RuntimeLchan SimCardCommands This new approach will "fork" separate SimCardCommands instances for each RuntimeLchan. Higher-layer code should now always use the RuntimeLchan.scc rather than the RuntimeState.card._scc in order to make sure commands use the correct logical channel. Change-Id: I13e2e871f2afc2460d9fd1cd566de42267c7d389 Related: OS#6230 --- pySim-shell.py | 26 +++++++++++++------------- pySim/ara_m.py | 8 ++++---- pySim/commands.py | 1 + pySim/global_platform.py | 2 +- pySim/runtime.py | 25 +++++++++++++------------ pySim/ts_102_222.py | 14 +++++++------- pySim/ts_31_102.py | 10 +++++----- pySim/ts_51_011.py | 2 +- 8 files changed, 45 insertions(+), 43 deletions(-) diff --git a/pySim-shell.py b/pySim-shell.py index 867795cf..8a1ad01a 100755 --- a/pySim-shell.py +++ b/pySim-shell.py @@ -326,7 +326,7 @@ class PysimApp(Cmd2Compat): DANGEROUS: pySim-shell will not know any card state changes, and not continue to work as expected if you e.g. select a different file.""" - data, sw = self.card._scc._tp.send_apdu(opts.APDU) + data, sw = self.lchan.scc._tp.send_apdu(opts.APDU) if data: self.poutput("SW: %s, RESP: %s" % (sw, data)) else: @@ -790,7 +790,7 @@ class PySimCommands(CommandSet): "cannot find ADM-PIN for ICCID '%s'" % (self._cmd.iccid)) if pin_adm: - self._cmd.card._scc.verify_chv(self._cmd.card._adm_chv_num, h2b(pin_adm)) + self._cmd.lchan.scc.verify_chv(self._cmd.card._adm_chv_num, h2b(pin_adm)) else: raise ValueError("error: cannot authenticate, no adm-pin!") @@ -798,10 +798,10 @@ class PySimCommands(CommandSet): """Display information about the currently inserted card""" self._cmd.poutput("Card info:") self._cmd.poutput(" Name: %s" % self._cmd.card.name) - self._cmd.poutput(" ATR: %s" % b2h(self._cmd.card._scc.get_atr())) + self._cmd.poutput(" ATR: %s" % b2h(self._cmd.lchan.scc.get_atr())) self._cmd.poutput(" ICCID: %s" % self._cmd.iccid) - self._cmd.poutput(" Class-Byte: %s" % self._cmd.card._scc.cla_byte) - self._cmd.poutput(" Select-Ctrl: %s" % self._cmd.card._scc.sel_ctrl) + self._cmd.poutput(" Class-Byte: %s" % self._cmd.lchan.scc.cla_byte) + self._cmd.poutput(" Select-Ctrl: %s" % self._cmd.lchan.scc.sel_ctrl) self._cmd.poutput(" AIDs:") for a in self._cmd.rs.mf.applications: self._cmd.poutput(" %s" % a) @@ -859,7 +859,7 @@ class Iso7816Commands(CommandSet): call it if you authenticate yourself using the specified PIN. There usually is at least PIN1 and PIN2.""" pin = self.get_code(opts.pin_code) - (data, sw) = self._cmd.card._scc.verify_chv(opts.pin_nr, h2b(pin)) + (data, sw) = self._cmd.lchan.scc.verify_chv(opts.pin_nr, h2b(pin)) self._cmd.poutput("CHV verification successful") unblock_chv_parser = argparse.ArgumentParser() @@ -875,7 +875,7 @@ class Iso7816Commands(CommandSet): """Unblock PIN code using specified PUK code""" new_pin = self.get_code(opts.new_pin_code) puk = self.get_code(opts.puk_code) - (data, sw) = self._cmd.card._scc.unblock_chv( + (data, sw) = self._cmd.lchan.scc.unblock_chv( opts.pin_nr, h2b(puk), h2b(new_pin)) self._cmd.poutput("CHV unblock successful") @@ -892,7 +892,7 @@ class Iso7816Commands(CommandSet): """Change PIN code to a new PIN code""" new_pin = self.get_code(opts.new_pin_code) pin = self.get_code(opts.pin_code) - (data, sw) = self._cmd.card._scc.change_chv( + (data, sw) = self._cmd.lchan.scc.change_chv( opts.pin_nr, h2b(pin), h2b(new_pin)) self._cmd.poutput("CHV change successful") @@ -906,7 +906,7 @@ class Iso7816Commands(CommandSet): def do_disable_chv(self, opts): """Disable PIN code using specified PIN code""" pin = self.get_code(opts.pin_code) - (data, sw) = self._cmd.card._scc.disable_chv(opts.pin_nr, h2b(pin)) + (data, sw) = self._cmd.lchan.scc.disable_chv(opts.pin_nr, h2b(pin)) self._cmd.poutput("CHV disable successful") enable_chv_parser = argparse.ArgumentParser() @@ -919,12 +919,12 @@ class Iso7816Commands(CommandSet): def do_enable_chv(self, opts): """Enable PIN code using specified PIN code""" pin = self.get_code(opts.pin_code) - (data, sw) = self._cmd.card._scc.enable_chv(opts.pin_nr, h2b(pin)) + (data, sw) = self._cmd.lchan.scc.enable_chv(opts.pin_nr, h2b(pin)) self._cmd.poutput("CHV enable successful") def do_deactivate_file(self, opts): """Deactivate the currently selected EF""" - (data, sw) = self._cmd.card._scc.deactivate_file() + (data, sw) = self._cmd.lchan.scc.deactivate_file() activate_file_parser = argparse.ArgumentParser() activate_file_parser.add_argument('NAME', type=str, help='File name or FID of file to activate') @@ -946,7 +946,7 @@ class Iso7816Commands(CommandSet): @cmd2.with_argparser(open_chan_parser) def do_open_channel(self, opts): """Open a logical channel.""" - (data, sw) = self._cmd.card._scc.manage_channel( + (data, sw) = self._cmd.lchan.scc.manage_channel( mode='open', lchan_nr=opts.chan_nr) # this is executed only in successful case, as unsuccessful raises exception self._cmd.lchan.add_lchan(opts.chan_nr) @@ -958,7 +958,7 @@ class Iso7816Commands(CommandSet): @cmd2.with_argparser(close_chan_parser) def do_close_channel(self, opts): """Close a logical channel.""" - (data, sw) = self._cmd.card._scc.manage_channel( + (data, sw) = self._cmd.lchan.scc.manage_channel( mode='close', lchan_nr=opts.chan_nr) # this is executed only in successful case, as unsuccessful raises exception self._cmd.rs.del_lchan(opts.chan_nr) diff --git a/pySim/ara_m.py b/pySim/ara_m.py index 81443812..b4ac7471 100644 --- a/pySim/ara_m.py +++ b/pySim/ara_m.py @@ -306,13 +306,13 @@ class ADF_ARAM(CardADF): def do_aram_get_all(self, opts): """GET DATA [All] on the ARA-M Applet""" - res_do = ADF_ARAM.get_all(self._cmd.card._scc._tp) + res_do = ADF_ARAM.get_all(self._cmd.lchan.scc._tp) if res_do: self._cmd.poutput_json(res_do.to_dict()) def do_aram_get_config(self, opts): """Perform GET DATA [Config] on the ARA-M Applet: Tell it our version and retrieve its version.""" - res_do = ADF_ARAM.get_config(self._cmd.card._scc._tp) + res_do = ADF_ARAM.get_config(self._cmd.lchan.scc._tp) if res_do: self._cmd.poutput_json(res_do.to_dict()) @@ -373,14 +373,14 @@ class ADF_ARAM(CardADF): d = [{'ref_ar_do': [{'ref_do': ref_do_content}, {'ar_do': ar_do_content}]}] csrado = CommandStoreRefArDO() csrado.from_dict(d) - res_do = ADF_ARAM.store_data(self._cmd.card._scc._tp, csrado) + res_do = ADF_ARAM.store_data(self._cmd.lchan.scc._tp, csrado) if res_do: self._cmd.poutput_json(res_do.to_dict()) def do_aram_delete_all(self, opts): """Perform STORE DATA [Command-Delete[all]] to delete all access rules.""" deldo = CommandDelete() - res_do = ADF_ARAM.store_data(self._cmd.card._scc._tp, deldo) + res_do = ADF_ARAM.store_data(self._cmd.lchan.scc._tp, deldo) if res_do: self._cmd.poutput_json(res_do.to_dict()) diff --git a/pySim/commands.py b/pySim/commands.py index 3edee987..336cf0cc 100644 --- a/pySim/commands.py +++ b/pySim/commands.py @@ -74,6 +74,7 @@ class SimCardCommands: """Fork a per-lchan specific SimCardCommands instance off the current instance.""" ret = SimCardCommands(transport = self._tp, lchan_nr = lchan_nr) ret.cla_byte = self._cla_byte + ret.sel_ctrl = self.sel_ctrl return ret @property diff --git a/pySim/global_platform.py b/pySim/global_platform.py index ea8b70df..bfa2adf6 100644 --- a/pySim/global_platform.py +++ b/pySim/global_platform.py @@ -238,7 +238,7 @@ class ADF_SD(CardADF): self._cmd.poutput('Unknown data object "%s", available options: %s' % (tlv_cls_name, do_names)) return - (data, sw) = self._cmd.card._scc.get_data(cla=0x80, tag=tlv_cls.tag) + (data, sw) = self._cmd.lchan.scc.get_data(cla=0x80, tag=tlv_cls.tag) ie = tlv_cls() ie.from_tlv(h2b(data)) self._cmd.poutput_json(ie.to_dict()) diff --git a/pySim/runtime.py b/pySim/runtime.py index 88de69eb..27c2ef1f 100644 --- a/pySim/runtime.py +++ b/pySim/runtime.py @@ -167,6 +167,7 @@ class RuntimeLchan: self.selected_adf = None self.selected_file_fcp = None self.selected_file_fcp_hex = None + self.scc = self.rs.card._scc.fork_lchan(lchan_nr) def add_lchan(self, lchan_nr: int) -> 'RuntimeLchan': """Add a new logical channel from the current logical channel. Just affects @@ -246,7 +247,7 @@ class RuntimeLchan: "Cannot select unknown file by name %s, only hexadecimal 4 digit FID is allowed" % fid) try: - (data, sw) = self.rs.card._scc.select_file(fid) + (data, sw) = self.scc.select_file(fid) except SwMatchError as swm: k = self.interpret_sw(swm.sw_actual) if not k: @@ -301,7 +302,7 @@ class RuntimeLchan: (data, sw) = self.rs.card.select_adf_by_aid(p.aid) self.selected_adf = p else: - (data, sw) = self.rs.card._scc.select_file(p.fid) + (data, sw) = self.scc.select_file(p.fid) self.selected_file = p except SwMatchError as swm: self._select_post(cmd_app) @@ -344,7 +345,7 @@ class RuntimeLchan: if isinstance(f, CardADF): (data, sw) = self.rs.card.select_adf_by_aid(f.aid) else: - (data, sw) = self.rs.card._scc.select_file(f.fid) + (data, sw) = self.scc.select_file(f.fid) self.selected_file = f except SwMatchError as swm: k = self.interpret_sw(swm.sw_actual) @@ -364,7 +365,7 @@ class RuntimeLchan: def status(self): """Request STATUS (current selected file FCP) from card.""" - (data, sw) = self.rs.card._scc.status() + (data, sw) = self.scc.status() return self.selected_file.decode_select_response(data) def get_file_for_selectable(self, name: str): @@ -375,7 +376,7 @@ class RuntimeLchan: """Request ACTIVATE FILE of specified file.""" sels = self.selected_file.get_selectables() f = sels[name] - data, sw = self.rs.card._scc.activate_file(f.fid) + data, sw = self.scc.activate_file(f.fid) return data, sw def read_binary(self, length: int = None, offset: int = 0): @@ -389,7 +390,7 @@ class RuntimeLchan: """ if not isinstance(self.selected_file, TransparentEF): raise TypeError("Only works with TransparentEF") - return self.rs.card._scc.read_binary(self.selected_file.fid, length, offset) + return self.scc.read_binary(self.selected_file.fid, length, offset) def read_binary_dec(self) -> Tuple[dict, str]: """Read [part of] a transparent EF binary data and decode it. @@ -413,7 +414,7 @@ class RuntimeLchan: """ if not isinstance(self.selected_file, TransparentEF): raise TypeError("Only works with TransparentEF") - return self.rs.card._scc.update_binary(self.selected_file.fid, data_hex, offset, conserve=self.rs.conserve_write) + return self.scc.update_binary(self.selected_file.fid, data_hex, offset, conserve=self.rs.conserve_write) def update_binary_dec(self, data: dict): """Update transparent EF from abstract data. Encodes the data to binary and @@ -436,7 +437,7 @@ class RuntimeLchan: if not isinstance(self.selected_file, LinFixedEF): raise TypeError("Only works with Linear Fixed EF") # returns a string of hex nibbles - return self.rs.card._scc.read_record(self.selected_file.fid, rec_nr) + return self.scc.read_record(self.selected_file.fid, rec_nr) def read_record_dec(self, rec_nr: int = 0) -> Tuple[dict, str]: """Read a record and decode it to abstract data. @@ -458,7 +459,7 @@ class RuntimeLchan: """ if not isinstance(self.selected_file, LinFixedEF): raise TypeError("Only works with Linear Fixed EF") - return self.rs.card._scc.update_record(self.selected_file.fid, rec_nr, data_hex, + return self.scc.update_record(self.selected_file.fid, rec_nr, data_hex, conserve=self.rs.conserve_write, leftpad=self.selected_file.leftpad) @@ -484,7 +485,7 @@ class RuntimeLchan: if not isinstance(self.selected_file, BerTlvEF): raise TypeError("Only works with BER-TLV EF") # returns a string of hex nibbles - return self.rs.card._scc.retrieve_data(self.selected_file.fid, tag) + return self.scc.retrieve_data(self.selected_file.fid, tag) def retrieve_tags(self): """Retrieve tags available on BER-TLV EF. @@ -494,7 +495,7 @@ class RuntimeLchan: """ if not isinstance(self.selected_file, BerTlvEF): raise TypeError("Only works with BER-TLV EF") - data, sw = self.rs.card._scc.retrieve_data(self.selected_file.fid, 0x5c) + data, sw = self.scc.retrieve_data(self.selected_file.fid, 0x5c) tag, length, value, remainder = bertlv_parse_one(h2b(data)) return list(value) @@ -507,7 +508,7 @@ class RuntimeLchan: """ if not isinstance(self.selected_file, BerTlvEF): raise TypeError("Only works with BER-TLV EF") - return self.rs.card._scc.set_data(self.selected_file.fid, tag, data_hex, conserve=self.rs.conserve_write) + return self.scc.set_data(self.selected_file.fid, tag, data_hex, conserve=self.rs.conserve_write) def unregister_cmds(self, cmd_app=None): """Unregister all file specific commands.""" diff --git a/pySim/ts_102_222.py b/pySim/ts_102_222.py index a4ea4534..372d67fc 100644 --- a/pySim/ts_102_222.py +++ b/pySim/ts_102_222.py @@ -49,7 +49,7 @@ class Ts102222Commands(CommandSet): self._cmd.perror("Refusing to permanently delete the file, please read the help text.") return f = self._cmd.lchan.get_file_for_selectable(opts.NAME) - (data, sw) = self._cmd.card._scc.delete_file(f.fid) + (data, sw) = self._cmd.lchan.scc.delete_file(f.fid) def complete_delete_file(self, text, line, begidx, endidx) -> List[str]: """Command Line tab completion for DELETE FILE""" @@ -70,7 +70,7 @@ class Ts102222Commands(CommandSet): self._cmd.perror("Refusing to terminate the file, please read the help text.") return f = self._cmd.lchan.get_file_for_selectable(opts.NAME) - (data, sw) = self._cmd.card._scc.terminate_df(f.fid) + (data, sw) = self._cmd.lchan.scc.terminate_df(f.fid) def complete_terminate_df(self, text, line, begidx, endidx) -> List[str]: """Command Line tab completion for TERMINATE DF""" @@ -86,7 +86,7 @@ class Ts102222Commands(CommandSet): self._cmd.perror("Refusing to terminate the file, please read the help text.") return f = self._cmd.lchan.get_file_for_selectable(opts.NAME) - (data, sw) = self._cmd.card._scc.terminate_ef(f.fid) + (data, sw) = self._cmd.lchan.scc.terminate_ef(f.fid) def complete_terminate_ef(self, text, line, begidx, endidx) -> List[str]: """Command Line tab completion for TERMINATE EF""" @@ -104,7 +104,7 @@ class Ts102222Commands(CommandSet): if not opts.force_terminate_card: self._cmd.perror("Refusing to permanently terminate the card, please read the help text.") return - (data, sw) = self._cmd.card._scc.terminate_card_usage() + (data, sw) = self._cmd.lchan.scc.terminate_card_usage() create_parser = argparse.ArgumentParser() create_parser.add_argument('FILE_ID', type=str, help='File Identifier as 4-character hex string') @@ -149,7 +149,7 @@ class Ts102222Commands(CommandSet): ShortFileIdentifier(decoded=opts.short_file_id), ] fcp = FcpTemplate(children=ies) - (data, sw) = self._cmd.card._scc.create_file(b2h(fcp.to_tlv())) + (data, sw) = self._cmd.lchan.scc.create_file(b2h(fcp.to_tlv())) # the newly-created file is automatically selected but our runtime state knows nothing of it self._cmd.lchan.select_file(self._cmd.lchan.selected_file) @@ -200,7 +200,7 @@ class Ts102222Commands(CommandSet): } ies.append(ProprietaryInformation(children=[ToolkitAccessConditions(decoded=toolkit_ac)])) fcp = FcpTemplate(children=ies) - (data, sw) = self._cmd.card._scc.create_file(b2h(fcp.to_tlv())) + (data, sw) = self._cmd.lchan.scc.create_file(b2h(fcp.to_tlv())) # the newly-created file is automatically selected but our runtime state knows nothing of it self._cmd.lchan.select_file(self._cmd.lchan.selected_file) @@ -217,7 +217,7 @@ class Ts102222Commands(CommandSet): ies = [FileIdentifier(decoded=f.fid), FileSize(decoded=opts.file_size)] fcp = FcpTemplate(children=ies) - (data, sw) = self._cmd.card._scc.resize_file(b2h(fcp.to_tlv())) + (data, sw) = self._cmd.lchan.scc.resize_file(b2h(fcp.to_tlv())) # the resized file is automatically selected but our runtime state knows nothing of it self._cmd.lchan.select_file(self._cmd.lchan.selected_file) diff --git a/pySim/ts_31_102.py b/pySim/ts_31_102.py index 54ff4c81..cd0d99c3 100644 --- a/pySim/ts_31_102.py +++ b/pySim/ts_31_102.py @@ -1512,7 +1512,7 @@ class ADF_USIM(CardADF): @cmd2.with_argparser(authenticate_parser) def do_authenticate(self, opts): """Perform Authentication and Key Agreement (AKA).""" - (data, sw) = self._cmd.card._scc.authenticate(opts.rand, opts.autn) + (data, sw) = self._cmd.lchan.scc.authenticate(opts.rand, opts.autn) self._cmd.poutput_json(data) term_prof_parser = argparse.ArgumentParser() @@ -1526,7 +1526,7 @@ class ADF_USIM(CardADF): in the context of SIM Toolkit, Proactive SIM and OTA. You must specify a hex-string with the encoded terminal profile you want to send to the card.""" - (data, sw) = self._cmd.card._scc.terminal_profile(opts.PROFILE) + (data, sw) = self._cmd.lchan.scc.terminal_profile(opts.PROFILE) self._cmd.poutput('SW: %s, data: %s' % (sw, data)) envelope_parser = argparse.ArgumentParser() @@ -1538,7 +1538,7 @@ class ADF_USIM(CardADF): variety of information is communicated from the terminal (modem/phone) to the card, particularly in the context of SIM Toolkit, Proactive SIM and OTA.""" - (data, sw) = self._cmd.card._scc.envelope(opts.PAYLOAD) + (data, sw) = self._cmd.lchan.scc.envelope(opts.PAYLOAD) self._cmd.poutput('SW: %s, data: %s' % (sw, data)) envelope_sms_parser = argparse.ArgumentParser() @@ -1556,7 +1556,7 @@ class ADF_USIM(CardADF): dev_ids = DeviceIdentities( decoded={'source_dev_id': 'network', 'dest_dev_id': 'uicc'}) sms_dl = SMSPPDownload(children=[dev_ids, tpdu_ie]) - (data, sw) = self._cmd.card._scc.envelope(b2h(sms_dl.to_tlv())) + (data, sw) = self._cmd.lchan.scc.envelope(b2h(sms_dl.to_tlv())) self._cmd.poutput('SW: %s, data: %s' % (sw, data)) get_id_parser = argparse.ArgumentParser() @@ -1570,7 +1570,7 @@ class ADF_USIM(CardADF): context = 0x01 # SUCI if opts.nswo_context: context = 0x02 # SUCI 5G NSWO - (data, sw) = self._cmd.card._scc.get_identity(context) + (data, sw) = self._cmd.lchan.scc.get_identity(context) do = SUCI_TlvDataObject() do.from_tlv(h2b(data)) do_d = do.to_dict() diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py index 14db6861..74130984 100644 --- a/pySim/ts_51_011.py +++ b/pySim/ts_51_011.py @@ -1001,7 +1001,7 @@ class DF_GSM(CardDF): @cmd2.with_argparser(authenticate_parser) def do_authenticate(self, opts): """Perform GSM Authentication.""" - (data, sw) = self._cmd.card._scc.run_gsm(opts.rand) + (data, sw) = self._cmd.lchan.scc.run_gsm(opts.rand) self._cmd.poutput_json(data)