laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/37840?usp=email )
Change subject: ara_m: use class byte of current lchan ......................................................................
ara_m: use class byte of current lchan
The ara_m commands use APDUs with a fix class byte (0x80). This means that all ARA-M related features only work in the basic logical channel. To fix this, let's compute the class byte for the current logical channel dynamically inside the send_apdu methods of SimCardCommands. This will fix the problem globally.
Related: OS#6531 Change-Id: Ie3e48678f178a488bfaea6cc2b9a3e18145a8d10 --- M pySim-shell.py M pySim/ara_m.py M pySim/commands.py 3 files changed, 33 insertions(+), 26 deletions(-)
Approvals: fixeria: Looks good to me, but someone else must approve laforge: Looks good to me, approved Jenkins Builder: Verified
diff --git a/pySim-shell.py b/pySim-shell.py index 0ba8a25..43e9dd2 100755 --- a/pySim-shell.py +++ b/pySim-shell.py @@ -252,9 +252,9 @@ # can be executed without the presence of a runtime state (self.rs) object. However, this also means that # self.lchan is also not present (see method equip). if opts.raw or self.lchan is None: - data, sw = self.card._scc.send_apdu(opts.APDU) + data, sw = self.card._scc.send_apdu(opts.APDU, apply_lchan = False) else: - data, sw = self.lchan.scc.send_apdu(opts.APDU) + data, sw = self.lchan.scc.send_apdu(opts.APDU, apply_lchan = False) if data: self.poutput("SW: %s, RESP: %s" % (sw, data)) else: diff --git a/pySim/ara_m.py b/pySim/ara_m.py index cdc934d..e6f0520 100644 --- a/pySim/ara_m.py +++ b/pySim/ara_m.py @@ -262,7 +262,7 @@ return pySim.global_platform.decode_select_response(data_hex)
@staticmethod - def xceive_apdu_tlv(tp, hdr: Hexstr, cmd_do, resp_cls, exp_sw='9000'): + def xceive_apdu_tlv(scc, hdr: Hexstr, cmd_do, resp_cls, exp_sw='9000'): """Transceive an APDU with the card, transparently encoding the command data from TLV and decoding the response data tlv.""" if cmd_do: @@ -274,7 +274,7 @@ cmd_do_enc = b'' cmd_do_len = 0 c_apdu = hdr + ('%02x' % cmd_do_len) + b2h(cmd_do_enc) - (data, _sw) = tp.send_apdu_checksw(c_apdu, exp_sw) + (data, _sw) = scc.send_apdu_checksw(c_apdu, exp_sw) if data: if resp_cls: resp_do = resp_cls() @@ -285,32 +285,32 @@ return None
@staticmethod - def store_data(tp, do) -> bytes: + def store_data(scc, do) -> bytes: """Build the Command APDU for STORE DATA.""" - return ADF_ARAM.xceive_apdu_tlv(tp, '80e29000', do, StoreResponseDoCollection) + return ADF_ARAM.xceive_apdu_tlv(scc, '80e29000', do, StoreResponseDoCollection)
@staticmethod - def get_all(tp): - return ADF_ARAM.xceive_apdu_tlv(tp, '80caff40', None, GetResponseDoCollection) + def get_all(scc): + return ADF_ARAM.xceive_apdu_tlv(scc, '80caff40', None, GetResponseDoCollection)
@staticmethod - def get_config(tp, v_major=0, v_minor=0, v_patch=1): + def get_config(scc, v_major=0, v_minor=0, v_patch=1): cmd_do = DeviceConfigDO() cmd_do.from_val_dict([{'device_interface_version_do': { 'major': v_major, 'minor': v_minor, 'patch': v_patch}}]) - return ADF_ARAM.xceive_apdu_tlv(tp, '80cadf21', cmd_do, ResponseAramConfigDO) + return ADF_ARAM.xceive_apdu_tlv(scc, '80cadf21', cmd_do, ResponseAramConfigDO)
@with_default_category('Application-Specific Commands') class AddlShellCommands(CommandSet): def do_aram_get_all(self, _opts): """GET DATA [All] on the ARA-M Applet""" - res_do = ADF_ARAM.get_all(self._cmd.lchan.scc._tp) + res_do = ADF_ARAM.get_all(self._cmd.lchan.scc) 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.lchan.scc._tp) + res_do = ADF_ARAM.get_config(self._cmd.lchan.scc) if res_do: self._cmd.poutput_json(res_do.to_dict())
@@ -378,14 +378,14 @@ d = [{'ref_ar_do': [{'ref_do': ref_do_content}, {'ar_do': ar_do_content}]}] csrado = CommandStoreRefArDO() csrado.from_val_dict(d) - res_do = ADF_ARAM.store_data(self._cmd.lchan.scc._tp, csrado) + res_do = ADF_ARAM.store_data(self._cmd.lchan.scc, 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.lchan.scc._tp, deldo) + res_do = ADF_ARAM.store_data(self._cmd.lchan.scc, deldo) if res_do: self._cmd.poutput_json(res_do.to_dict())
@@ -479,7 +479,7 @@ export_str = "" export_str += "aram_delete_all\n"
- res_do = ADF_ARAM.get_all(lchan.scc._tp) + res_do = ADF_ARAM.get_all(lchan.scc) if not res_do: return export_str.strip()
diff --git a/pySim/commands.py b/pySim/commands.py index 43fc705..3a7fe47 100644 --- a/pySim/commands.py +++ b/pySim/commands.py @@ -110,26 +110,30 @@ else: return cla_with_lchan(cla, self.lchan_nr)
- def send_apdu(self, pdu: Hexstr) -> ResTuple: + def send_apdu(self, pdu: Hexstr, apply_lchan:bool = True) -> ResTuple: """Sends an APDU and auto fetch response data
Args: pdu : string of hexadecimal characters (ex. "A0A40000023F00") + apply_lchan : apply the currently selected lchan to the CLA byte before sending Returns: tuple(data, sw), where data : string (in hex) of returned data (ex. "074F4EFFFF") sw : string (in hex) of status word (ex. "9000") """ + if apply_lchan: + pdu = self.cla4lchan(pdu[0:2]) + pdu[2:] if self.scp: return self.scp.send_apdu_wrapper(self._tp.send_apdu, pdu) else: return self._tp.send_apdu(pdu)
- def send_apdu_checksw(self, pdu: Hexstr, sw: SwMatchstr = "9000") -> ResTuple: + def send_apdu_checksw(self, pdu: Hexstr, sw: SwMatchstr = "9000", apply_lchan:bool = True) -> ResTuple: """Sends an APDU and check returned SW
Args: pdu : string of hexadecimal characters (ex. "A0A40000023F00") + apply_lchan : apply the currently selected lchan to the CLA byte before sending sw : string of 4 hexadecimal characters (ex. "9000"). The user may mask out certain digits using a '?' to add some ambiguity if needed. Returns: @@ -137,13 +141,15 @@ data : string (in hex) of returned data (ex. "074F4EFFFF") sw : string (in hex) of status word (ex. "9000") """ + if apply_lchan: + pdu = self.cla4lchan(pdu[0:2]) + pdu[2:] if self.scp: return self.scp.send_apdu_wrapper(self._tp.send_apdu_checksw, pdu, sw) else: return self._tp.send_apdu_checksw(pdu, sw)
def send_apdu_constr(self, cla: Hexstr, ins: Hexstr, p1: Hexstr, p2: Hexstr, cmd_constr: Construct, - cmd_data: Hexstr, resp_constr: Construct) -> Tuple[dict, SwHexstr]: + cmd_data: Hexstr, resp_constr: Construct, apply_lchan:bool = True) -> Tuple[dict, SwHexstr]: """Build and sends an APDU using a 'construct' definition; parses response.
Args: @@ -154,13 +160,14 @@ cmd_cosntr : defining how to generate binary APDU command data cmd_data : command data passed to cmd_constr resp_cosntr : defining how to decode binary APDU response data + apply_lchan : apply the currently selected lchan to the CLA byte before sending Returns: Tuple of (decoded_data, sw) """ cmd = cmd_constr.build(cmd_data) if cmd_data else '' p3 = i2h([len(cmd)]) pdu = ''.join([cla, ins, p1, p2, p3, b2h(cmd)]) - (data, sw) = self.send_apdu(pdu) + (data, sw) = self.send_apdu(pdu, apply_lchan = apply_lchan) if data: # filter the resulting dict to avoid '_io' members inside rsp = filter_dict(resp_constr.parse(h2b(data))) @@ -170,7 +177,7 @@
def send_apdu_constr_checksw(self, cla: Hexstr, ins: Hexstr, p1: Hexstr, p2: Hexstr, cmd_constr: Construct, cmd_data: Hexstr, resp_constr: Construct, - sw_exp: SwMatchstr="9000") -> Tuple[dict, SwHexstr]: + sw_exp: SwMatchstr="9000", apply_lchan:bool = True) -> Tuple[dict, SwHexstr]: """Build and sends an APDU using a 'construct' definition; parses response.
Args: @@ -185,8 +192,8 @@ Returns: Tuple of (decoded_data, sw) """ - (rsp, sw) = self.send_apdu_constr(cla, ins, - p1, p2, cmd_constr, cmd_data, resp_constr) + (rsp, sw) = self.send_apdu_constr(cla, ins, p1, p2, cmd_constr, cmd_data, resp_constr, + apply_lchan = apply_lchan) if not sw_match(sw, sw_exp): raise SwMatchError(sw, sw_exp.lower(), self._tp.sw_interpreter) return (rsp, sw) @@ -750,7 +757,7 @@ Args: payload : payload as hex string """ - return self.send_apdu_checksw('80c20000%02x%s' % (len(payload)//2, payload)) + return self.send_apdu_checksw('80c20000%02x%s' % (len(payload)//2, payload), apply_lchan = False)
def terminal_profile(self, payload: Hexstr) -> ResTuple: """Send TERMINAL PROFILE to card @@ -759,7 +766,7 @@ payload : payload as hex string """ data_length = len(payload) // 2 - data, sw = self.send_apdu_checksw(('80100000%02x' % data_length) + payload) + data, sw = self.send_apdu_checksw(('80100000%02x' % data_length) + payload, apply_lchan = False) return (data, sw)
# ETSI TS 102 221 11.1.22 @@ -797,7 +804,7 @@ raise ValueError('Time unit must be 0x00..0x04') min_dur_enc = encode_duration(min_len_secs) max_dur_enc = encode_duration(max_len_secs) - data, sw = self.send_apdu_checksw('8076000004' + min_dur_enc + max_dur_enc) + data, sw = self.send_apdu_checksw('8076000004' + min_dur_enc + max_dur_enc, apply_lchan = False) negotiated_duration_secs = decode_duration(data[:4]) resume_token = data[4:] return (negotiated_duration_secs, resume_token, sw) @@ -807,7 +814,7 @@ """Send SUSPEND UICC (resume) to the card.""" if len(h2b(token)) != 8: raise ValueError("Token must be 8 bytes long") - data, sw = self.send_apdu_checksw('8076010008' + token) + data, sw = self.send_apdu_checksw('8076010008' + token, apply_lchan = False) return (data, sw)
def get_data(self, tag: int, cla: int = 0x00):