filesystem: fix decode_select_response

There are some problems with the usage of decode_select_response. At the
moment the ADF files overload the related method to provide decoding of
the select responses as per 3gpp TS 102 221. However, this also means
that the decoder is only available under ADF.USIM and ADF.ISIM. DF.GSM
and DF.TELECOM also overload the decoder method, just like an ADF would
do. This decoding method is then implemented as per 3gpp TS 51 011.
Since this a a problem on UICCs, the method detects the magic byte 0x62
that can be found at the beginning on every select response of an UICC
to defer to the TS 102 221 decoding method. TS 51 011 defines the first
two bytes of the select response as RFU. This at least problematic.

To solve this there should be a default method for
decode_select_response in the profile, which can be used if no file
overloads it with a specific decoder. ADFs use specific decoders, but
everything else should use the default decoder. When we deal with an
UICC, we expect the select response to be consistantly conform to TS
102 221, if we deal with a clasic sim we expect responses as per TS 51
011 only.

Since it is still possible to replace the select response decoder we
still have the opportunity to have custom select response in cartain
DFs and ADFs should we need them.

Change-Id: I95e33ec1755727dc9bbbc6016ce2d99a9e66f214
Related: OS#5274
This commit is contained in:
Philipp Maier
2021-11-04 12:48:41 +01:00
parent 9764de202b
commit 5af7bdf5c7
4 changed files with 60 additions and 18 deletions

View File

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

View File

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

View File

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

View File

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