blob: db7819c432ab23fdbfa8ad7a26e5c53bd812e9f6 [file] [log] [blame]
Harald Welte2a33ad22021-10-20 10:14:18 +02001# -*- 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"""
8The File (and its derived classes) uses the classes of pySim.filesystem in
9order to describe the files specified in UIC Reference P38 T 9001 5.0 "FFFIS for GSM-R SIM Cards"
10"""
11
12#
13# Copyright (C) 2021 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
30from pySim.utils import *
31#from pySim.tlv import *
32from struct import pack, unpack
33from construct import *
34from construct import Optional as COptional
35from pySim.construct import *
36import enum
37
Harald Welte323a3502023-07-11 17:26:39 +020038from pySim.profile import CardProfileAddon
Harald Welte2a33ad22021-10-20 10:14:18 +020039from pySim.filesystem import *
Harald Welte2a33ad22021-10-20 10:14:18 +020040import pySim.ts_51_011
41
42######################################################################
43# DF.EIRENE (FFFIS for GSM-R SIM Cards)
44######################################################################
45
Harald Weltec91085e2022-02-10 18:05:45 +010046
Harald Welte2a33ad22021-10-20 10:14:18 +020047class FuncNTypeAdapter(Adapter):
48 def _decode(self, obj, context, path):
49 bcd = swap_nibbles(b2h(obj))
Harald Welte8dbf7142023-01-24 14:35:19 +010050 last_digit = int(bcd[-1], 16)
Harald Welte2a33ad22021-10-20 10:14:18 +020051 return {'functional_number': bcd[:-1],
Harald Welte8dbf7142023-01-24 14:35:19 +010052 'presentation_of_only_this_fn': bool(last_digit & 4),
53 'permanent_fn': bool(last_digit & 8)}
Harald Weltec91085e2022-02-10 18:05:45 +010054
Harald Welte2a33ad22021-10-20 10:14:18 +020055 def _encode(self, obj, context, path):
56 return 'FIXME'
57
Harald Weltec91085e2022-02-10 18:05:45 +010058
Harald Welte2a33ad22021-10-20 10:14:18 +020059class EF_FN(LinFixedEF):
60 """Section 7.2"""
Harald Welte865eea62023-01-27 19:26:12 +010061 _test_decode = [
62 ( "40315801000010ff01",
63 { "functional_number_and_type": { "functional_number": "04138510000001f",
64 "presentation_of_only_this_fn": True, "permanent_fn": True }, "list_number": 1 } ),
65 ]
Harald Welte2a33ad22021-10-20 10:14:18 +020066 def __init__(self):
Harald Weltee6d7b142023-01-24 14:27:25 +010067 super().__init__(fid='6ff1', sfid=None, name='EF.FN',
Harald Welte99e4cc02022-07-21 15:25:47 +020068 desc='Functional numbers', rec_len=(9, 9))
Harald Welte2a33ad22021-10-20 10:14:18 +020069 self._construct = Struct('functional_number_and_type'/FuncNTypeAdapter(Bytes(8)),
70 'list_number'/Int8ub)
71
72
73class PlConfAdapter(Adapter):
74 """Section 7.4.3"""
Harald Weltec91085e2022-02-10 18:05:45 +010075
Harald Welte2a33ad22021-10-20 10:14:18 +020076 def _decode(self, obj, context, path):
77 num = int(obj) & 0x7
78 if num == 0:
79 return 'None'
80 elif num == 1:
81 return 4
82 elif num == 2:
83 return 3
84 elif num == 3:
85 return 2
86 elif num == 4:
87 return 1
88 elif num == 5:
89 return 0
Harald Weltec91085e2022-02-10 18:05:45 +010090
Harald Welte2a33ad22021-10-20 10:14:18 +020091 def _encode(self, obj, context, path):
92 if obj == 'None':
93 return 0
94 obj = int(obj)
95 if obj == 4:
96 return 1
97 elif obj == 3:
98 return 2
99 elif obj == 2:
100 return 3
101 elif obj == 1:
102 return 4
103 elif obj == 0:
104 return 5
105
Harald Weltec91085e2022-02-10 18:05:45 +0100106
Harald Welte2a33ad22021-10-20 10:14:18 +0200107class PlCallAdapter(Adapter):
108 """Section 7.4.12"""
Harald Weltec91085e2022-02-10 18:05:45 +0100109
Harald Welte2a33ad22021-10-20 10:14:18 +0200110 def _decode(self, obj, context, path):
111 num = int(obj) & 0x7
112 if num == 0:
113 return 'None'
114 elif num == 1:
115 return 4
116 elif num == 2:
117 return 3
118 elif num == 3:
119 return 2
120 elif num == 4:
121 return 1
122 elif num == 5:
123 return 0
124 elif num == 6:
125 return 'B'
126 elif num == 7:
127 return 'A'
Harald Weltec91085e2022-02-10 18:05:45 +0100128
Harald Welte2a33ad22021-10-20 10:14:18 +0200129 def _encode(self, obj, context, path):
130 if obj == 'None':
131 return 0
132 if obj == 4:
133 return 1
134 elif obj == 3:
135 return 2
136 elif obj == 2:
137 return 3
138 elif obj == 1:
139 return 4
140 elif obj == 0:
141 return 5
142 elif obj == 'B':
143 return 6
144 elif obj == 'A':
145 return 7
146
Harald Weltec91085e2022-02-10 18:05:45 +0100147
148NextTableType = Enum(Byte, decision=0xf0, predefined=0xf1,
149 num_dial_digits=0xf2, ic=0xf3, empty=0xff)
150
Harald Welte2a33ad22021-10-20 10:14:18 +0200151
152class EF_CallconfC(TransparentEF):
153 """Section 7.3"""
Harald Welte865eea62023-01-27 19:26:12 +0100154 _test_de_encode = [
155 ( "026121ffffffffffff1e000a040a010253600795792426f0",
156 { "pl_conf": 3, "conf_nr": "1612ffffffffffff", "max_rand": 30, "n_ack_max": 10,
157 "pl_ack": 1, "n_nested_max": 10, "train_emergency_gid": 1, "shunting_emergency_gid": 2,
158 "imei": "350670599742620f" } ),
159 ]
Harald Welte2a33ad22021-10-20 10:14:18 +0200160 def __init__(self):
Harald Welte13edf302022-07-21 15:19:23 +0200161 super().__init__(fid='6ff2', sfid=None, name='EF.CallconfC', size=(24, 24),
Harald Welte2a33ad22021-10-20 10:14:18 +0200162 desc='Call Configuration of emergency calls Configuration')
163 self._construct = Struct('pl_conf'/PlConfAdapter(Int8ub),
164 'conf_nr'/BcdAdapter(Bytes(8)),
165 'max_rand'/Int8ub,
166 'n_ack_max'/Int16ub,
167 'pl_ack'/PlCallAdapter(Int8ub),
168 'n_nested_max'/Int8ub,
169 'train_emergency_gid'/Int8ub,
170 'shunting_emergency_gid'/Int8ub,
171 'imei'/BcdAdapter(Bytes(8)))
172
Harald Weltec91085e2022-02-10 18:05:45 +0100173
Harald Welte2a33ad22021-10-20 10:14:18 +0200174class EF_CallconfI(LinFixedEF):
175 """Section 7.5"""
Harald Weltec91085e2022-02-10 18:05:45 +0100176
Harald Welte2a33ad22021-10-20 10:14:18 +0200177 def __init__(self):
Harald Welte99e4cc02022-07-21 15:25:47 +0200178 super().__init__(fid='6ff3', sfid=None, name='EF.CallconfI', rec_len=(21, 21),
Harald Welte2a33ad22021-10-20 10:14:18 +0200179 desc='Call Configuration of emergency calls Information')
180 self._construct = Struct('t_dur'/Int24ub,
181 't_relcalc'/Int32ub,
182 'pl_call'/PlCallAdapter(Int8ub),
Harald Weltec91085e2022-02-10 18:05:45 +0100183 'cause' /
184 FlagsEnum(Int8ub, powered_off=1,
185 radio_link_error=2, user_command=5),
Harald Welte2a33ad22021-10-20 10:14:18 +0200186 'gcr'/BcdAdapter(Bytes(4)),
187 'fnr'/BcdAdapter(Bytes(8)))
188
Harald Weltec91085e2022-02-10 18:05:45 +0100189
Harald Welte2a33ad22021-10-20 10:14:18 +0200190class EF_Shunting(TransparentEF):
191 """Section 7.6"""
Harald Welte865eea62023-01-27 19:26:12 +0100192 _test_de_encode = [
193 ( "03f8ffffff000000", { "common_gid": 3, "shunting_gid": "f8ffffff000000" } ),
194 ]
Harald Welte2a33ad22021-10-20 10:14:18 +0200195 def __init__(self):
Harald Weltec91085e2022-02-10 18:05:45 +0100196 super().__init__(fid='6ff4', sfid=None,
Harald Welte13edf302022-07-21 15:19:23 +0200197 name='EF.Shunting', desc='Shunting', size=(8, 8))
Harald Welte2a33ad22021-10-20 10:14:18 +0200198 self._construct = Struct('common_gid'/Int8ub,
Harald Welte3c98d5e2022-07-20 07:40:05 +0200199 'shunting_gid'/HexAdapter(Bytes(7)))
Harald Welte2a33ad22021-10-20 10:14:18 +0200200
Harald Weltec91085e2022-02-10 18:05:45 +0100201
Harald Welte2a33ad22021-10-20 10:14:18 +0200202class EF_GsmrPLMN(LinFixedEF):
203 """Section 7.7"""
Harald Welte865eea62023-01-27 19:26:12 +0100204 _test_de_encode = [
Harald Welte842fbdb2023-12-27 17:06:58 +0100205 ( "22f860f86f8d6f8e01", { "plmn": "228-06", "class_of_network": {
Harald Welte865eea62023-01-27 19:26:12 +0100206 "supported": { "vbs": True, "vgcs": True, "emlpp": True,
207 "fn": True, "eirene": True }, "preference": 0 },
208 "ic_incoming_ref_tbl": "6f8d", "outgoing_ref_tbl": "6f8e",
209 "ic_table_ref": "01" } ),
Harald Welte842fbdb2023-12-27 17:06:58 +0100210 ( "22f810416f8d6f8e02", { "plmn": "228-01", "class_of_network": {
Harald Welte865eea62023-01-27 19:26:12 +0100211 "supported": { "vbs": False, "vgcs": False, "emlpp": False,
212 "fn": True, "eirene": False }, "preference": 1 },
213 "ic_incoming_ref_tbl": "6f8d", "outgoing_ref_tbl": "6f8e",
214 "ic_table_ref": "02" } ),
215 ]
Harald Welte2a33ad22021-10-20 10:14:18 +0200216 def __init__(self):
Harald Weltec91085e2022-02-10 18:05:45 +0100217 super().__init__(fid='6ff5', sfid=None, name='EF.GsmrPLMN',
Harald Welte99e4cc02022-07-21 15:25:47 +0200218 desc='GSM-R network selection', rec_len=(9, 9))
Harald Welte842fbdb2023-12-27 17:06:58 +0100219 self._construct = Struct('plmn'/PlmnAdapter(Bytes(3)),
Harald Welte2a33ad22021-10-20 10:14:18 +0200220 'class_of_network'/BitStruct('supported'/FlagsEnum(BitsInteger(5), vbs=1, vgcs=2, emlpp=4, fn=8, eirene=16),
221 'preference'/BitsInteger(3)),
222 'ic_incoming_ref_tbl'/HexAdapter(Bytes(2)),
223 'outgoing_ref_tbl'/HexAdapter(Bytes(2)),
224 'ic_table_ref'/HexAdapter(Bytes(1)))
225
Harald Weltec91085e2022-02-10 18:05:45 +0100226
Harald Welte2a33ad22021-10-20 10:14:18 +0200227class EF_IC(LinFixedEF):
228 """Section 7.8"""
Harald Welte865eea62023-01-27 19:26:12 +0100229 _test_de_encode = [
230 ( "f06f8e40f10001", { "next_table_type": "decision", "id_of_next_table": "6f8e",
231 "ic_decision_value": "041f", "network_string_table_index": 1 } ),
232 ( "ffffffffffffff", { "next_table_type": "empty", "id_of_next_table": "ffff",
233 "ic_decision_value": "ffff", "network_string_table_index": 65535 } ),
234 ]
Harald Welte2a33ad22021-10-20 10:14:18 +0200235 def __init__(self):
Harald Weltec91085e2022-02-10 18:05:45 +0100236 super().__init__(fid='6f8d', sfid=None, name='EF.IC',
Harald Welte99e4cc02022-07-21 15:25:47 +0200237 desc='International Code', rec_len=(7, 7))
Harald Welte2a33ad22021-10-20 10:14:18 +0200238 self._construct = Struct('next_table_type'/NextTableType,
239 'id_of_next_table'/HexAdapter(Bytes(2)),
240 'ic_decision_value'/BcdAdapter(Bytes(2)),
Harald Welte9181a692023-01-31 16:32:28 +0100241 'network_string_table_index'/Int16ub)
Harald Welte2a33ad22021-10-20 10:14:18 +0200242
Harald Weltec91085e2022-02-10 18:05:45 +0100243
Harald Welte2a33ad22021-10-20 10:14:18 +0200244class EF_NW(LinFixedEF):
245 """Section 7.9"""
Harald Welte865eea62023-01-27 19:26:12 +0100246 _test_de_encode = [
247 ( "47534d2d52204348", "GSM-R CH" ),
248 ( "537769737347534d", "SwissGSM" ),
249 ( "47534d2d52204442", "GSM-R DB" ),
250 ( "47534d2d52524649", "GSM-RRFI" ),
251 ]
Harald Welte2a33ad22021-10-20 10:14:18 +0200252 def __init__(self):
Harald Weltec91085e2022-02-10 18:05:45 +0100253 super().__init__(fid='6f80', sfid=None, name='EF.NW',
Harald Welte99e4cc02022-07-21 15:25:47 +0200254 desc='Network Name', rec_len=(8, 8))
Harald Welte2a33ad22021-10-20 10:14:18 +0200255 self._construct = GsmString(8)
256
Harald Weltec91085e2022-02-10 18:05:45 +0100257
Harald Welte2a33ad22021-10-20 10:14:18 +0200258class EF_Switching(LinFixedEF):
259 """Section 8.4"""
Harald Welte865eea62023-01-27 19:26:12 +0100260 _test_de_encode = [
261 ( "f26f87f0ff00", { "next_table_type": "num_dial_digits", "id_of_next_table": "6f87",
262 "decision_value": "0fff", "string_table_index": 0 } ),
263 ( "f06f8ff1ff01", { "next_table_type": "decision", "id_of_next_table": "6f8f",
264 "decision_value": "1fff", "string_table_index": 1 } ),
265 ( "f16f89f5ff05", { "next_table_type": "predefined", "id_of_next_table": "6f89",
266 "decision_value": "5fff", "string_table_index": 5 } ),
267 ]
268 def __init__(self, fid='1234', name='Switching', desc=None):
Harald Weltec91085e2022-02-10 18:05:45 +0100269 super().__init__(fid=fid, sfid=None,
Harald Welte99e4cc02022-07-21 15:25:47 +0200270 name=name, desc=desc, rec_len=(6, 6))
Harald Welte2a33ad22021-10-20 10:14:18 +0200271 self._construct = Struct('next_table_type'/NextTableType,
272 'id_of_next_table'/HexAdapter(Bytes(2)),
273 'decision_value'/BcdAdapter(Bytes(2)),
274 'string_table_index'/Int8ub)
275
Harald Weltec91085e2022-02-10 18:05:45 +0100276
Harald Welte2a33ad22021-10-20 10:14:18 +0200277class EF_Predefined(LinFixedEF):
278 """Section 8.5"""
Harald Welte865eea62023-01-27 19:26:12 +0100279 _test_de_encode = [
280 ( "f26f85", 1, { "next_table_type": "num_dial_digits", "id_of_next_table": "6f85" } ),
281 ( "f0ffc8", 2, { "predefined_value1": "0fff", "string_table_index1": 200 } ),
282 ]
Harald Welte6bf2d5f2023-01-24 15:54:26 +0100283 # header and other records have different structure. WTF !?!
284 construct_first = Struct('next_table_type'/NextTableType,
285 'id_of_next_table'/HexAdapter(Bytes(2)))
Harald Welte73a7fea2023-01-24 16:52:29 +0100286 construct_others = Struct('predefined_value1'/BcdAdapter(Bytes(2)),
Harald Welte6bf2d5f2023-01-24 15:54:26 +0100287 'string_table_index1'/Int8ub)
Harald Weltec91085e2022-02-10 18:05:45 +0100288
Harald Welte865eea62023-01-27 19:26:12 +0100289 def __init__(self, fid='1234', name='Predefined', desc=None):
Harald Weltec91085e2022-02-10 18:05:45 +0100290 super().__init__(fid=fid, sfid=None,
Harald Welte99e4cc02022-07-21 15:25:47 +0200291 name=name, desc=desc, rec_len=(3, 3))
Harald Welte2a33ad22021-10-20 10:14:18 +0200292
Harald Welte6bf2d5f2023-01-24 15:54:26 +0100293 def _decode_record_bin(self, raw_bin_data : bytes, record_nr : int) -> dict:
294 if record_nr == 1:
295 return parse_construct(self.construct_first, raw_bin_data)
296 else:
297 return parse_construct(self.construct_others, raw_bin_data)
298
299 def _encode_record_bin(self, abstract_data : dict, record_nr : int) -> bytearray:
300 r = None
301 if record_nr == 1:
302 r = self.construct_first.build(abstract_data)
303 else:
304 r = self.construct_others.build(abstract_data)
305 return filter_dict(r)
Harald Weltec91085e2022-02-10 18:05:45 +0100306
Harald Welte2a33ad22021-10-20 10:14:18 +0200307class EF_DialledVals(TransparentEF):
308 """Section 8.6"""
Harald Welte865eea62023-01-27 19:26:12 +0100309 _test_de_encode = [
310 ( "ffffff22", { "next_table_type": "empty", "id_of_next_table": "ffff", "dialed_digits": "22" } ),
311 ( "f16f8885", { "next_table_type": "predefined", "id_of_next_table": "6f88", "dialed_digits": "58" }),
312 ]
313 def __init__(self, fid='1234', name='DialledVals', desc=None):
Harald Welte13edf302022-07-21 15:19:23 +0200314 super().__init__(fid=fid, sfid=None, name=name, desc=desc, size=(4, 4))
Harald Welte2a33ad22021-10-20 10:14:18 +0200315 self._construct = Struct('next_table_type'/NextTableType,
316 'id_of_next_table'/HexAdapter(Bytes(2)),
317 'dialed_digits'/BcdAdapter(Bytes(1)))
318
319
320class DF_EIRENE(CardDF):
321 def __init__(self, fid='7fe0', name='DF.EIRENE', desc='GSM-R EIRENE'):
322 super().__init__(fid=fid, name=name, desc=desc)
323 files = [
324 # Section 7.1.6 / Table 10 EIRENE GSM EFs
325 EF_FN(),
326 EF_CallconfC(),
327 EF_CallconfI(),
328 EF_Shunting(),
329 EF_GsmrPLMN(),
330 EF_IC(),
331 EF_NW(),
332
333 # support of the numbering plan
334 EF_Switching(fid='6f8e', name='EF.CT', desc='Call Type'),
335 EF_Switching(fid='6f8f', name='EF.SC', desc='Short Code'),
336 EF_Predefined(fid='6f88', name='EF.FC', desc='Function Code'),
Harald Weltec91085e2022-02-10 18:05:45 +0100337 EF_Predefined(fid='6f89', name='EF.Service',
338 desc='VGCS/VBS Service Code'),
339 EF_Predefined(fid='6f8a', name='EF.Call',
340 desc='First digit of the group ID'),
341 EF_Predefined(fid='6f8b', name='EF.FctTeam',
342 desc='Call Type 6 Team Type + Team member function'),
343 EF_Predefined(fid='6f92', name='EF.Controller',
344 desc='Call Type 7 Controller function code'),
345 EF_Predefined(fid='6f8c', name='EF.Gateway',
346 desc='Access to external networks'),
347 EF_DialledVals(fid='6f81', name='EF.5to8digits',
348 desc='Call Type 2 User Identity Number length'),
349 EF_DialledVals(fid='6f82', name='EF.2digits',
350 desc='2 digits input'),
351 EF_DialledVals(fid='6f83', name='EF.8digits',
352 desc='8 digits input'),
353 EF_DialledVals(fid='6f84', name='EF.9digits',
354 desc='9 digits input'),
355 EF_DialledVals(fid='6f85', name='EF.SSSSS',
356 desc='Group call area input'),
357 EF_DialledVals(fid='6f86', name='EF.LLLLL',
358 desc='Location number Call Type 6'),
359 EF_DialledVals(fid='6f91', name='EF.Location',
360 desc='Location number Call Type 7'),
361 EF_DialledVals(fid='6f87', name='EF.FreeNumber',
362 desc='Free Number Call Type 0 and 8'),
363 ]
Harald Welte2a33ad22021-10-20 10:14:18 +0200364 self.add_files(files)
Harald Welte323a3502023-07-11 17:26:39 +0200365
366
367class AddonGSMR(CardProfileAddon):
368 """An Addon that can be found on either classic GSM SIM or on UICC to support GSM-R."""
369 def __init__(self):
370 files = [
371 DF_EIRENE()
372 ]
373 super().__init__('GSM-R', desc='Railway GSM', files_in_mf=files)
374
375 def probe(self, card: 'CardBase') -> bool:
376 return card.file_exists(self.files_in_mf[0].fid)