diff --git a/pySim/filesystem.py b/pySim/filesystem.py index dcc26081..3caa470d 100644 --- a/pySim/filesystem.py +++ b/pySim/filesystem.py @@ -52,7 +52,7 @@ class CardFile(object): RESERVED_FIDS = ['3f00'] def __init__(self, fid:str=None, sfid:str=None, name:str=None, desc:str=None, - parent:Optional['CardDF']=None): + parent:Optional['CardDF']=None, profile:Optional['CardProfile']=None): """ Args: fid : File Identifier (4 hex digits) @@ -60,6 +60,7 @@ class CardFile(object): name : Brief name of the file, lik EF_ICCID desc : Description of the file parent : Parent CardFile object within filesystem hierarchy + profile : Card profile that this file should be part of """ if not isinstance(self, CardADF) and fid == None: raise ValueError("fid is mandatory") @@ -72,6 +73,7 @@ class CardFile(object): self.parent = parent if self.parent and self.parent != self and self.fid: self.parent.add_file(self) + self.profile = profile self.shell_commands = [] # type: List[CommandSet] # Note: the basic properties (fid, name, ect.) are verified when @@ -173,10 +175,34 @@ class CardFile(object): return list(sels.keys()) def decode_select_response(self, data_hex:str): - """Decode the response to a SELECT command.""" + """Decode the response to a SELECT command. + + Args: + data_hex: Hex string of the select response + """ + + # When the current file does not implement a custom select response decoder, + # we just ask the parent file to decode the select response. If this method + # is not overloaded by the current file we will again ask the parent file. + # This way we recursively travel up the file system tree until we hit a file + # that does implement a concrete decoder. if self.parent: return self.parent.decode_select_response(data_hex) + def get_profile(self): + """Get the profile associated with this file. If this file does not have any + profile assigned, try to find a file above (usually the MF) in the filesystem + hirarchy that has a profile assigned + """ + + # If we have a profile set, return it + if self.profile: + return self.profile + + # Walk up recursively until we hit a parent that has a profile set + if self.parent: + return self.parent.get_profile() + return None class CardDF(CardFile): """DF (Dedicated File) in the smart card filesystem. Those are basically sub-directories.""" @@ -331,12 +357,18 @@ class CardMF(CardDF): def decode_select_response(self, data_hex:str) -> Any: """Decode the response to a SELECT command. - This is the fall-back method which doesn't perform any decoding. It mostly - exists so specific derived classes can overload it for actual decoding. + This is the fall-back method which automatically defers to the standard decoding + method defined by the card profile. When no profile is set, then no decoding is + performed. Specific derived classes (usually ADF) can overload this method to + install specific decoding. """ - return data_hex + profile = self.get_profile() + if profile: + return profile.decode_select_response(data_hex) + else: + return data_hex class CardADF(CardDF): """ADF (Application Dedicated File) in the smart card filesystem""" @@ -1029,7 +1061,7 @@ class RuntimeState(object): card : pysim.cards.Card instance profile : CardProfile instance """ - self.mf = CardMF() + self.mf = CardMF(profile=profile) self.card = card self.selected_file = self.mf # type: CardDF self.profile = profile @@ -1437,6 +1469,19 @@ class CardProfile(object): """ return interpret_sw(self.sw, sw) + def decode_select_response(self, data_hex:str) -> Any: + """Decode the response to a SELECT command. + + This is the fall-back method which doesn't perform any decoding. It mostly + exists so specific derived classes can overload it for actual decoding. + This method is implemented in the profile and is only used when application + specific decoding cannot be performed (no ADF is selected). + + Args: + data_hex: Hex string of the select response + """ + return data_hex + class CardModel(abc.ABC): """A specific card model, typically having some additional vendor-specific files. All diff --git a/pySim/gsm_r.py b/pySim/gsm_r.py index 7cd7529e..22b88fe6 100644 --- a/pySim/gsm_r.py +++ b/pySim/gsm_r.py @@ -253,6 +253,3 @@ class DF_EIRENE(CardDF): EF_DialledVals(fid='6f87', name='EF.FreeNumber', desc='Free Number Call Type 0 and 8'), ] self.add_files(files) - - def decode_select_response(self, data_hex): - return pySim.ts_51_011.decode_select_response(data_hex) diff --git a/pySim/ts_102_221.py b/pySim/ts_102_221.py index 36659394..3c99c4d9 100644 --- a/pySim/ts_102_221.py +++ b/pySim/ts_102_221.py @@ -684,3 +684,6 @@ class CardProfileUICC(CardProfile): } super().__init__('UICC', desc='ETSI TS 102 221', files_in_mf=files, sw=sw) + + def decode_select_response(self, data_hex:str) -> Any: + return decode_select_response(data_hex) diff --git a/pySim/ts_51_011.py b/pySim/ts_51_011.py index 743c14bd..a00cf0d1 100644 --- a/pySim/ts_51_011.py +++ b/pySim/ts_51_011.py @@ -332,7 +332,6 @@ from pySim.construct import * import enum from pySim.filesystem import * -import pySim.ts_102_221 ###################################################################### # DF.TELECOM @@ -451,9 +450,6 @@ class DF_TELECOM(CardDF): ] self.add_files(files) - def decode_select_response(self, data_hex): - return decode_select_response(data_hex) - ###################################################################### # DF.GSM ###################################################################### @@ -936,13 +932,11 @@ class DF_GSM(CardDF): ] self.add_files(files) - def decode_select_response(self, data_hex): - return decode_select_response(data_hex) -def decode_select_response(resp_hex): + +def _decode_select_response(resp_hex): + resp_bin = h2b(resp_hex) - if resp_bin[0] == 0x62: - return pySim.ts_102_221.decode_select_response(resp_hex) struct_of_file_map = { 0: 'transparent', 1: 'linear_fixed', @@ -983,3 +977,6 @@ def decode_select_response(resp_hex): class CardProfileSIM(CardProfile): def __init__(self): super().__init__('SIM', desc='GSM SIM Card', files_in_mf=[DF_TELECOM(), DF_GSM()]) + + def decode_select_response(self, data_hex:str) -> Any: + return _decode_select_response(data_hex)