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