filesystem: Maintain a 'service' attribute for all files on a card

This can be populated by card profiles with the SST/IST/UST service
that is associated with the file.

Change-Id: I3b3f74b691368fa09967ecb377a9f7a6d8af7869
This commit is contained in:
Harald Welte
2022-02-11 21:54:37 +01:00
parent afb8d3f925
commit 9170fbf08d

View File

@@ -34,7 +34,7 @@ import cmd2
from cmd2 import CommandSet, with_default_category, with_argparser from cmd2 import CommandSet, with_default_category, with_argparser
import argparse import argparse
from typing import cast, Optional, Iterable, List, Dict, Tuple from typing import cast, Optional, Iterable, List, Dict, Tuple, Union
from smartcard.util import toBytes from smartcard.util import toBytes
@@ -44,6 +44,10 @@ from pySim.exceptions import *
from pySim.jsonpath import js_path_find, js_path_modify from pySim.jsonpath import js_path_find, js_path_modify
from pySim.commands import SimCardCommands from pySim.commands import SimCardCommands
# int: a single service is associated with this file
# list: any of the listed services requires this file
# tuple: logical-and of the listed services requires this file
CardFileService = Union[int, List[int], Tuple[int, ...]]
class CardFile(object): class CardFile(object):
"""Base class for all objects in the smart card filesystem. """Base class for all objects in the smart card filesystem.
@@ -53,7 +57,8 @@ class CardFile(object):
RESERVED_FIDS = ['3f00'] RESERVED_FIDS = ['3f00']
def __init__(self, fid: str = None, sfid: str = None, name: str = None, desc: str = None, def __init__(self, fid: str = None, sfid: str = None, name: str = None, desc: str = None,
parent: Optional['CardDF'] = None, profile: Optional['CardProfile'] = None): parent: Optional['CardDF'] = None, profile: Optional['CardProfile'] = None,
service: Optional[CardFileService] = None):
""" """
Args: Args:
fid : File Identifier (4 hex digits) fid : File Identifier (4 hex digits)
@@ -62,6 +67,7 @@ class CardFile(object):
desc : Description of the file desc : Description of the file
parent : Parent CardFile object within filesystem hierarchy parent : Parent CardFile object within filesystem hierarchy
profile : Card profile that this file should be part of profile : Card profile that this file should be part of
service : Service (SST/UST/IST) associated with the file
""" """
if not isinstance(self, CardADF) and fid == None: if not isinstance(self, CardADF) and fid == None:
raise ValueError("fid is mandatory") raise ValueError("fid is mandatory")
@@ -75,6 +81,7 @@ class CardFile(object):
if self.parent and self.parent != self and self.fid: if self.parent and self.parent != self and self.fid:
self.parent.add_file(self) self.parent.add_file(self)
self.profile = profile self.profile = profile
self.service = service
self.shell_commands = [] # type: List[CommandSet] self.shell_commands = [] # type: List[CommandSet]
# Note: the basic properties (fid, name, ect.) are verified when # Note: the basic properties (fid, name, ect.) are verified when
@@ -207,6 +214,28 @@ class CardFile(object):
return self.parent.get_profile() return self.parent.get_profile()
return None return None
def should_exist_for_services(self, services: List[int]):
"""Assuming the provided list of activated services, should this file exist and be activated?."""
if self.service is None:
return None
elif isinstance(self.service, int):
# a single service determines the result
return self.service in services
elif isinstance(self.service, list):
# any of the services active -> true
for s in self.service:
if s in services:
return True
return False
elif isinstance(self.service, tuple):
# all of the services active -> true
for s in self.service:
if not s in services:
return False
return True
else:
raise ValueError("self.service must be either int or list or tuple")
class CardDF(CardFile): class CardDF(CardFile):
"""DF (Dedicated File) in the smart card filesystem. Those are basically sub-directories.""" """DF (Dedicated File) in the smart card filesystem. Those are basically sub-directories."""
@@ -223,10 +252,27 @@ class CardDF(CardFile):
super().__init__(**kwargs) super().__init__(**kwargs)
self.children = dict() self.children = dict()
self.shell_commands = [self.ShellCommands()] self.shell_commands = [self.ShellCommands()]
# dict of CardFile affected by service(int), indexed by service
self.files_by_service = {}
def __str__(self): def __str__(self):
return "DF(%s)" % (super().__str__()) return "DF(%s)" % (super().__str__())
def _add_file_services(self, child):
"""Add a child (DF/EF) to the files_by_services of the parent."""
if not child.service:
return
if isinstance(child.service, int):
self.files_by_service.setdefault(child.service, []).append(child)
elif isinstance(child.service, list):
for service in child.service:
self.files_by_service.setdefault(service, []).append(child)
elif isinstance(child.service, tuple):
for service in child.service:
self.files_by_service.setdefault(service, []).append(child)
else:
raise ValueError
def add_file(self, child: CardFile, ignore_existing: bool = False): def add_file(self, child: CardFile, ignore_existing: bool = False):
"""Add a child (DF/EF) to this DF. """Add a child (DF/EF) to this DF.
Args: Args:
@@ -256,6 +302,7 @@ class CardDF(CardFile):
"File with given name %s already exists in %s" % (child.name, self)) "File with given name %s already exists in %s" % (child.name, self))
self.children[child.fid] = child self.children[child.fid] = child
child.parent = self child.parent = self
self._add_file_services(child)
def add_files(self, children: Iterable[CardFile], ignore_existing: bool = False): def add_files(self, children: Iterable[CardFile], ignore_existing: bool = False):
"""Add a list of child (DF/EF) to this DF """Add a list of child (DF/EF) to this DF
@@ -519,7 +566,7 @@ class TransparentEF(CardEF):
self._cmd.poutput_json(data) self._cmd.poutput_json(data)
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None, def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
size={1, None}): size={1, None}, **kwargs):
""" """
Args: Args:
fid : File Identifier (4 hex digits) fid : File Identifier (4 hex digits)
@@ -529,7 +576,7 @@ class TransparentEF(CardEF):
parent : Parent CardFile object within filesystem hierarchy parent : Parent CardFile object within filesystem hierarchy
size : tuple of (minimum_size, recommended_size) size : tuple of (minimum_size, recommended_size)
""" """
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent) super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, **kwargs)
self._construct = None self._construct = None
self._tlv = None self._tlv = None
self.size = size self.size = size
@@ -654,8 +701,8 @@ class LinFixedEF(CardEF):
class ShellCommands(CommandSet): class ShellCommands(CommandSet):
"""Shell commands specific for Linear Fixed EFs.""" """Shell commands specific for Linear Fixed EFs."""
def __init__(self): def __init__(self, **kwargs):
super().__init__() super().__init__(**kwargs)
read_rec_parser = argparse.ArgumentParser() read_rec_parser = argparse.ArgumentParser()
read_rec_parser.add_argument( read_rec_parser.add_argument(
@@ -777,7 +824,7 @@ class LinFixedEF(CardEF):
self._cmd.poutput_json(data) self._cmd.poutput_json(data)
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None,
parent: Optional[CardDF] = None, rec_len={1, None}): parent: Optional[CardDF] = None, rec_len={1, None}, **kwargs):
""" """
Args: Args:
fid : File Identifier (4 hex digits) fid : File Identifier (4 hex digits)
@@ -787,7 +834,7 @@ class LinFixedEF(CardEF):
parent : Parent CardFile object within filesystem hierarchy parent : Parent CardFile object within filesystem hierarchy
rec_len : set of {minimum_length, recommended_length} rec_len : set of {minimum_length, recommended_length}
""" """
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent) super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, **kwargs)
self.rec_len = rec_len self.rec_len = rec_len
self.shell_commands = [self.ShellCommands()] self.shell_commands = [self.ShellCommands()]
self._construct = None self._construct = None
@@ -908,9 +955,8 @@ class CyclicEF(LinFixedEF):
# we don't really have any special support for those; just recycling LinFixedEF here # we don't really have any special support for those; just recycling LinFixedEF here
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None, def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
rec_len={1, None}): rec_len={1, None}, **kwargs):
super().__init__(fid=fid, sfid=sfid, name=name, super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, rec_len=rec_len, **kwargs)
desc=desc, parent=parent, rec_len=rec_len)
class TransRecEF(TransparentEF): class TransRecEF(TransparentEF):
@@ -924,7 +970,7 @@ class TransRecEF(TransparentEF):
""" """
def __init__(self, fid: str, rec_len: int, sfid: str = None, name: str = None, desc: str = None, def __init__(self, fid: str, rec_len: int, sfid: str = None, name: str = None, desc: str = None,
parent: Optional[CardDF] = None, size={1, None}): parent: Optional[CardDF] = None, size={1, None}, **kwargs):
""" """
Args: Args:
fid : File Identifier (4 hex digits) fid : File Identifier (4 hex digits)
@@ -935,7 +981,7 @@ class TransRecEF(TransparentEF):
rec_len : Length of the fixed-length records within transparent EF rec_len : Length of the fixed-length records within transparent EF
size : tuple of (minimum_size, recommended_size) size : tuple of (minimum_size, recommended_size)
""" """
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, size=size) super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, size=size, **kwargs)
self.rec_len = rec_len self.rec_len = rec_len
def decode_record_hex(self, raw_hex_data: str) -> dict: def decode_record_hex(self, raw_hex_data: str) -> dict:
@@ -1112,7 +1158,7 @@ class BerTlvEF(CardEF):
self._cmd.poutput(data) self._cmd.poutput(data)
def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None, def __init__(self, fid: str, sfid: str = None, name: str = None, desc: str = None, parent: CardDF = None,
size={1, None}): size={1, None}, **kwargs):
""" """
Args: Args:
fid : File Identifier (4 hex digits) fid : File Identifier (4 hex digits)
@@ -1122,7 +1168,7 @@ class BerTlvEF(CardEF):
parent : Parent CardFile object within filesystem hierarchy parent : Parent CardFile object within filesystem hierarchy
size : tuple of (minimum_size, recommended_size) size : tuple of (minimum_size, recommended_size)
""" """
super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent) super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, parent=parent, **kwargs)
self._construct = None self._construct = None
self.size = size self.size = size
self.shell_commands = [self.ShellCommands()] self.shell_commands = [self.ShellCommands()]