<p>laforge <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/pysim/+/26165">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">pySim-shell: add method to match card profile to card<br><br>UICC and old SIM cards can be difficult to tell apart without prior<br>knowledge of the card. The ATR won't tell if the card is UICC or not.<br>The only remaining option is to try out if the card is able to handle<br>UICC APDUs. The same is true for 2G SIM cards. It is not guranteed that<br>every UICC card will have 2G functionality.<br><br>Lets add functionality to match a profile to the currently plugged card<br>by actively probing it.<br><br>Lets also add another profile to distinguish between UICC-only cards and<br>UICC cards that include SIM functionality.<br><br>Change-Id: If090d32551145f75c644657b90085a3ef5bfa691<br>Related: OS#5274<br>---<br>M pySim-shell.py<br>M pySim/filesystem.py<br>A pySim/profile.py<br>M pySim/ts_102_221.py<br>M pySim/ts_51_011.py<br>M pySim/utils.py<br>6 files changed, 216 insertions(+), 70 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/pySim-shell.py b/pySim-shell.py</span><br><span>index 128c0ea..0519ec4 100755</span><br><span>--- a/pySim-shell.py</span><br><span>+++ b/pySim-shell.py</span><br><span>@@ -45,8 +45,10 @@</span><br><span> from pySim.card_handler import CardHandler, CardHandlerAuto</span><br><span> </span><br><span> from pySim.filesystem import CardMF, RuntimeState, CardDF, CardADF, CardModel</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.profile import CardProfile</span><br><span> from pySim.ts_51_011 import CardProfileSIM, DF_TELECOM, DF_GSM</span><br><span> from pySim.ts_102_221 import CardProfileUICC</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_102_221 import CardProfileUICCSIM</span><br><span> from pySim.ts_31_102 import CardApplicationUSIM</span><br><span> from pySim.ts_31_103 import CardApplicationISIM</span><br><span> from pySim.ara_m import CardApplicationARAM</span><br><span>@@ -80,19 +82,32 @@</span><br><span> </span><br><span>     card = card_detect("auto", scc)</span><br><span>    if card is None:</span><br><span style="color: hsl(0, 100%, 40%);">-                print("Could not detect card type!")</span><br><span style="color: hsl(120, 100%, 40%);">+                print("Warning: Could not detect card type - assuming a generic card type...")</span><br><span style="color: hsl(120, 100%, 40%);">+              card = SimCard(scc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ profile = CardProfile.pick(scc)</span><br><span style="color: hsl(120, 100%, 40%);">+       if profile is None:</span><br><span style="color: hsl(120, 100%, 40%);">+           print("Unsupported card type!")</span><br><span>            return None, None</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ print("Info: Card is of type: %s" % str(profile))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # FIXME: This shouln't be here, the profile should add the applications,</span><br><span style="color: hsl(120, 100%, 40%);">+  # however, we cannot simply put his into ts_102_221.py since we would</span><br><span style="color: hsl(120, 100%, 40%);">+ # have to e.g. import CardApplicationUSIM from ts_31_102.py, which already</span><br><span style="color: hsl(120, 100%, 40%);">+    # imports from ts_102_221.py. This means we will end up with a circular</span><br><span style="color: hsl(120, 100%, 40%);">+       # import, which needs to be resolved first.</span><br><span style="color: hsl(120, 100%, 40%);">+   if isinstance(profile, CardProfileUICC):</span><br><span style="color: hsl(120, 100%, 40%);">+              profile.add_application(CardApplicationUSIM())</span><br><span style="color: hsl(120, 100%, 40%);">+                profile.add_application(CardApplicationISIM())</span><br><span style="color: hsl(120, 100%, 40%);">+                profile.add_application(CardApplicationARAM())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     # Create runtime state with card profile</span><br><span style="color: hsl(0, 100%, 40%);">-        profile = CardProfileUICC()</span><br><span style="color: hsl(0, 100%, 40%);">-     profile.add_application(CardApplicationUSIM())</span><br><span style="color: hsl(0, 100%, 40%);">-  profile.add_application(CardApplicationISIM())</span><br><span style="color: hsl(0, 100%, 40%);">-  profile.add_application(CardApplicationARAM())</span><br><span>       rs = RuntimeState(card, profile)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    # FIXME: do this dynamically</span><br><span style="color: hsl(0, 100%, 40%);">-    rs.mf.add_file(DF_TELECOM())</span><br><span style="color: hsl(0, 100%, 40%);">-    rs.mf.add_file(DF_GSM())</span><br><span style="color: hsl(120, 100%, 40%);">+      # FIXME: This is an GSM-R related file, it needs to be added throughout,</span><br><span style="color: hsl(120, 100%, 40%);">+      # the profile. At the moment we add it for all cards, this won't hurt,</span><br><span style="color: hsl(120, 100%, 40%);">+    # but regular SIM and UICC will not have it and fail to select it.</span><br><span>   rs.mf.add_file(DF_EIRENE())</span><br><span> </span><br><span>      CardModel.apply_matching_models(scc, rs)</span><br><span>diff --git a/pySim/filesystem.py b/pySim/filesystem.py</span><br><span>index 0238c97..6b20db5 100644</span><br><span>--- a/pySim/filesystem.py</span><br><span>+++ b/pySim/filesystem.py</span><br><span>@@ -1443,65 +1443,6 @@</span><br><span>         """</span><br><span>         return interpret_sw(self.sw, sw)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-class CardProfile(object):</span><br><span style="color: hsl(0, 100%, 40%);">-    """A Card Profile describes a card, it's filesystem hierarchy, an [initial] list of</span><br><span style="color: hsl(0, 100%, 40%);">-       applications as well as profile-specific SW and shell commands.  Every card has</span><br><span style="color: hsl(0, 100%, 40%);">-       one card profile, but there may be multiple applications within that profile."""</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self, name, **kw):</span><br><span style="color: hsl(0, 100%, 40%);">-        """</span><br><span style="color: hsl(0, 100%, 40%);">-        Args:</span><br><span style="color: hsl(0, 100%, 40%);">-            desc (str) : Description</span><br><span style="color: hsl(0, 100%, 40%);">-            files_in_mf : List of CardEF instances present in MF</span><br><span style="color: hsl(0, 100%, 40%);">-            applications : List of CardApplications present on card</span><br><span style="color: hsl(0, 100%, 40%);">-            sw : List of status word definitions</span><br><span style="color: hsl(0, 100%, 40%);">-            shell_cmdsets : List of cmd2 shell command sets of profile-specific commands</span><br><span style="color: hsl(0, 100%, 40%);">-            cla : class byte that should be used with cards of this profile</span><br><span style="color: hsl(0, 100%, 40%);">-            sel_ctrl : selection control bytes class byte that should be used with cards of this profile</span><br><span style="color: hsl(0, 100%, 40%);">-        """</span><br><span style="color: hsl(0, 100%, 40%);">-        self.name = name</span><br><span style="color: hsl(0, 100%, 40%);">-        self.desc = kw.get("desc", None)</span><br><span style="color: hsl(0, 100%, 40%);">-        self.files_in_mf = kw.get("files_in_mf", [])</span><br><span style="color: hsl(0, 100%, 40%);">-        self.sw = kw.get("sw", {})</span><br><span style="color: hsl(0, 100%, 40%);">-        self.applications = kw.get("applications", [])</span><br><span style="color: hsl(0, 100%, 40%);">-        self.shell_cmdsets = kw.get("shell_cmdsets", [])</span><br><span style="color: hsl(0, 100%, 40%);">-        self.cla = kw.get("cla", "00")</span><br><span style="color: hsl(0, 100%, 40%);">-        self.sel_ctrl = kw.get("sel_ctrl", "0004")</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def __str__(self):</span><br><span style="color: hsl(0, 100%, 40%);">-        return self.name</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def add_application(self, app:CardApplication):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Add an application to a card profile.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        Args:</span><br><span style="color: hsl(0, 100%, 40%);">-            app : CardApplication instance to be added to profile</span><br><span style="color: hsl(0, 100%, 40%);">-        """</span><br><span style="color: hsl(0, 100%, 40%);">-        self.applications.append(app)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def interpret_sw(self, sw:str):</span><br><span style="color: hsl(0, 100%, 40%);">-        """Interpret a given status word within the profile.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        Args:</span><br><span style="color: hsl(0, 100%, 40%);">-            sw : Status word as string of 4 hex digits</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        Returns:</span><br><span style="color: hsl(0, 100%, 40%);">-            Tuple of two strings</span><br><span style="color: hsl(0, 100%, 40%);">-        """</span><br><span style="color: hsl(0, 100%, 40%);">-        return interpret_sw(self.sw, sw)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    def decode_select_response(self, data_hex:str) -> Any:</span><br><span style="color: hsl(0, 100%, 40%);">-        """Decode the response to a SELECT command.</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        This is the fall-back method which doesn't perform any decoding. It mostly</span><br><span style="color: hsl(0, 100%, 40%);">-        exists so specific derived classes can overload it for actual decoding.</span><br><span style="color: hsl(0, 100%, 40%);">-        This method is implemented in the profile and is only used when application</span><br><span style="color: hsl(0, 100%, 40%);">-        specific decoding cannot be performed (no ADF is selected).</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        Args:</span><br><span style="color: hsl(0, 100%, 40%);">-         data_hex: Hex string of the select response</span><br><span style="color: hsl(0, 100%, 40%);">-        """</span><br><span style="color: hsl(0, 100%, 40%);">-        return data_hex</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> </span><br><span> class CardModel(abc.ABC):</span><br><span>     """A specific card model, typically having some additional vendor-specific files. All</span><br><span>diff --git a/pySim/profile.py b/pySim/profile.py</span><br><span>new file mode 100644</span><br><span>index 0000000..f068d7c</span><br><span>--- /dev/null</span><br><span>+++ b/pySim/profile.py</span><br><span>@@ -0,0 +1,148 @@</span><br><span style="color: hsl(120, 100%, 40%);">+# -*- coding: utf-8 -*-</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+""" pySim: tell old 2G SIMs apart from UICC</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# (C) 2021 by Sysmocom s.f.m.c. GmbH</span><br><span style="color: hsl(120, 100%, 40%);">+# All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# This program is free software: you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+# it under the terms of the GNU General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+# the Free Software Foundation, either version 2 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+# (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+# but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+# GNU General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+# You should have received a copy of the GNU General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+# along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+#</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.commands import SimCardCommands</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.filesystem import CardApplication, interpret_sw</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.utils import all_subclasses</span><br><span style="color: hsl(120, 100%, 40%);">+from typing import Any</span><br><span style="color: hsl(120, 100%, 40%);">+import abc</span><br><span style="color: hsl(120, 100%, 40%);">+import operator</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def _mf_select_test(scc:SimCardCommands, cla_byte:str, sel_ctrl:str) -> bool:</span><br><span style="color: hsl(120, 100%, 40%);">+       cla_byte_bak = scc.cla_byte</span><br><span style="color: hsl(120, 100%, 40%);">+   sel_ctrl_bak = scc.sel_ctrl</span><br><span style="color: hsl(120, 100%, 40%);">+   scc.reset_card()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    scc.cla_byte = cla_byte</span><br><span style="color: hsl(120, 100%, 40%);">+       scc.sel_ctrl = sel_ctrl</span><br><span style="color: hsl(120, 100%, 40%);">+       rc = True</span><br><span style="color: hsl(120, 100%, 40%);">+     try:</span><br><span style="color: hsl(120, 100%, 40%);">+          scc.select_file('3f00')</span><br><span style="color: hsl(120, 100%, 40%);">+       except:</span><br><span style="color: hsl(120, 100%, 40%);">+               rc = False</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  scc.reset_card()</span><br><span style="color: hsl(120, 100%, 40%);">+      scc.cla_byte = cla_byte_bak</span><br><span style="color: hsl(120, 100%, 40%);">+   scc.sel_ctrl = sel_ctrl_bak</span><br><span style="color: hsl(120, 100%, 40%);">+   return rc</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def match_uicc(scc:SimCardCommands) -> bool:</span><br><span style="color: hsl(120, 100%, 40%);">+  """ Try to access MF via UICC APDUs (3GPP TS 102.221), if this works, the</span><br><span style="color: hsl(120, 100%, 40%);">+      card is considered a UICC card.</span><br><span style="color: hsl(120, 100%, 40%);">+       """</span><br><span style="color: hsl(120, 100%, 40%);">+    return _mf_select_test(scc, "00", "0004")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def match_sim(scc:SimCardCommands) -> bool:</span><br><span style="color: hsl(120, 100%, 40%);">+       """ Try to access MF via 2G APDUs (3GPP TS 11.11), if this works, the card</span><br><span style="color: hsl(120, 100%, 40%);">+     is also a simcard. This will be the case for most UICC cards, but there may</span><br><span style="color: hsl(120, 100%, 40%);">+   also be plain UICC cards without 2G support as well.</span><br><span style="color: hsl(120, 100%, 40%);">+  """</span><br><span style="color: hsl(120, 100%, 40%);">+    return _mf_select_test(scc, "a0", "0000")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CardProfile(object):</span><br><span style="color: hsl(120, 100%, 40%);">+   """A Card Profile describes a card, it's filesystem hierarchy, an [initial] list of</span><br><span style="color: hsl(120, 100%, 40%);">+        applications as well as profile-specific SW and shell commands.  Every card has</span><br><span style="color: hsl(120, 100%, 40%);">+       one card profile, but there may be multiple applications within that profile."""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     def __init__(self, name, **kw):</span><br><span style="color: hsl(120, 100%, 40%);">+               """</span><br><span style="color: hsl(120, 100%, 40%);">+            Args:</span><br><span style="color: hsl(120, 100%, 40%);">+                 desc (str) : Description</span><br><span style="color: hsl(120, 100%, 40%);">+                      files_in_mf : List of CardEF instances present in MF</span><br><span style="color: hsl(120, 100%, 40%);">+                  applications : List of CardApplications present on card</span><br><span style="color: hsl(120, 100%, 40%);">+                       sw : List of status word definitions</span><br><span style="color: hsl(120, 100%, 40%);">+                  shell_cmdsets : List of cmd2 shell command sets of profile-specific commands</span><br><span style="color: hsl(120, 100%, 40%);">+                  cla : class byte that should be used with cards of this profile</span><br><span style="color: hsl(120, 100%, 40%);">+                       sel_ctrl : selection control bytes class byte that should be used with cards of this profile</span><br><span style="color: hsl(120, 100%, 40%);">+          """</span><br><span style="color: hsl(120, 100%, 40%);">+            self.name = name</span><br><span style="color: hsl(120, 100%, 40%);">+              self.desc = kw.get("desc", None)</span><br><span style="color: hsl(120, 100%, 40%);">+            self.files_in_mf = kw.get("files_in_mf", [])</span><br><span style="color: hsl(120, 100%, 40%);">+                self.sw = kw.get("sw", {})</span><br><span style="color: hsl(120, 100%, 40%);">+          self.applications = kw.get("applications", [])</span><br><span style="color: hsl(120, 100%, 40%);">+              self.shell_cmdsets = kw.get("shell_cmdsets", [])</span><br><span style="color: hsl(120, 100%, 40%);">+            self.cla = kw.get("cla", "00")</span><br><span style="color: hsl(120, 100%, 40%);">+            self.sel_ctrl = kw.get("sel_ctrl", "0004")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+            return self.name</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def add_application(self, app:CardApplication):</span><br><span style="color: hsl(120, 100%, 40%);">+               """Add an application to a card profile.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             Args:</span><br><span style="color: hsl(120, 100%, 40%);">+                 app : CardApplication instance to be added to profile</span><br><span style="color: hsl(120, 100%, 40%);">+         """</span><br><span style="color: hsl(120, 100%, 40%);">+            self.applications.append(app)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       def interpret_sw(self, sw:str):</span><br><span style="color: hsl(120, 100%, 40%);">+               """Interpret a given status word within the profile.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         Args:</span><br><span style="color: hsl(120, 100%, 40%);">+                 sw : Status word as string of 4 hex digits</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+                      Tuple of two strings</span><br><span style="color: hsl(120, 100%, 40%);">+          """</span><br><span style="color: hsl(120, 100%, 40%);">+            return interpret_sw(self.sw, sw)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode_select_response(self, data_hex:str) -> Any:</span><br><span style="color: hsl(120, 100%, 40%);">+             """Decode the response to a SELECT command.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          This is the fall-back method which doesn't perform any decoding. It mostly</span><br><span style="color: hsl(120, 100%, 40%);">+                exists so specific derived classes can overload it for actual decoding.</span><br><span style="color: hsl(120, 100%, 40%);">+               This method is implemented in the profile and is only used when application</span><br><span style="color: hsl(120, 100%, 40%);">+           specific decoding cannot be performed (no ADF is selected).</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+         Args:</span><br><span style="color: hsl(120, 100%, 40%);">+                 data_hex: Hex string of the select response</span><br><span style="color: hsl(120, 100%, 40%);">+           """</span><br><span style="color: hsl(120, 100%, 40%);">+            return data_hex</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     @staticmethod</span><br><span style="color: hsl(120, 100%, 40%);">+ @abc.abstractmethod</span><br><span style="color: hsl(120, 100%, 40%);">+   def match_with_card(scc:SimCardCommands) -> bool:</span><br><span style="color: hsl(120, 100%, 40%);">+          """Check if the specific profile matches the card. This method is a</span><br><span style="color: hsl(120, 100%, 40%);">+            placeholder that is overloaded by specific dirived classes. The method</span><br><span style="color: hsl(120, 100%, 40%);">+                actively probes the card to make sure the profile class matches the</span><br><span style="color: hsl(120, 100%, 40%);">+           physical card. This usually also means that the card is reset during</span><br><span style="color: hsl(120, 100%, 40%);">+          the process, so this method must not be called at random times. It may</span><br><span style="color: hsl(120, 100%, 40%);">+                only be called on startup.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          Args:</span><br><span style="color: hsl(120, 100%, 40%);">+                 scc: SimCardCommands class</span><br><span style="color: hsl(120, 100%, 40%);">+            Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+                      match = True, no match = False</span><br><span style="color: hsl(120, 100%, 40%);">+                """</span><br><span style="color: hsl(120, 100%, 40%);">+            return False</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        @staticmethod</span><br><span style="color: hsl(120, 100%, 40%);">+ def pick(scc:SimCardCommands):</span><br><span style="color: hsl(120, 100%, 40%);">+                profiles = list(all_subclasses(CardProfile))</span><br><span style="color: hsl(120, 100%, 40%);">+          profiles.sort(key=operator.attrgetter('ORDER'))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             for p in profiles:</span><br><span style="color: hsl(120, 100%, 40%);">+                    if p.match_with_card(scc):</span><br><span style="color: hsl(120, 100%, 40%);">+                            return p()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          return None</span><br><span>diff --git a/pySim/ts_102_221.py b/pySim/ts_102_221.py</span><br><span>index 61e236e..53cd118 100644</span><br><span>--- a/pySim/ts_102_221.py</span><br><span>+++ b/pySim/ts_102_221.py</span><br><span>@@ -23,6 +23,13 @@</span><br><span> from pySim.utils import *</span><br><span> from pySim.filesystem import *</span><br><span> from bidict import bidict</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.profile import CardProfile</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.profile import match_uicc</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.profile import match_sim</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# A UICC will usually also support 2G functionality. If this is the case, we</span><br><span style="color: hsl(120, 100%, 40%);">+# need to add DF_GSM and DF_TELECOM along with the UICC related files</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.ts_51_011 import DF_GSM, DF_TELECOM</span><br><span> </span><br><span> ts_102_22x_cmdset = CardCommandSet('TS 102 22x', [</span><br><span>     # TS 102 221 Section 10.1.2 Table 10.5 "Coding of Instruction Byte"</span><br><span>@@ -603,9 +610,11 @@</span><br><span>         addl_info = FlagsEnum(Byte, req_inc_idle_current=1, support_uicc_suspend=2)</span><br><span>         self._construct = Struct('max_current_mA'/Int8ub, 't_op_s'/Int8ub, 'addl_info'/addl_info)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> class CardProfileUICC(CardProfile):</span><br><span style="color: hsl(0, 100%, 40%);">-    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ORDER = 1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, name = 'UICC'):</span><br><span>         files = [</span><br><span>             EF_DIR(),</span><br><span>             EF_ICCID(),</span><br><span>@@ -683,7 +692,27 @@</span><br><span>             },</span><br><span>           }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        super().__init__('UICC', desc='ETSI TS 102 221', cla="00", sel_ctrl="0004", files_in_mf=files, sw=sw)</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(name, desc='ETSI TS 102 221', cla="00", sel_ctrl="0004", files_in_mf=files, sw=sw)</span><br><span> </span><br><span>     def decode_select_response(self, data_hex:str) -> Any:</span><br><span>         return decode_select_response(data_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    @staticmethod</span><br><span style="color: hsl(120, 100%, 40%);">+    def match_with_card(scc:SimCardCommands) -> bool:</span><br><span style="color: hsl(120, 100%, 40%);">+        return match_uicc(scc)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class CardProfileUICCSIM(CardProfileUICC):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Same as above, but including 2G SIM support"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ORDER = 0</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__('UICC-SIM')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        # Add GSM specific files</span><br><span style="color: hsl(120, 100%, 40%);">+        self.files_in_mf.append(DF_TELECOM())</span><br><span style="color: hsl(120, 100%, 40%);">+        self.files_in_mf.append(DF_GSM())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    @staticmethod</span><br><span style="color: hsl(120, 100%, 40%);">+    def match_with_card(scc:SimCardCommands) -> bool:</span><br><span style="color: hsl(120, 100%, 40%);">+        return match_uicc(scc) and match_sim(scc)</span><br><span>diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py</span><br><span>index 73d569e..393277b 100644</span><br><span>--- a/pySim/ts_51_011.py</span><br><span>+++ b/pySim/ts_51_011.py</span><br><span>@@ -332,6 +332,8 @@</span><br><span> import enum</span><br><span> </span><br><span> from pySim.filesystem import *</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.profile import CardProfile</span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.profile import match_sim</span><br><span> </span><br><span> ######################################################################</span><br><span> # DF.TELECOM</span><br><span>@@ -975,6 +977,9 @@</span><br><span>     return ret</span><br><span> </span><br><span> class CardProfileSIM(CardProfile):</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ORDER = 2</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>     def __init__(self):</span><br><span>         sw = {</span><br><span>           'Normal': {</span><br><span>@@ -1016,3 +1021,7 @@</span><br><span>         super().__init__('SIM', desc='GSM SIM Card', cla="a0", sel_ctrl="0000", files_in_mf=[DF_TELECOM(), DF_GSM()], sw=sw)</span><br><span>     def decode_select_response(self, data_hex:str) -> Any:</span><br><span>       return _decode_select_response(data_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    @staticmethod</span><br><span style="color: hsl(120, 100%, 40%);">+    def match_with_card(scc:SimCardCommands) -> bool:</span><br><span style="color: hsl(120, 100%, 40%);">+        return match_sim(scc)</span><br><span>diff --git a/pySim/utils.py b/pySim/utils.py</span><br><span>index 521abd6..e30c970 100644</span><br><span>--- a/pySim/utils.py</span><br><span>+++ b/pySim/utils.py</span><br><span>@@ -1530,3 +1530,7 @@</span><br><span>         if cla and not cmd.match_cla(cla):</span><br><span>             return None</span><br><span>         return cmd</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def all_subclasses(cls) -> set:</span><br><span style="color: hsl(120, 100%, 40%);">+  """Recursively get all subclasses of a specified class"""</span><br><span style="color: hsl(120, 100%, 40%);">+       return set(cls.__subclasses__()).union([s for c in cls.__subclasses__() for s in all_subclasses(c)])</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/pysim/+/26165">change 26165</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/pysim/+/26165"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: pysim </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: If090d32551145f75c644657b90085a3ef5bfa691 </div>
<div style="display:none"> Gerrit-Change-Number: 26165 </div>
<div style="display:none"> Gerrit-PatchSet: 9 </div>
<div style="display:none"> Gerrit-Owner: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: daniel <dwillmann@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: osmith <osmith@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>