<p>dexter has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/pysim/+/26165">View Change</a></p><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>5 files changed, 167 insertions(+), 7 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/65/26165/1</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 0be6f21..7941329 100755</span><br><span>--- a/pySim-shell.py</span><br><span>+++ b/pySim-shell.py</span><br><span>@@ -47,6 +47,7 @@</span><br><span> from pySim.filesystem import CardMF, RuntimeState, CardDF, CardADF, CardModel</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.gsm_r import DF_EIRENE</span><br><span>@@ -58,6 +59,8 @@</span><br><span> </span><br><span> from pySim.card_key_provider import CardKeyProviderCsv, card_key_provider_register, card_key_provider_get_field</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+from pySim.profile import profile_detect</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def init_card(sl):</span><br><span> """</span><br><span> Detect card in reader and setup card profile and runtime state. This</span><br><span>@@ -79,18 +82,31 @@</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 = profile_detect(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 type(profile) is CardProfileUICC or type(profile) is CardProfileUICCSIM:</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%);">+</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> 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 throught,</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 75cd210..df99bc7 100644</span><br><span>--- a/pySim/filesystem.py</span><br><span>+++ b/pySim/filesystem.py</span><br><span>@@ -1505,6 +1505,21 @@</span><br><span> """</span><br><span> return data_hex</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ @staticmethod</span><br><span style="color: hsl(120, 100%, 40%);">+ def match_witch_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> </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..78f5e23</span><br><span>--- /dev/null</span><br><span>+++ b/pySim/profile.py</span><br><span>@@ -0,0 +1,39 @@</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 CardProfile</span><br><span style="color: hsl(120, 100%, 40%);">+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 style="color: hsl(120, 100%, 40%);">+from pySim.ts_51_011 import CardProfileSIM</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# In order for autodetection ...</span><br><span style="color: hsl(120, 100%, 40%);">+_profile_classes = [ CardProfileUICCSIM, CardProfileUICC, CardProfileSIM ]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def profile_detect(scc:SimCardCommands):</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for p in _profile_classes:</span><br><span style="color: hsl(120, 100%, 40%);">+ if p.match_witch_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 f86a8b3..b1d5e37 100644</span><br><span>--- a/pySim/ts_102_221.py</span><br><span>+++ b/pySim/ts_102_221.py</span><br><span>@@ -24,6 +24,10 @@</span><br><span> from pySim.filesystem import *</span><br><span> from bidict import bidict</span><br><span> </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 style="color: hsl(120, 100%, 40%);">+</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> CardCommand('SELECT', 0xA4, ['0X', '4X', '6X']),</span><br><span>@@ -454,6 +458,48 @@</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(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%);">+ 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 = "00"</span><br><span style="color: hsl(120, 100%, 40%);">+ scc.sel_ctrl = "0004"</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_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%);">+ 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 = "a0"</span><br><span style="color: hsl(120, 100%, 40%);">+ scc.sel_ctrl = "0000"</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> </span><br><span> class CardProfileUICC(CardProfile):</span><br><span> def __init__(self):</span><br><span>@@ -538,3 +584,20 @@</span><br><span> </span><br><span> def decode_select_response(self, data_hex:str) -> Any:</span><br><span> return pySim.ts_102_221_select.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_witch_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%);">+ def __init__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+ super().__init__()</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_witch_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 c146a79..c2c1711 100644</span><br><span>--- a/pySim/ts_51_011.py</span><br><span>+++ b/pySim/ts_51_011.py</span><br><span>@@ -974,8 +974,35 @@</span><br><span> </span><br><span> return ret</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+def _match_witch_card(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</span><br><span style="color: hsl(120, 100%, 40%);">+ card is considered a SIM card. This test will also succeed on UICC</span><br><span style="color: hsl(120, 100%, 40%);">+ cards that also have SIM card functionality for compatibility with</span><br><span style="color: hsl(120, 100%, 40%);">+ older MS</span><br><span style="color: hsl(120, 100%, 40%);">+ """</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 = "a0"</span><br><span style="color: hsl(120, 100%, 40%);">+ scc.sel_ctrl = "0000"</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> class CardProfileSIM(CardProfile):</span><br><span> def __init__(self):</span><br><span> super().__init__('SIM', desc='GSM SIM Card', cla="a0", sel_ctrl="0000", files_in_mf=[DF_TELECOM(), DF_GSM()])</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_witch_card(scc:SimCardCommands) -> bool:</span><br><span style="color: hsl(120, 100%, 40%);">+ return _match_witch_card(scc)</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: 1 </div>
<div style="display:none"> Gerrit-Owner: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>