dexter has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/26043 )
Change subject: pySim-shell: add method to probe for UICC ...................................................................... pySim-shell: add method to probe for UICC UICC and old SIM cards can be difficult to tell apart without prior knowledge of the card. The ATR won't tell if the card is UICC or not. The only remaining option is to try out if the card is able to handle UICC APDUs. The same is true for 2G SIM cards. It is not guranteed that every UICC card will have 2G functionality. Lets add functionality that probes the card type and then decide which profile is suitable. Change-Id: I535bef35847140e611d4fa95ed2859ee81cce605 Related: OS#5274 --- M pySim-shell.py M pySim/filesystem.py A pySim/probe.py M pySim/ts_102_221.py M pySim/ts_51_011.py 5 files changed, 119 insertions(+), 12 deletions(-) git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/43/26043/1 diff --git a/pySim-shell.py b/pySim-shell.py index 3fc5859..555ddac 100755 --- a/pySim-shell.py +++ b/pySim-shell.py @@ -58,6 +58,8 @@ from pySim.card_key_provider import CardKeyProviderCsv, card_key_provider_register, card_key_provider_get_field +from pySim.probe import probe_card + def init_card(sl): """ Detect card in reader and setup card profile and runtime state. This @@ -79,19 +81,36 @@ card = card_detect("auto", scc) if card is None: - print("Could not detect card type!") - return None, None; + print("Warning: Could not detect card type - assuming a generic card type...") + card = SimCard(scc); + + card_is_uicc = probe_card(card, ['UICC']) + card_is_sim = probe_card(card, ['SIM']) + + if card_is_uicc: + if card_is_sim: + print("Info: Card is of type SIM and UICC") + else: + print("Info: Card is of type UICC") + profile = CardProfileUICC() + profile.add_application(CardApplicationUSIM()) + profile.add_application(CardApplicationISIM()) + elif card_is_sim: + print("Info: Card is of type SIM") + profile = CardProfileSIM() + else: + print("Unsupported card type!") + return None, None # Create runtime state with card profile - profile = CardProfileUICC() - profile.add_application(CardApplicationUSIM()) - profile.add_application(CardApplicationISIM()) rs = RuntimeState(card, profile) - # FIXME: do this dynamically - rs.mf.add_file(DF_TELECOM()) - rs.mf.add_file(DF_GSM()) - rs.mf.add_file(DF_EIRENE()) + # The card is an UICC, but can also be used a 2G SIM, so we must add the SIM + # SIM related bits manaully because they are not part of the UICC profile + if card_is_uicc and card_is_sim: + rs.mf.add_file(DF_TELECOM()) + rs.mf.add_file(DF_GSM()) + rs.mf.add_file(DF_EIRENE()) CardModel.apply_matching_models(scc, rs) diff --git a/pySim/filesystem.py b/pySim/filesystem.py index dcc2608..bf4025c 100644 --- a/pySim/filesystem.py +++ b/pySim/filesystem.py @@ -1033,6 +1033,11 @@ self.card = card self.selected_file = self.mf # type: CardDF self.profile = profile + + # make sure the class and selection control bytes, which are specified + # by the card profile are used + self.card.set_apdu_parameter(cla=self.profile.cla, sel_ctrl=self.profile.sel_ctrl) + # add application ADFs + MF-files from profile apps = self._match_applications() for a in apps: @@ -1042,11 +1047,22 @@ self.mf.add_file(f) self.conserve_write = True + # make sure that when the runtime state is created, the card is also + # in a defined state. + self.reset() + def _match_applications(self): """match the applications from the profile with applications on the card""" apps_profile = self.profile.applications - aids_card = self.card.read_aids() apps_taken = [] + + # When the profile does not feature any applications, then we are done already + if not apps_profile: + return [] + + # Read AIDs from card and match them against the applications defined by the + # card profile + aids_card = self.card.read_aids() if aids_card: aids_taken = [] print("AIDs on card:") @@ -1407,6 +1423,8 @@ applications : List of CardApplications present on card sw : List of status word definitions shell_cmdsets : List of cmd2 shell command sets of profile-specific commands + cla : class byte that should be used with cards of this profile + sel_ctrl : selection control bytes class byte that should be used with cards of this profile """ self.name = name self.desc = kw.get("desc", None) @@ -1414,6 +1432,8 @@ self.sw = kw.get("sw", []) self.applications = kw.get("applications", []) self.shell_cmdsets = kw.get("shell_cmdsets", []) + self.cla = kw.get("cla", "00") + self.sel_ctrl = kw.get("sel_ctrl", "0004") def __str__(self): return self.name diff --git a/pySim/probe.py b/pySim/probe.py new file mode 100644 index 0000000..4348c21 --- /dev/null +++ b/pySim/probe.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +""" pySim: tell old 2G SIMs apart from UICC +""" + +# +# (C) 2021 by Sysmocom s.f.m.c. GmbH +# All Rights Reserved +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +from pySim.cards import SimCard + +def _test_ts_11_11_apdu(card:SimCard) -> bool: + """ Try to access MF via 2G APDUs (3GPP TS 11.11) """ + card.set_apdu_parameter(cla="a0", sel_ctrl="0000") + card.reset() + try: + card._scc.select_file('3f00') + except: + return False + + return True + +def _test_uicc_apdu(card:SimCard) -> bool: + """ Try to access MF via UICC APDUs """ + card.set_apdu_parameter(cla="00", sel_ctrl="0004") + card.reset() + try: + card._scc.select_file('3f00') + except: + return False + + return True + +def probe_card(card:SimCard, card_is:list=['SIM','UICC']) -> bool: + """ Find out whether the card is of type UICC """ + + rc = [] + + # back up CLA byte and SEL control bytes + cla_byte_bak, sel_ctrl_bak = card.get_apdu_parameter() + + if 'SIM' in card_is: + rc.append(_test_ts_11_11_apdu(card)) + if 'UICC' in card_is: + rc.append(_test_uicc_apdu(card)) + + # Leave with a defined state + card.reset() + card.set_apdu_parameter(cla=cla_byte_bak, sel_ctrl=sel_ctrl_bak) + + if False in rc: + return False + + return True diff --git a/pySim/ts_102_221.py b/pySim/ts_102_221.py index 3665939..403daec 100644 --- a/pySim/ts_102_221.py +++ b/pySim/ts_102_221.py @@ -683,4 +683,4 @@ }, } - super().__init__('UICC', desc='ETSI TS 102 221', files_in_mf=files, sw=sw) + super().__init__('UICC', desc='ETSI TS 102 221', cla="00", sel_ctrl="0004", files_in_mf=files, sw=sw) diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py index 743c14b..fa70661 100644 --- a/pySim/ts_51_011.py +++ b/pySim/ts_51_011.py @@ -982,4 +982,4 @@ class CardProfileSIM(CardProfile): def __init__(self): - super().__init__('SIM', desc='GSM SIM Card', files_in_mf=[DF_TELECOM(), DF_GSM()]) + super().__init__('SIM', desc='GSM SIM Card', cla="a0", sel_ctrl="0000", files_in_mf=[DF_TELECOM(), DF_GSM()]) -- To view, visit https://gerrit.osmocom.org/c/pysim/+/26043 To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: I535bef35847140e611d4fa95ed2859ee81cce605 Gerrit-Change-Number: 26043 Gerrit-PatchSet: 1 Gerrit-Owner: dexter <pma...@sysmocom.de> Gerrit-MessageType: newchange