# -*- coding: utf-8 -*- # without this, pylint will fail when inner classes are used # within the 'nested' kwarg of our TlvMeta metaclass on python 3.7 :( # pylint: disable=undefined-variable """ DF_PHONEBOOK, DF_MULTIMEDIA, DF_MCS as specified in 3GPP TS 31.102 V16.6.0 Needs to be a separate python module to avoid cyclic imports """ # # Copyright (C) 2022 Harald Welte # # 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 . # from construct import Optional as COptional from construct import Struct, Int16ub, Int32ub from osmocom.tlv import * from osmocom.construct import * from pySim.filesystem import * # TS 31.102 Section 4.2.8 class EF_UServiceTable(TransparentEF): def __init__(self, fid, sfid, name, desc, size, table, **kwargs): super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) self.table = table @staticmethod def _bit_byte_offset_for_service(service: int) -> Tuple[int, int]: i = service - 1 byte_offset = i//8 bit_offset = i % 8 return (byte_offset, bit_offset) def _decode_bin(self, in_bin): ret = {} for i in range(0, len(in_bin)): byte = in_bin[i] for bitno in range(0, 8): service_nr = i * 8 + bitno + 1 ret[service_nr] = { 'activated': True if byte & (1 << bitno) else False } if service_nr in self.table: ret[service_nr]['description'] = self.table[service_nr] return ret def _encode_bin(self, in_json, **kwargs): # compute the required binary size bin_len = 0 for srv in in_json.keys(): service_nr = int(srv) (byte_offset, bit_offset) = EF_UServiceTable._bit_byte_offset_for_service( service_nr) if byte_offset >= bin_len: bin_len = byte_offset+1 # encode the actual data out = bytearray(b'\x00' * bin_len) for srv in in_json.keys(): service_nr = int(srv) (byte_offset, bit_offset) = EF_UServiceTable._bit_byte_offset_for_service( service_nr) if in_json[srv]['activated'] is True: bit = 1 else: bit = 0 out[byte_offset] |= (bit) << bit_offset return out def get_active_services(self, cmd): # obtain list of currently active services (service_data, _sw) = cmd.lchan.read_binary_dec() active_services = [] for s in service_data.keys(): if service_data[s]['activated']: active_services.append(s) return active_services def ust_service_check(self, cmd): """Check consistency between services of this file and files present/activated""" num_problems = 0 # obtain list of currently active services active_services = self.get_active_services(cmd) # iterate over all the service-constraints we know of files_by_service = self.parent.files_by_service try: for s in sorted(files_by_service.keys()): active_str = 'active' if s in active_services else 'inactive' cmd.poutput("Checking service No %u (%s)" % (s, active_str)) for f in files_by_service[s]: should_exist = f.should_exist_for_services(active_services) try: cmd.lchan.select_file(f) sw = None exists = True except SwMatchError as e: sw = str(e) exists = False if exists != should_exist: num_problems += 1 if exists: cmd.perror(" ERROR: File %s is selectable but should not!" % f) else: cmd.perror(" ERROR: File %s is not selectable (%s) but should!" % (f, sw)) finally: # re-select the EF.UST cmd.lchan.select_file(self) return num_problems def ust_update(self, cmd, activate=[], deactivate=[]): service_data, _sw = cmd.lchan.read_binary() service_data = h2b(service_data) for service in activate: nbyte, nbit = EF_UServiceTable._bit_byte_offset_for_service(service) if nbyte > len(service_data): missing = nbyte - service_data service_data.extend(missing * "00") service_data[nbyte] |= (1 << nbit) for service in deactivate: nbyte, nbit = EF_UServiceTable._bit_byte_offset_for_service(service) if nbyte > len(service_data): missing = nbyte - service_data service_data.extend(missing * "00") service_data[nbyte] &= ~(1 << nbit) service_data = b2h(service_data) cmd.lchan.update_binary(service_data) # TS 31.102 Section 4.4.2.1 class EF_PBR(LinFixedEF): # TODO: a80ac0034f3a02c5034f0904aa0acb034f3d07c2034f4a06 def __init__(self, fid='4F30', name='EF.PBR', desc='Phone Book Reference', **kwargs): super().__init__(fid, name=name, desc=desc, **kwargs) #self._tlv = FIXME # TS 31.102 Section 4.4.2.12.2 class EF_PSC(TransparentEF): _construct = Struct('synce_counter'/Int32ub) def __init__(self, fid='4F22', name='EF.PSC', desc='Phone Book Synchronization Counter', **kwargs): super().__init__(fid, name=name, desc=desc, **kwargs) #self._tlv = FIXME # TS 31.102 Section 4.4.2.12.3 class EF_CC(TransparentEF): _construct = Struct('change_counter'/Int16ub) def __init__(self, fid='4F23', name='EF.CC', desc='Change Counter', **kwargs): super().__init__(fid, name=name, desc=desc, **kwargs) # TS 31.102 Section 4.4.2.12.4 class EF_PUID(TransparentEF): _construct = Struct('previous_uid'/Int16ub) def __init__(self, fid='4F24', name='EF.PUID', desc='Previous Unique Identifer', **kwargs): super().__init__(fid, name=name, desc=desc, **kwargs) # TS 31.102 Section 4.4.2 class DF_PHONEBOOK(CardDF): def __init__(self, fid='5F3A', name='DF.PHONEBOOK', desc='Phonebook', **kwargs): super().__init__(fid=fid, name=name, desc=desc, **kwargs) files = [ EF_PBR(), EF_PSC(), EF_CC(), EF_PUID(), # FIXME: Those 4Fxx entries with unspecified FID... ] self.add_files(files) # TS 31.102 Section 4.6.3.1 class EF_MML(BerTlvEF): def __init__(self, fid='4F47', name='EF.MML', desc='Multimedia Messages List', **kwargs): super().__init__(fid, name=name, desc=desc, **kwargs) # TS 31.102 Section 4.6.3.2 class EF_MMDF(BerTlvEF): def __init__(self, fid='4F48', name='EF.MMDF', desc='Multimedia Messages Data File', **kwargs): super().__init__(fid, name=name, desc=desc, **kwargs) class DF_MULTIMEDIA(CardDF): def __init__(self, fid='5F3B', name='DF.MULTIMEDIA', desc='Multimedia', **kwargs): super().__init__(fid=fid, name=name, desc=desc, **kwargs) files = [ EF_MML(), EF_MMDF(), ] self.add_files(files) # TS 31.102 Section 4.6.4.1 EF_MST_map = { 1: 'MCPTT UE configuration data', 2: 'MCPTT User profile data', 3: 'MCS Group configuration data', 4: 'MCPTT Service configuration data', 5: 'MCS UE initial configuration data', 6: 'MCData UE configuration data', 7: 'MCData user profile data', 8: 'MCData service configuration data', 9: 'MCVideo UE configuration data', 10: 'MCVideo user profile data', 11: 'MCVideo service configuration data', } # TS 31.102 Section 4.6.4.2 class EF_MCS_CONFIG(BerTlvEF): class McpttUeConfigurationData(BER_TLV_IE, tag=0x80): pass class McpttUserProfileData(BER_TLV_IE, tag=0x81): pass class McsGroupConfigurationData(BER_TLV_IE, tag=0x82): pass class McpttServiceConfigurationData(BER_TLV_IE, tag=0x83): pass class McsUeInitialConfigurationData(BER_TLV_IE, tag=0x84): pass class McdataUeConfigurationData(BER_TLV_IE, tag=0x85): pass class McdataUserProfileData(BER_TLV_IE, tag=0x86): pass class McdataServiceConfigurationData(BER_TLV_IE, tag=0x87): pass class McvideoUeConfigurationData(BER_TLV_IE, tag=0x88): pass class McvideoUserProfileData(BER_TLV_IE, tag=0x89): pass class McvideoServiceConfigurationData(BER_TLV_IE, tag=0x8a): pass class McsConfigDataCollection(TLV_IE_Collection, nested=[McpttUeConfigurationData, McpttUserProfileData, McsGroupConfigurationData, McpttServiceConfigurationData, McsUeInitialConfigurationData, McdataUeConfigurationData, McdataUserProfileData, McdataServiceConfigurationData, McvideoUeConfigurationData, McvideoUserProfileData, McvideoServiceConfigurationData]): pass def __init__(self, fid='4F02', sfid=0x02, name='EF.MCS_CONFIG', desc='MCS configuration data', **kwargs): super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs) self._tlv = EF_MCS_CONFIG.McsConfigDataCollection # TS 31.102 Section 4.6.4.1 class EF_MST(EF_UServiceTable): def __init__(self, fid='4F01', sfid=0x01, name='EF.MST', desc='MCS Service Table', size=(2,2), table=EF_MST_map, **kwargs): super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, table=table) class DF_MCS(CardDF): def __init__(self, fid='5F3D', name='DF.MCS', desc='Mission Critical Services', **kwargs): super().__init__(fid=fid, name=name, desc=desc, **kwargs) files = [ EF_MST(), EF_MCS_CONFIG(), ] self.add_files(files) # TS 31.102 Section 4.6.5.2 EF_VST_map = { 1: 'MCPTT UE configuration data', 2: 'MCPTT User profile data', 3: 'MCS Group configuration data', 4: 'MCPTT Service configuration data', 5: 'MCS UE initial configuration data', 6: 'MCData UE configuration data', 7: 'MCData user profile data', 8: 'MCData service configuration data', 9: 'MCVideo UE configuration data', 10: 'MCVideo user profile data', 11: 'MCVideo service configuration data', } # TS 31.102 Section 4.6.5.2 class EF_VST(EF_UServiceTable): def __init__(self, fid='4F01', sfid=0x01, name='EF.VST', desc='V2X Service Table', size=(2,2), table=EF_VST_map, **kwargs): super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, table=table) # TS 31.102 Section 4.6.5.3 class EF_V2X_CONFIG(BerTlvEF): class V2xConfigurationData(BER_TLV_IE, tag=0x80): pass class V2xConfigDataCollection(TLV_IE_Collection, nested=[V2xConfigurationData]): pass def __init__(self, fid='4F02', sfid=0x02, name='EF.V2X_CONFIG', desc='V2X configuration data', **kwargs): super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs) self._tlv = EF_V2X_CONFIG.V2xConfigDataCollection # TS 31.102 Section 4.6.5 class DF_V2X(CardDF): def __init__(self, fid='5F3E', name='DF.V2X', desc='Vehicle to X', **kwargs): super().__init__(fid=fid, name=name, desc=desc, **kwargs) files = [ EF_VST(), EF_V2X_CONFIG(), ] self.add_files(files)