diff --git a/pySim-shell.py b/pySim-shell.py index 4d4f2e23..b48abc74 100755 --- a/pySim-shell.py +++ b/pySim-shell.py @@ -137,11 +137,6 @@ def init_card(sl): # Create runtime state with card profile rs = RuntimeState(card, profile) - # FIXME: This is an GSM-R related file, it needs to be added throughout, - # the profile. At the moment we add it for all cards, this won't hurt, - # but regular SIM and UICC will not have it and fail to select it. - rs.mf.add_file(DF_EIRENE()) - CardModel.apply_matching_models(scc, rs) # inform the transport that we can do context-specific SW interpretation diff --git a/pySim-trace.py b/pySim-trace.py index 4c8696d4..ba1568cf 100755 --- a/pySim-trace.py +++ b/pySim-trace.py @@ -11,7 +11,7 @@ from pySim.filesystem import RuntimeState from pySim.cards import UiccCardBase from pySim.commands import SimCardCommands from pySim.profile import CardProfile -from pySim.ts_102_221 import CardProfileUICCSIM +from pySim.ts_102_221 import CardProfileUICC from pySim.ts_31_102 import CardApplicationUSIM from pySim.ts_31_103 import CardApplicationISIM from pySim.transport import LinkBase @@ -70,8 +70,9 @@ class DummySimLink(LinkBase): class Tracer: def __init__(self, **kwargs): - # we assume a generic SIM + UICC + USIM + ISIM card - profile = CardProfileUICCSIM() + # we assume a generic UICC profile; as all APDUs return 9000 in DummySimLink above, + # all CardProfileAddon (including SIM) will probe successful. + profile = CardProfileUICC() profile.add_application(CardApplicationUSIM()) profile.add_application(CardApplicationISIM()) scc = SimCardCommands(transport=DummySimLink()) diff --git a/pySim/cdma_ruim.py b/pySim/cdma_ruim.py index 8b664902..b254403f 100644 --- a/pySim/cdma_ruim.py +++ b/pySim/cdma_ruim.py @@ -22,7 +22,7 @@ import enum from pySim.utils import * from pySim.filesystem import * from pySim.profile import match_ruim -from pySim.profile import CardProfile +from pySim.profile import CardProfile, CardProfileAddon from pySim.ts_51_011 import CardProfileSIM from pySim.ts_51_011 import DF_TELECOM, DF_GSM from pySim.ts_51_011 import EF_ServiceTable @@ -191,3 +191,14 @@ class CardProfileRUIM(CardProfile): @staticmethod def match_with_card(scc: SimCardCommands) -> bool: return match_ruim(scc) + +class AddonRUIM(CardProfileAddon): + """An Addon that can be found on on a combined SIM + RUIM or UICC + RUIM to support CDMA.""" + def __init__(self): + files = [ + DF_CDMA() + ] + super().__init__('RUIM', desc='CDMA RUIM', files_in_mf=files) + + def probe(self, card: 'CardBase') -> bool: + return card.file_exists(self.files_in_mf[0].fid) diff --git a/pySim/filesystem.py b/pySim/filesystem.py index 22ff60dc..cb3b403e 100644 --- a/pySim/filesystem.py +++ b/pySim/filesystem.py @@ -1307,6 +1307,16 @@ class RuntimeState: self.card.set_apdu_parameter( cla=self.profile.cla, sel_ctrl=self.profile.sel_ctrl) + for addon_cls in self.profile.addons: + addon = addon_cls() + if addon.probe(self.card): + print("Detected %s Add-on \"%s\"" % (self.profile, addon)) + for f in addon.files_in_mf: + self.mf.add_file(f) + + # go back to MF before the next steps (addon probing might have changed DF) + self.card._scc.select_file('3F00') + # add application ADFs + MF-files from profile apps = self._match_applications() for a in apps: diff --git a/pySim/gsm_r.py b/pySim/gsm_r.py index 389a8cb8..cd111d68 100644 --- a/pySim/gsm_r.py +++ b/pySim/gsm_r.py @@ -35,8 +35,8 @@ from construct import Optional as COptional from pySim.construct import * import enum +from pySim.profile import CardProfileAddon from pySim.filesystem import * -import pySim.ts_102_221 import pySim.ts_51_011 ###################################################################### @@ -362,3 +362,15 @@ class DF_EIRENE(CardDF): desc='Free Number Call Type 0 and 8'), ] self.add_files(files) + + +class AddonGSMR(CardProfileAddon): + """An Addon that can be found on either classic GSM SIM or on UICC to support GSM-R.""" + def __init__(self): + files = [ + DF_EIRENE() + ] + super().__init__('GSM-R', desc='Railway GSM', files_in_mf=files) + + def probe(self, card: 'CardBase') -> bool: + return card.file_exists(self.files_in_mf[0].fid) diff --git a/pySim/profile.py b/pySim/profile.py index e464e1f9..0d09e81b 100644 --- a/pySim/profile.py +++ b/pySim/profile.py @@ -88,6 +88,7 @@ class CardProfile: 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 + addons: List of optional CardAddons that a card of this profile might have """ self.name = name self.desc = kw.get("desc", None) @@ -97,6 +98,8 @@ class CardProfile: self.shell_cmdsets = kw.get("shell_cmdsets", []) self.cla = kw.get("cla", "00") self.sel_ctrl = kw.get("sel_ctrl", "0004") + # list of optional addons that a card of this profile might have + self.addons = kw.get("addons", []) def __str__(self): return self.name @@ -161,3 +164,34 @@ class CardProfile: return p() return None + + def add_addon(self, addon: 'CardProfileAddon'): + assert(addon not in self.addons) + # we don't install any additional files, as that is happening in the RuntimeState. + self.addons.append(addon) + +class CardProfileAddon(abc.ABC): + """A Card Profile Add-on is something that is not a card application or a full stand-alone + card profile, but an add-on to an existing profile. Think of GSM-R specific files existing + on what is otherwise a SIM or USIM+SIM card.""" + + def __init__(self, name: str, **kw): + """ + Args: + desc (str) : Description + files_in_mf : List of CardEF instances present in MF + shell_cmdsets : List of cmd2 shell command sets of profile-specific commands + """ + self.name = name + self.desc = kw.get("desc", None) + self.files_in_mf = kw.get("files_in_mf", []) + self.shell_cmdsets = kw.get("shell_cmdsets", []) + pass + + def __str__(self): + return self.name + + @abc.abstractmethod + def probe(self, card: 'CardBase') -> bool: + """Probe a given card to determine whether or not this add-on is present/supported.""" + pass diff --git a/pySim/ts_102_221.py b/pySim/ts_102_221.py index b6c003b8..df8b8421 100644 --- a/pySim/ts_102_221.py +++ b/pySim/ts_102_221.py @@ -31,7 +31,9 @@ import pySim.iso7816_4 as iso7816_4 # A UICC will usually also support 2G functionality. If this is the case, we # need to add DF_GSM and DF_TELECOM along with the UICC related files -from pySim.ts_51_011 import DF_GSM, DF_TELECOM +from pySim.ts_51_011 import DF_GSM, DF_TELECOM, AddonSIM +from pySim.gsm_r import AddonGSMR +from pySim.cdma_ruim import AddonRUIM ts_102_22x_cmdset = CardCommandSet('TS 102 22x', [ # TS 102 221 Section 10.1.2 Table 10.5 "Coding of Instruction Byte" @@ -768,6 +770,11 @@ class CardProfileUICC(CardProfile): # FIXME: DF.CD EF_UMPC(), ] + addons = [ + AddonSIM, + AddonGSMR, + AddonRUIM, + ] sw = { 'Normal': { '9000': 'Normal ending of the command', @@ -839,7 +846,7 @@ class CardProfileUICC(CardProfile): super().__init__(name, desc='ETSI TS 102 221', cla="00", sel_ctrl="0004", files_in_mf=files, sw=sw, - shell_cmdsets = [self.AddlShellCommands()]) + shell_cmdsets = [self.AddlShellCommands()], addons = addons) @staticmethod def decode_select_response(resp_hex: str) -> object: @@ -895,4 +902,5 @@ class CardProfileUICCSIM(CardProfileUICC): @staticmethod def match_with_card(scc: SimCardCommands) -> bool: - return match_uicc(scc) and match_sim(scc) + # don't ever select this profile, we only use this from pySim-trace + return False diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py index 1f98e724..c81bfdfb 100644 --- a/pySim/ts_51_011.py +++ b/pySim/ts_51_011.py @@ -30,9 +30,10 @@ order to describe the files specified in the relevant ETSI + 3GPP specifications # from pySim.profile import match_sim -from pySim.profile import CardProfile +from pySim.profile import CardProfile, CardProfileAddon from pySim.filesystem import * from pySim.ts_31_102_telecom import DF_PHONEBOOK, DF_MULTIMEDIA, DF_MCS, DF_V2X +from pySim.gsm_r import AddonGSMR import enum from pySim.construct import * from construct import Optional as COptional @@ -1047,8 +1048,12 @@ class CardProfileSIM(CardProfile): }, } + addons = [ + AddonGSMR, + ] + super().__init__('SIM', desc='GSM SIM Card', cla="a0", - sel_ctrl="0000", files_in_mf=[DF_TELECOM(), DF_GSM()], sw=sw) + sel_ctrl="0000", files_in_mf=[DF_TELECOM(), DF_GSM()], sw=sw, addons = addons) @staticmethod def decode_select_response(resp_hex: str) -> object: @@ -1104,3 +1109,17 @@ class CardProfileSIM(CardProfile): @staticmethod def match_with_card(scc: SimCardCommands) -> bool: return match_sim(scc) + + +class AddonSIM(CardProfileAddon): + """An add-on that can be found on a UICC in order to support classic GSM SIM.""" + def __init__(self): + files = [ + DF_GSM(), + DF_TELECOM(), + ] + super().__init__('SIM', desc='GSM SIM', files_in_mf=files) + + def probe(self, card:'CardBase') -> bool: + # we assume the add-on to be present in case DF.GSM is found on the card + return card.file_exists(self.files_in_mf[0].fid)