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):

--
To view, visit https://gerrit.osmocom.org/c/pysim/+/37840?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: merged
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Ie3e48678f178a488bfaea6cc2b9a3e18145a8d10
Gerrit-Change-Number: 37840
Gerrit-PatchSet: 9
Gerrit-Owner: dexter <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <[email protected]>
Gerrit-Reviewer: laforge <[email protected]>

Reply via email to