Harald Welte | de4c14c | 2022-07-16 11:53:59 +0200 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | |
| 3 | # without this, pylint will fail when inner classes are used |
| 4 | # within the 'nested' kwarg of our TlvMeta metaclass on python 3.7 :( |
| 5 | # pylint: disable=undefined-variable |
| 6 | |
| 7 | """ |
Harald Welte | 650f612 | 2022-07-17 21:42:50 +0200 | [diff] [blame] | 8 | DF_PHONEBOOK, DF_MULTIMEDIA, DF_MCS as specified in 3GPP TS 31.102 V16.6.0 |
Harald Welte | de4c14c | 2022-07-16 11:53:59 +0200 | [diff] [blame] | 9 | Needs to be a separate python module to avoid cyclic imports |
| 10 | """ |
| 11 | |
| 12 | # |
| 13 | # Copyright (C) 2022 Harald Welte <laforge@osmocom.org> |
| 14 | # |
| 15 | # This program is free software: you can redistribute it and/or modify |
| 16 | # it under the terms of the GNU General Public License as published by |
| 17 | # the Free Software Foundation, either version 2 of the License, or |
| 18 | # (at your option) any later version. |
| 19 | # |
| 20 | # This program is distributed in the hope that it will be useful, |
| 21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 23 | # GNU General Public License for more details. |
| 24 | # |
| 25 | # You should have received a copy of the GNU General Public License |
| 26 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 27 | # |
| 28 | |
| 29 | from pySim.tlv import * |
| 30 | from pySim.filesystem import * |
| 31 | from pySim.construct import * |
| 32 | from construct import Optional as COptional |
| 33 | from construct import * |
| 34 | |
Harald Welte | 6f8a870 | 2022-07-17 21:50:31 +0200 | [diff] [blame] | 35 | # TS 31.102 Section 4.2.8 |
| 36 | class EF_UServiceTable(TransparentEF): |
| 37 | def __init__(self, fid, sfid, name, desc, size, table, **kwargs): |
| 38 | super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, **kwargs) |
| 39 | self.table = table |
| 40 | |
| 41 | @staticmethod |
| 42 | def _bit_byte_offset_for_service(service: int) -> Tuple[int, int]: |
| 43 | i = service - 1 |
| 44 | byte_offset = i//8 |
| 45 | bit_offset = (i % 8) |
| 46 | return (byte_offset, bit_offset) |
| 47 | |
| 48 | def _decode_bin(self, in_bin): |
| 49 | ret = {} |
| 50 | for i in range(0, len(in_bin)): |
| 51 | byte = in_bin[i] |
| 52 | for bitno in range(0, 8): |
| 53 | service_nr = i * 8 + bitno + 1 |
| 54 | ret[service_nr] = { |
| 55 | 'activated': True if byte & (1 << bitno) else False |
| 56 | } |
| 57 | if service_nr in self.table: |
| 58 | ret[service_nr]['description'] = self.table[service_nr] |
| 59 | return ret |
| 60 | |
| 61 | def _encode_bin(self, in_json): |
| 62 | # compute the required binary size |
| 63 | bin_len = 0 |
| 64 | for srv in in_json.keys(): |
| 65 | service_nr = int(srv) |
| 66 | (byte_offset, bit_offset) = EF_UServiceTable._bit_byte_offset_for_service( |
| 67 | service_nr) |
| 68 | if byte_offset >= bin_len: |
| 69 | bin_len = byte_offset+1 |
| 70 | # encode the actual data |
| 71 | out = bytearray(b'\x00' * bin_len) |
| 72 | for srv in in_json.keys(): |
| 73 | service_nr = int(srv) |
| 74 | (byte_offset, bit_offset) = EF_UServiceTable._bit_byte_offset_for_service( |
| 75 | service_nr) |
| 76 | if in_json[srv]['activated'] == True: |
| 77 | bit = 1 |
| 78 | else: |
| 79 | bit = 0 |
| 80 | out[byte_offset] |= (bit) << bit_offset |
| 81 | return out |
| 82 | |
| 83 | def get_active_services(self, cmd): |
| 84 | # obtain list of currently active services |
| 85 | (service_data, sw) = cmd.lchan.read_binary_dec() |
| 86 | active_services = [] |
| 87 | for s in service_data.keys(): |
| 88 | if service_data[s]['activated']: |
| 89 | active_services.append(s) |
| 90 | return active_services |
| 91 | |
| 92 | def ust_service_check(self, cmd): |
| 93 | """Check consistency between services of this file and files present/activated""" |
| 94 | num_problems = 0 |
| 95 | # obtain list of currently active services |
| 96 | active_services = self.get_active_services(cmd) |
| 97 | # iterate over all the service-constraints we know of |
| 98 | files_by_service = self.parent.files_by_service |
| 99 | try: |
| 100 | for s in sorted(files_by_service.keys()): |
| 101 | active_str = 'active' if s in active_services else 'inactive' |
| 102 | cmd.poutput("Checking service No %u (%s)" % (s, active_str)) |
| 103 | for f in files_by_service[s]: |
| 104 | should_exist = f.should_exist_for_services(active_services) |
| 105 | try: |
| 106 | cmd.lchan.select_file(f) |
| 107 | sw = None |
| 108 | exists = True |
| 109 | except SwMatchError as e: |
| 110 | sw = str(e) |
| 111 | exists = False |
| 112 | if exists != should_exist: |
| 113 | num_problems += 1 |
| 114 | if exists: |
| 115 | cmd.perror(" ERROR: File %s is selectable but should not!" % f) |
| 116 | else: |
| 117 | cmd.perror(" ERROR: File %s is not selectable (%s) but should!" % (f, sw)) |
| 118 | finally: |
| 119 | # re-select the EF.UST |
| 120 | cmd.lchan.select_file(self) |
| 121 | return num_problems |
| 122 | |
| 123 | |
| 124 | |
Harald Welte | de4c14c | 2022-07-16 11:53:59 +0200 | [diff] [blame] | 125 | # TS 31.102 Section 4.4.2.1 |
| 126 | class EF_PBR(LinFixedEF): |
| 127 | def __init__(self, fid='4F30', name='EF.PBR', desc='Phone Book Reference', **kwargs): |
| 128 | super().__init__(fid, name=name, desc=desc, **kwargs) |
| 129 | #self._tlv = FIXME |
| 130 | |
| 131 | # TS 31.102 Section 4.4.2.12.2 |
| 132 | class EF_PSC(TransparentEF): |
| 133 | _construct = Struct('synce_counter'/Int32ub) |
| 134 | def __init__(self, fid='4F22', name='EF.PSC', desc='Phone Book Synchronization Counter', **kwargs): |
| 135 | super().__init__(fid, name=name, desc=desc, **kwargs) |
| 136 | #self._tlv = FIXME |
| 137 | |
| 138 | # TS 31.102 Section 4.4.2.12.3 |
| 139 | class EF_CC(TransparentEF): |
| 140 | _construct = Struct('change_counter'/Int16ub) |
| 141 | def __init__(self, fid='4F23', name='EF.CC', desc='Change Counter', **kwargs): |
| 142 | super().__init__(fid, name=name, desc=desc, **kwargs) |
| 143 | |
| 144 | # TS 31.102 Section 4.4.2.12.4 |
| 145 | class EF_PUID(TransparentEF): |
| 146 | _construct = Struct('previous_uid'/Int16ub) |
| 147 | def __init__(self, fid='4F24', name='EF.PUID', desc='Previous Unique Identifer', **kwargs): |
| 148 | super().__init__(fid, name=name, desc=desc, **kwargs) |
| 149 | |
| 150 | # TS 31.102 Section 4.4.2 |
| 151 | class DF_PHONEBOOK(CardDF): |
| 152 | def __init__(self, fid='5F3A', name='DF.PHONEBOOK', desc='Phonebook', **kwargs): |
| 153 | super().__init__(fid=fid, name=name, desc=desc, **kwargs) |
| 154 | files = [ |
| 155 | EF_PBR(), |
| 156 | EF_PSC(), |
| 157 | EF_CC(), |
| 158 | EF_PUID(), |
| 159 | # FIXME: Those 4Fxx entries with unspecified FID... |
| 160 | ] |
| 161 | self.add_files(files) |
Harald Welte | a045221 | 2022-07-17 21:23:21 +0200 | [diff] [blame] | 162 | |
| 163 | |
| 164 | |
| 165 | # TS 31.102 Section 4.6.3.1 |
| 166 | class EF_MML(BerTlvEF): |
| 167 | def __init__(self, fid='4F47', name='EF.MML', desc='Multimedia Messages List', **kwargs): |
| 168 | super().__init__(fid, name=name, desc=desc, **kwargs) |
| 169 | |
| 170 | # TS 31.102 Section 4.6.3.2 |
| 171 | class EF_MMDF(BerTlvEF): |
| 172 | def __init__(self, fid='4F48', name='EF.MMDF', desc='Multimedia Messages Data File', **kwargs): |
| 173 | super().__init__(fid, name=name, desc=desc, **kwargs) |
| 174 | |
| 175 | class DF_MULTIMEDIA(CardDF): |
| 176 | def __init__(self, fid='5F3B', name='DF.MULTIMEDIA', desc='Multimedia', **kwargs): |
| 177 | super().__init__(fid=fid, name=name, desc=desc, **kwargs) |
| 178 | files = [ |
| 179 | EF_MML(), |
| 180 | EF_MMDF(), |
| 181 | ] |
| 182 | self.add_files(files) |
Harald Welte | 650f612 | 2022-07-17 21:42:50 +0200 | [diff] [blame] | 183 | |
| 184 | |
| 185 | # TS 31.102 Section 4.6.4.1 |
| 186 | EF_MST_map = { |
| 187 | 1: 'MCPTT UE configuration data', |
| 188 | 2: 'MCPTT User profile data', |
| 189 | 3: 'MCS Group configuration data', |
| 190 | 4: 'MCPTT Service configuration data', |
| 191 | 5: 'MCS UE initial configuration data', |
| 192 | 6: 'MCData UE configuration data', |
| 193 | 7: 'MCData user profile data', |
| 194 | 8: 'MCData service configuration data', |
| 195 | 9: 'MCVideo UE configuration data', |
| 196 | 10: 'MCVideo user profile data', |
| 197 | 11: 'MCVideo service configuration data', |
| 198 | } |
| 199 | |
| 200 | # TS 31.102 Section 4.6.4.2 |
| 201 | class EF_MCS_CONFIG(BerTlvEF): |
| 202 | class McpttUeConfigurationData(BER_TLV_IE, tag=0x80): |
| 203 | pass |
| 204 | class McpttUserProfileData(BER_TLV_IE, tag=0x81): |
| 205 | pass |
| 206 | class McsGroupConfigurationData(BER_TLV_IE, tag=0x82): |
| 207 | pass |
| 208 | class McpttServiceConfigurationData(BER_TLV_IE, tag=0x83): |
| 209 | pass |
| 210 | class McsUeInitialConfigurationData(BER_TLV_IE, tag=0x84): |
| 211 | pass |
| 212 | class McdataUeConfigurationData(BER_TLV_IE, tag=0x85): |
| 213 | pass |
| 214 | class McdataUserProfileData(BER_TLV_IE, tag=0x86): |
| 215 | pass |
| 216 | class McdataServiceConfigurationData(BER_TLV_IE, tag=0x87): |
| 217 | pass |
| 218 | class McvideoUeConfigurationData(BER_TLV_IE, tag=0x88): |
| 219 | pass |
| 220 | class McvideoUserProfileData(BER_TLV_IE, tag=0x89): |
| 221 | pass |
| 222 | class McvideoServiceConfigurationData(BER_TLV_IE, tag=0x8a): |
| 223 | pass |
| 224 | class McsConfigDataCollection(TLV_IE_Collection, nested=[McpttUeConfigurationData, |
| 225 | McpttUserProfileData, McsGroupConfigurationData, |
| 226 | McpttServiceConfigurationData, McsUeInitialConfigurationData, |
| 227 | McdataUeConfigurationData, McdataUserProfileData, |
| 228 | McdataServiceConfigurationData, McvideoUeConfigurationData, |
| 229 | McvideoUserProfileData, McvideoServiceConfigurationData]): |
| 230 | pass |
| 231 | def __init__(self, fid='4F02', sfid=0x02, name='EF.MCS_CONFIG', desc='MCS configuration data', **kwargs): |
| 232 | super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs) |
| 233 | self._tlv = EF_MCS_CONFIG.McsConfigDataCollection |
| 234 | |
| 235 | # TS 31.102 Section 4.6.4.1 |
| 236 | class EF_MST(EF_UServiceTable): |
Harald Welte | 13edf30 | 2022-07-21 15:19:23 +0200 | [diff] [blame] | 237 | def __init__(self, fid='4F01', sfid=0x01, name='EF.MST', desc='MCS Service Table', size=(2,2), |
Harald Welte | 650f612 | 2022-07-17 21:42:50 +0200 | [diff] [blame] | 238 | table=EF_MST_map, **kwargs): |
| 239 | super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, table=table) |
| 240 | |
| 241 | class DF_MCS(CardDF): |
| 242 | def __init__(self, fid='5F3D', name='DF.MCS', desc='Mission Critical Services', **kwargs): |
| 243 | super().__init__(fid=fid, name=name, desc=desc, **kwargs) |
| 244 | files = [ |
| 245 | EF_MST(), |
| 246 | EF_MCS_CONFIG(), |
| 247 | ] |
| 248 | self.add_files(files) |
Harald Welte | 228ae8e | 2022-07-17 22:01:04 +0200 | [diff] [blame] | 249 | |
| 250 | |
| 251 | # TS 31.102 Section 4.6.5.2 |
| 252 | EF_VST_map = { |
| 253 | 1: 'MCPTT UE configuration data', |
| 254 | 2: 'MCPTT User profile data', |
| 255 | 3: 'MCS Group configuration data', |
| 256 | 4: 'MCPTT Service configuration data', |
| 257 | 5: 'MCS UE initial configuration data', |
| 258 | 6: 'MCData UE configuration data', |
| 259 | 7: 'MCData user profile data', |
| 260 | 8: 'MCData service configuration data', |
| 261 | 9: 'MCVideo UE configuration data', |
| 262 | 10: 'MCVideo user profile data', |
| 263 | 11: 'MCVideo service configuration data', |
| 264 | } |
| 265 | |
| 266 | # TS 31.102 Section 4.6.5.2 |
| 267 | class EF_VST(EF_UServiceTable): |
Harald Welte | 13edf30 | 2022-07-21 15:19:23 +0200 | [diff] [blame] | 268 | def __init__(self, fid='4F01', sfid=0x01, name='EF.VST', desc='V2X Service Table', size=(2,2), |
Harald Welte | 228ae8e | 2022-07-17 22:01:04 +0200 | [diff] [blame] | 269 | table=EF_VST_map, **kwargs): |
| 270 | super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, size=size, table=table) |
| 271 | |
| 272 | # TS 31.102 Section 4.6.5.3 |
| 273 | class EF_V2X_CONFIG(BerTlvEF): |
| 274 | class V2xConfigurationData(BER_TLV_IE, tag=0x80): |
| 275 | pass |
| 276 | class V2xConfigDataCollection(TLV_IE_Collection, nested=[V2xConfigurationData]): |
| 277 | pass |
| 278 | def __init__(self, fid='4F02', sfid=0x02, name='EF.V2X_CONFIG', desc='V2X configuration data', **kwargs): |
| 279 | super().__init__(fid=fid, sfid=sfid, name=name, desc=desc, **kwargs) |
| 280 | self._tlv = EF_V2X_CONFIG.V2xConfigDataCollection |
| 281 | |
| 282 | # TS 31.102 Section 4.6.5 |
| 283 | class DF_V2X(CardDF): |
| 284 | def __init__(self, fid='5F3E', name='DF.V2X', desc='Vehicle to X', **kwargs): |
| 285 | super().__init__(fid=fid, name=name, desc=desc, **kwargs) |
| 286 | files = [ |
| 287 | EF_VST(), |
| 288 | EF_V2X_CONFIG(), |
| 289 | ] |
| 290 | self.add_files(files) |