blob: 75106d8e71651fcfe6c7a76ebe1106733800cf6c [file] [log] [blame]
Sylvain Munaut76504e02010-12-07 00:24:32 +01001# -*- coding: utf-8 -*-
2
3""" pySim: various utilities
4"""
5
6#
7# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21#
22
23
Harald Welteee3501f2021-04-02 13:00:18 +020024def h2b(s: str) -> bytearray:
Harald Welte4f6ca432021-02-01 17:51:56 +010025 """convert from a string of hex nibbles to a sequence of bytes"""
26 return bytearray.fromhex(s)
Sylvain Munaut76504e02010-12-07 00:24:32 +010027
Harald Welteee3501f2021-04-02 13:00:18 +020028def b2h(b: bytearray) -> str:
Harald Welte4f6ca432021-02-01 17:51:56 +010029 """convert from a sequence of bytes to a string of hex nibbles"""
30 return ''.join(['%02x'%(x) for x in b])
Sylvain Munaut76504e02010-12-07 00:24:32 +010031
Harald Welteee3501f2021-04-02 13:00:18 +020032def h2i(s:str):
33 """convert from a string of hex nibbles to a list of integers"""
Sylvain Munaut76504e02010-12-07 00:24:32 +010034 return [(int(x,16)<<4)+int(y,16) for x,y in zip(s[0::2], s[1::2])]
35
Harald Welteee3501f2021-04-02 13:00:18 +020036def i2h(s) -> str:
37 """convert from a list of integers to a string of hex nibbles"""
Sylvain Munaut76504e02010-12-07 00:24:32 +010038 return ''.join(['%02x'%(x) for x in s])
39
Harald Welteee3501f2021-04-02 13:00:18 +020040def h2s(s:str) -> str:
41 """convert from a string of hex nibbles to an ASCII string"""
Vadim Yanitskiyeb06b452020-05-10 02:32:46 +070042 return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])
43 if int(x + y, 16) != 0xff])
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030044
Harald Welteee3501f2021-04-02 13:00:18 +020045def s2h(s:str) -> str:
46 """convert from an ASCII string to a string of hex nibbles"""
Harald Welte4f6ca432021-02-01 17:51:56 +010047 b = bytearray()
48 b.extend(map(ord, s))
49 return b2h(b)
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030050
Supreeth Herle7d77d2d2020-05-11 09:07:08 +020051# List of bytes to string
Harald Welteee3501f2021-04-02 13:00:18 +020052def i2s(s) -> str:
53 """convert from a list of integers to an ASCII string"""
Supreeth Herle7d77d2d2020-05-11 09:07:08 +020054 return ''.join([chr(x) for x in s])
55
Harald Welteee3501f2021-04-02 13:00:18 +020056def swap_nibbles(s:str) -> str:
57 """swap the nibbles in a hex string"""
Sylvain Munaut76504e02010-12-07 00:24:32 +010058 return ''.join([x+y for x,y in zip(s[1::2], s[0::2])])
59
Harald Welteee3501f2021-04-02 13:00:18 +020060def rpad(s:str, l:int, c='f') -> str:
61 """pad string on the right side.
62 Args:
63 s : string to pad
64 l : total length to pad to
65 c : padding character
66 Returns:
67 String 's' padded with as many 'c' as needed to reach total length of 'l'
68 """
Sylvain Munaut76504e02010-12-07 00:24:32 +010069 return s + c * (l - len(s))
70
Harald Welteee3501f2021-04-02 13:00:18 +020071def lpad(s:str, l:int, c='f') -> str:
72 """pad string on the left side.
73 Args:
74 s : string to pad
75 l : total length to pad to
76 c : padding character
77 Returns:
78 String 's' padded with as many 'c' as needed to reach total length of 'l'
79 """
Sylvain Munaut76504e02010-12-07 00:24:32 +010080 return c * (l - len(s)) + s
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040081
Harald Welteee3501f2021-04-02 13:00:18 +020082def half_round_up(n:int) -> int:
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020083 return (n + 1)//2
84
85# IMSI encoded format:
86# For IMSI 0123456789ABCDE:
87#
88# | byte 1 | 2 upper | 2 lower | 3 upper | 3 lower | ... | 9 upper | 9 lower |
89# | length in bytes | 0 | odd/even | 2 | 1 | ... | E | D |
90#
91# If the IMSI is less than 15 characters, it should be padded with 'f' from the end.
92#
93# The length is the total number of bytes used to encoded the IMSI. This includes the odd/even
94# parity bit. E.g. an IMSI of length 14 is 8 bytes long, not 7, as it uses bytes 2 to 9 to
95# encode itself.
96#
97# Because of this, an odd length IMSI fits exactly into len(imsi) + 1 // 2 bytes, whereas an
98# even length IMSI only uses half of the last byte.
99
Harald Welteee3501f2021-04-02 13:00:18 +0200100def enc_imsi(imsi:str):
101 """Converts a string IMSI into the encoded value of the EF"""
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200102 l = half_round_up(len(imsi) + 1) # Required bytes - include space for odd/even indicator
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400103 oe = len(imsi) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200104 ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe<<3)|1, rpad(imsi, 15)))
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400105 return ei
106
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400107def dec_imsi(ef):
108 """Converts an EF value to the imsi string representation"""
109 if len(ef) < 4:
110 return None
Pau Espin Pedrol665bd222017-12-29 20:30:35 +0100111 l = int(ef[0:2], 16) * 2 # Length of the IMSI string
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200112 l = l - 1 # Encoded length byte includes oe nibble
113 swapped = swap_nibbles(ef[2:]).rstrip('f')
Philipp Maiercd3d6262020-05-11 21:41:56 +0200114 if len(swapped) < 1:
115 return None
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400116 oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200117 if not oe:
118 # if even, only half of last byte was used
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400119 l = l-1
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200120 if l != len(swapped) - 1:
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400121 return None
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200122 imsi = swapped[1:]
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400123 return imsi
124
125def dec_iccid(ef):
126 return swap_nibbles(ef).strip('f')
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400127
128def enc_iccid(iccid):
129 return swap_nibbles(rpad(iccid, 20))
130
131def enc_plmn(mcc, mnc):
Alexander Chemerisdddbf522017-07-18 16:49:59 +0300132 """Converts integer MCC/MNC into 3 bytes for EF"""
Harald Welte7f1d3c42020-05-12 21:12:44 +0200133 if len(mnc) == 2:
Vadim Yanitskiyc8458e22021-03-12 00:34:10 +0100134 mnc += "F" # pad to 3 digits if needed
135 return (mcc[1] + mcc[0]) + (mnc[2] + mcc[2]) + (mnc[1] + mnc[0])
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +0300136
137def dec_spn(ef):
138 byte1 = int(ef[0:2])
139 hplmn_disp = (byte1&0x01 == 0x01)
140 oplmn_disp = (byte1&0x02 == 0x02)
141 name = h2s(ef[2:])
142 return (name, hplmn_disp, oplmn_disp)
143
144def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
145 byte1 = 0x00
146 if hplmn_disp: byte1 = byte1|0x01
147 if oplmn_disp: byte1 = byte1|0x02
148 return i2h([byte1])+s2h(name)
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900149
Supreeth Herlef3948532020-03-24 12:23:51 +0100150def hexstr_to_Nbytearr(s, nbytes):
151 return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2)) ]
152
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100153# Accepts hex string representing three bytes
154def dec_mcc_from_plmn(plmn):
155 ia = h2i(plmn)
156 digit1 = ia[0] & 0x0F # 1st byte, LSB
157 digit2 = (ia[0] & 0xF0) >> 4 # 1st byte, MSB
158 digit3 = ia[1] & 0x0F # 2nd byte, LSB
159 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
160 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100161 return derive_mcc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100162
163def dec_mnc_from_plmn(plmn):
164 ia = h2i(plmn)
Vadim Yanitskiyb271be32021-03-11 23:56:58 +0100165 digit1 = ia[2] & 0x0F # 3rd byte, LSB
166 digit2 = (ia[2] & 0xF0) >> 4 # 3rd byte, MSB
167 digit3 = (ia[1] & 0xF0) >> 4 # 2nd byte, MSB
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100168 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
169 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100170 return derive_mnc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100171
172def dec_act(twohexbytes):
173 act_list = [
174 {'bit': 15, 'name': "UTRAN"},
175 {'bit': 14, 'name': "E-UTRAN"},
176 {'bit': 7, 'name': "GSM"},
177 {'bit': 6, 'name': "GSM COMPACT"},
178 {'bit': 5, 'name': "cdma2000 HRPD"},
179 {'bit': 4, 'name': "cdma2000 1xRTT"},
180 ]
181 ia = h2i(twohexbytes)
182 u16t = (ia[0] << 8)|ia[1]
183 sel = []
184 for a in act_list:
185 if u16t & (1 << a['bit']):
186 sel.append(a['name'])
187 return sel
188
189def dec_xplmn_w_act(fivehexbytes):
190 res = {'mcc': 0, 'mnc': 0, 'act': []}
191 plmn_chars = 6
192 act_chars = 4
193 plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
194 act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
195 res['mcc'] = dec_mcc_from_plmn(plmn_str)
196 res['mnc'] = dec_mnc_from_plmn(plmn_str)
197 res['act'] = dec_act(act_str)
198 return res
199
200def format_xplmn_w_act(hexstr):
201 s = ""
herlesupreeth45fa6042020-09-18 15:32:20 +0200202 for rec_data in hexstr_to_Nbytearr(hexstr, 5):
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100203 rec_info = dec_xplmn_w_act(rec_data)
204 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
205 rec_str = "unused"
206 else:
Supreeth Herled24f1632019-11-30 10:37:09 +0100207 rec_str = "MCC: %03d MNC: %03d AcT: %s" % (rec_info['mcc'], rec_info['mnc'], ", ".join(rec_info['act']))
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100208 s += "\t%s # %s\n" % (rec_data, rec_str)
209 return s
210
Sebastian Vivianie61170c2020-06-03 08:57:00 +0100211def dec_loci(hexstr):
212 res = {'tmsi': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'status': 0}
213 res['tmsi'] = hexstr[:8]
214 res['mcc'] = dec_mcc_from_plmn(hexstr[8:14])
215 res['mnc'] = dec_mnc_from_plmn(hexstr[8:14])
216 res['lac'] = hexstr[14:18]
217 res['status'] = h2i(hexstr[20:22])
218 return res
219
220def dec_psloci(hexstr):
221 res = {'p-tmsi': '', 'p-tmsi-sig': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'rac': '', 'status': 0}
222 res['p-tmsi'] = hexstr[:8]
223 res['p-tmsi-sig'] = hexstr[8:14]
224 res['mcc'] = dec_mcc_from_plmn(hexstr[14:20])
225 res['mnc'] = dec_mnc_from_plmn(hexstr[14:20])
226 res['lac'] = hexstr[20:24]
227 res['rac'] = hexstr[24:26]
228 res['status'] = h2i(hexstr[26:28])
229 return res
230
231def dec_epsloci(hexstr):
232 res = {'guti': '', 'mcc': 0, 'mnc': 0, 'tac': '', 'status': 0}
233 res['guti'] = hexstr[:24]
234 res['tai'] = hexstr[24:34]
235 res['mcc'] = dec_mcc_from_plmn(hexstr[24:30])
236 res['mnc'] = dec_mnc_from_plmn(hexstr[24:30])
237 res['tac'] = hexstr[30:34]
238 res['status'] = h2i(hexstr[34:36])
239 return res
240
Harald Welteca673942020-06-03 15:19:40 +0200241def dec_xplmn(threehexbytes):
242 res = {'mcc': 0, 'mnc': 0, 'act': []}
243 plmn_chars = 6
244 plmn_str = threehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
245 res['mcc'] = dec_mcc_from_plmn(plmn_str)
246 res['mnc'] = dec_mnc_from_plmn(plmn_str)
247 return res
248
249def format_xplmn(hexstr):
250 s = ""
herlesupreeth45fa6042020-09-18 15:32:20 +0200251 for rec_data in hexstr_to_Nbytearr(hexstr, 3):
Harald Welteca673942020-06-03 15:19:40 +0200252 rec_info = dec_xplmn(rec_data)
253 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
254 rec_str = "unused"
255 else:
256 rec_str = "MCC: %03d MNC: %03d" % (rec_info['mcc'], rec_info['mnc'])
257 s += "\t%s # %s\n" % (rec_data, rec_str)
258 return s
259
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900260def derive_milenage_opc(ki_hex, op_hex):
261 """
262 Run the milenage algorithm to calculate OPC from Ki and OP
263 """
264 from Crypto.Cipher import AES
265 from Crypto.Util.strxor import strxor
266 from pySim.utils import b2h
267
268 # We pass in hex string and now need to work on bytes
Harald Welteeab8d2a2021-03-05 18:30:23 +0100269 ki_bytes = bytes(h2b(ki_hex))
270 op_bytes = bytes(h2b(op_hex))
Harald Welteab34fa82021-03-05 18:39:59 +0100271 aes = AES.new(ki_bytes, AES.MODE_ECB)
Harald Welteeab8d2a2021-03-05 18:30:23 +0100272 opc_bytes = aes.encrypt(op_bytes)
273 return b2h(strxor(opc_bytes, op_bytes))
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900274
275def calculate_luhn(cc):
276 """
277 Calculate Luhn checksum used in e.g. ICCID and IMEI
278 """
Daniel Willmanndd014ea2020-10-19 10:35:11 +0200279 num = list(map(int, str(cc)))
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900280 check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
281 return 0 if check_digit == 10 else check_digit
Philipp Maier7592eee2019-09-12 13:03:23 +0200282
283def mcc_from_imsi(imsi):
284 """
285 Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
286 """
287 if imsi == None:
288 return None
289
290 if len(imsi) > 3:
291 return imsi[:3]
292 else:
293 return None
294
295def mnc_from_imsi(imsi, long=False):
296 """
297 Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
298 """
299 if imsi == None:
300 return None
301
302 if len(imsi) > 3:
303 if long:
304 return imsi[3:6]
305 else:
306 return imsi[3:5]
307 else:
308 return None
Supreeth Herled24f1632019-11-30 10:37:09 +0100309
310def derive_mcc(digit1, digit2, digit3):
311 """
312 Derive decimal representation of the MCC (Mobile Country Code)
313 from three given digits.
314 """
315
316 mcc = 0
317
318 if digit1 != 0x0f:
319 mcc += digit1 * 100
320 if digit2 != 0x0f:
321 mcc += digit2 * 10
322 if digit3 != 0x0f:
323 mcc += digit3
324
325 return mcc
326
327def derive_mnc(digit1, digit2, digit3=0x0f):
328 """
329 Derive decimal representation of the MNC (Mobile Network Code)
330 from two or (optionally) three given digits.
331 """
332
333 mnc = 0
334
335 # 3-rd digit is optional for the MNC. If present
336 # the algorythm is the same as for the MCC.
337 if digit3 != 0x0f:
338 return derive_mcc(digit1, digit2, digit3)
339
340 if digit1 != 0x0f:
341 mnc += digit1 * 10
342 if digit2 != 0x0f:
343 mnc += digit2
344
345 return mnc
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100346
347def dec_msisdn(ef_msisdn):
348 """
349 Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
350 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
351 """
352
353 # Convert from str to (kind of) 'bytes'
354 ef_msisdn = h2b(ef_msisdn)
355
356 # Make sure mandatory fields are present
357 if len(ef_msisdn) < 14:
358 raise ValueError("EF.MSISDN is too short")
359
360 # Skip optional Alpha Identifier
361 xlen = len(ef_msisdn) - 14
362 msisdn_lhv = ef_msisdn[xlen:]
363
364 # Parse the length (in bytes) of the BCD encoded number
Harald Welte4f6ca432021-02-01 17:51:56 +0100365 bcd_len = msisdn_lhv[0]
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100366 # BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
367 if bcd_len == 0xff:
368 return None
369 elif bcd_len > 11 or bcd_len < 1:
370 raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
371
372 # Parse ToN / NPI
Harald Welte4f6ca432021-02-01 17:51:56 +0100373 ton = (msisdn_lhv[1] >> 4) & 0x07
374 npi = msisdn_lhv[1] & 0x0f
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100375 bcd_len -= 1
376
377 # No MSISDN?
378 if not bcd_len:
379 return (npi, ton, None)
380
381 msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
382 # International number 10.5.118/3GPP TS 24.008
Vadim Yanitskiy7ba24282020-02-27 00:04:13 +0700383 if ton == 0x01:
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100384 msisdn = '+' + msisdn
385
386 return (npi, ton, msisdn)
Supreeth Herle5a541012019-12-22 08:59:16 +0100387
388def enc_msisdn(msisdn, npi=0x01, ton=0x03):
389 """
390 Encode MSISDN as LHV so it can be stored to EF.MSISDN.
391 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
392
393 Default NPI / ToN values:
394 - NPI: ISDN / telephony numbering plan (E.164 / E.163),
395 - ToN: network specific or international number (if starts with '+').
396 """
397
398 # Leading '+' indicates International Number
399 if msisdn[0] == '+':
400 msisdn = msisdn[1:]
401 ton = 0x01
402
403 # Append 'f' padding if number of digits is odd
404 if len(msisdn) % 2 > 0:
405 msisdn += 'f'
406
407 # BCD length also includes NPI/ToN header
408 bcd_len = len(msisdn) // 2 + 1
409 npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
410 bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
411
412 return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
Supreeth Herle441c4a72020-03-24 10:19:15 +0100413
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200414def dec_st(st, table="sim"):
415 """
416 Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
417 """
418
Supreeth Herledf330372020-04-20 14:48:55 +0200419 if table == "isim":
420 from pySim.ts_31_103 import EF_IST_map
421 lookup_map = EF_IST_map
422 elif table == "usim":
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200423 from pySim.ts_31_102 import EF_UST_map
424 lookup_map = EF_UST_map
425 else:
426 from pySim.ts_51_011 import EF_SST_map
427 lookup_map = EF_SST_map
428
429 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
430
431 avail_st = ""
432 # Get each byte and check for available services
433 for i in range(0, len(st_bytes)):
434 # Byte i contains info about Services num (8i+1) to num (8i+8)
435 byte = int(st_bytes[i], 16)
436 # Services in each byte are in order MSB to LSB
437 # MSB - Service (8i+8)
438 # LSB - Service (8i+1)
439 for j in range(1, 9):
440 if byte&0x01 == 0x01 and ((8*i) + j in lookup_map):
441 # Byte X contains info about Services num (8X-7) to num (8X)
442 # bit = 1: service available
443 # bit = 0: service not available
444 avail_st += '\tService %d - %s\n' % ((8*i) + j, lookup_map[(8*i) + j])
445 byte = byte >> 1
446 return avail_st
Supreeth Herle98370552020-05-11 09:04:41 +0200447
448def first_TLV_parser(bytelist):
449 '''
450 first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
451
452 parses first TLV format record in a list of bytelist
453 returns a 3-Tuple: Tag, Length, Value
454 Value is a list of bytes
455 parsing of length is ETSI'style 101.220
456 '''
457 Tag = bytelist[0]
458 if bytelist[1] == 0xFF:
459 Len = bytelist[2]*256 + bytelist[3]
460 Val = bytelist[4:4+Len]
461 else:
462 Len = bytelist[1]
463 Val = bytelist[2:2+Len]
464 return (Tag, Len, Val)
465
466def TLV_parser(bytelist):
467 '''
468 TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
469
470 loops on the input list of bytes with the "first_TLV_parser()" function
471 returns a list of 3-Tuples
472 '''
473 ret = []
474 while len(bytelist) > 0:
475 T, L, V = first_TLV_parser(bytelist)
476 if T == 0xFF:
477 # padding bytes
478 break
479 ret.append( (T, L, V) )
480 # need to manage length of L
481 if L > 0xFE:
482 bytelist = bytelist[ L+4 : ]
483 else:
484 bytelist = bytelist[ L+2 : ]
485 return ret
Supreeth Herled572ede2020-03-22 09:55:04 +0100486
Supreeth Herled84daa12020-03-24 12:20:40 +0100487def enc_st(st, service, state=1):
488 """
489 Encodes the EF S/U/IST/EST and returns the updated Service Table
490
491 Parameters:
492 st - Current value of SIM/USIM/ISIM Service Table
493 service - Service Number to encode as activated/de-activated
494 state - 1 mean activate, 0 means de-activate
495
496 Returns:
497 s - Modified value of SIM/USIM/ISIM Service Table
498
499 Default values:
500 - state: 1 - Sets the particular Service bit to 1
501 """
502 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
503
504 s = ""
505 # Check whether the requested service is present in each byte
506 for i in range(0, len(st_bytes)):
507 # Byte i contains info about Services num (8i+1) to num (8i+8)
508 if service in range((8*i) + 1, (8*i) + 9):
509 byte = int(st_bytes[i], 16)
510 # Services in each byte are in order MSB to LSB
511 # MSB - Service (8i+8)
512 # LSB - Service (8i+1)
513 mod_byte = 0x00
514 # Copy bit by bit contents of byte to mod_byte with modified bit
515 # for requested service
516 for j in range(1, 9):
517 mod_byte = mod_byte >> 1
518 if service == (8*i) + j:
519 mod_byte = state == 1 and mod_byte|0x80 or mod_byte&0x7f
520 else:
521 mod_byte = byte&0x01 == 0x01 and mod_byte|0x80 or mod_byte&0x7f
522 byte = byte >> 1
523
524 s += ('%02x' % (mod_byte))
525 else:
526 s += st_bytes[i]
527
528 return s
529
Supreeth Herle3b342c22020-03-24 16:15:02 +0100530def dec_addr_tlv(hexstr):
Supreeth Herled572ede2020-03-22 09:55:04 +0100531 """
Supreeth Herle3b342c22020-03-24 16:15:02 +0100532 Decode hex string to get EF.P-CSCF Address or EF.ePDGId or EF.ePDGIdEm.
533 See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.8, 4.2.102 and 4.2.104.
Supreeth Herled572ede2020-03-22 09:55:04 +0100534 """
535
536 # Convert from hex str to int bytes list
Supreeth Herle3b342c22020-03-24 16:15:02 +0100537 addr_tlv_bytes = h2i(hexstr)
Supreeth Herled572ede2020-03-22 09:55:04 +0100538
539 s = ""
540
541 # Get list of tuples containing parsed TLVs
Supreeth Herle3b342c22020-03-24 16:15:02 +0100542 tlvs = TLV_parser(addr_tlv_bytes)
Supreeth Herled572ede2020-03-22 09:55:04 +0100543
544 for tlv in tlvs:
545 # tlv = (T, L, [V])
546 # T = Tag
547 # L = Length
548 # [V] = List of value
549
550 # Invalid Tag value scenario
551 if tlv[0] != 0x80:
552 continue
553
Supreeth Herled6a5ec52020-06-01 12:27:51 +0200554 # Empty field - Zero length
555 if tlv[1] == 0:
556 continue
557
Supreeth Herled572ede2020-03-22 09:55:04 +0100558 # First byte in the value has the address type
559 addr_type = tlv[2][0]
Supreeth Herle43fd03b2020-03-25 14:52:46 +0100560 # TODO: Support parsing of IPv6
Supreeth Herle3b342c22020-03-24 16:15:02 +0100561 # Address Type: 0x00 (FQDN), 0x01 (IPv4), 0x02 (IPv6), other (Reserved)
Supreeth Herled572ede2020-03-22 09:55:04 +0100562 if addr_type == 0x00: #FQDN
563 # Skip address tye byte i.e. first byte in value list
564 content = tlv[2][1:]
565 s += "\t%s # %s\n" % (i2h(content), i2s(content))
Supreeth Herle43fd03b2020-03-25 14:52:46 +0100566 elif addr_type == 0x01: #IPv4
567 # Skip address tye byte i.e. first byte in value list
568 # Skip the unused byte in Octect 4 after address type byte as per 3GPP TS 31.102
569 ipv4 = tlv[2][2:]
570 content = '.'.join(str(x) for x in ipv4)
571 s += "\t%s # %s\n" % (i2h(ipv4), content)
Supreeth Herled572ede2020-03-22 09:55:04 +0100572
573 return s
Philipp Maierff84c232020-05-12 17:24:18 +0200574
Supreeth Herle3b342c22020-03-24 16:15:02 +0100575def enc_addr_tlv(addr, addr_type='00'):
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100576 """
Supreeth Herle3b342c22020-03-24 16:15:02 +0100577 Encode address TLV object used in EF.P-CSCF Address, EF.ePDGId and EF.ePDGIdEm.
578 See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.8, 4.2.102 and 4.2.104.
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100579
580 Default values:
Supreeth Herle3b342c22020-03-24 16:15:02 +0100581 - addr_type: 00 - FQDN format of Address
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100582 """
583
584 s = ""
585
Supreeth Herle654eca72020-03-25 14:25:38 +0100586 # TODO: Encoding of IPv6 address
587 if addr_type == '00': #FQDN
Supreeth Herle3b342c22020-03-24 16:15:02 +0100588 hex_str = s2h(addr)
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100589 s += '80' + ('%02x' % ((len(hex_str)//2)+1)) + '00' + hex_str
Supreeth Herle654eca72020-03-25 14:25:38 +0100590 elif addr_type == '01': #IPv4
591 ipv4_list = addr.split('.')
592 ipv4_str = ""
593 for i in ipv4_list:
594 ipv4_str += ('%02x' % (int(i)))
595
596 # Unused bytes shall be set to 'ff'. i.e 4th Octet after Address Type is not used
597 # IPv4 Address is in octet 5 to octet 8 of the TLV data object
598 s += '80' + ('%02x' % ((len(ipv4_str)//2)+2)) + '01' + 'ff' + ipv4_str
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100599
600 return s
601
Philipp Maier47236502021-03-09 21:28:25 +0100602def is_hex(string, minlen=2, maxlen=None) -> bool:
603 """
604 Check if a string is a valid hexstring
605 """
606
607 # Filter obviously bad strings
608 if not string:
609 return False
610 if len(string) < minlen or minlen < 2:
611 return False
612 if len(string) % 2:
613 return False
614 if maxlen and len(string) > maxlen:
615 return False
616
617 # Try actual encoding to be sure
618 try:
619 try_encode = h2b(string)
620 return True
621 except:
622 return False
623
Harald Welte79b5ba42021-01-08 21:22:38 +0100624def sanitize_pin_adm(pin_adm, pin_adm_hex = None):
Philipp Maiere8536c02020-05-11 21:35:01 +0200625 """
626 The ADM pin can be supplied either in its hexadecimal form or as
627 ascii string. This function checks the supplied opts parameter and
628 returns the pin_adm as hex encoded string, regardles in which form
629 it was originally supplied by the user
630 """
631
Harald Welte79b5ba42021-01-08 21:22:38 +0100632 if pin_adm is not None:
633 if len(pin_adm) <= 8:
634 pin_adm = ''.join(['%02x'%(ord(x)) for x in pin_adm])
Philipp Maiere8536c02020-05-11 21:35:01 +0200635 pin_adm = rpad(pin_adm, 16)
636
637 else:
638 raise ValueError("PIN-ADM needs to be <=8 digits (ascii)")
639
Harald Welte79b5ba42021-01-08 21:22:38 +0100640 if pin_adm_hex is not None:
641 if len(pin_adm_hex) == 16:
642 pin_adm = pin_adm_hex
Philipp Maiere8536c02020-05-11 21:35:01 +0200643 # Ensure that it's hex-encoded
644 try:
645 try_encode = h2b(pin_adm)
646 except ValueError:
647 raise ValueError("PIN-ADM needs to be hex encoded using this option")
648 else:
649 raise ValueError("PIN-ADM needs to be exactly 16 digits (hex encoded)")
650
651 return pin_adm
652
Philipp Maierff84c232020-05-12 17:24:18 +0200653def init_reader(opts):
654 """
655 Init card reader driver
656 """
Philipp Maierc8caec22021-02-22 16:07:53 +0100657 try:
658 if opts.pcsc_dev is not None:
659 print("Using PC/SC reader interface")
660 from pySim.transport.pcsc import PcscSimLink
661 sl = PcscSimLink(opts.pcsc_dev)
662 elif opts.osmocon_sock is not None:
663 print("Using Calypso-based (OsmocomBB) reader interface")
664 from pySim.transport.calypso import CalypsoSimLink
665 sl = CalypsoSimLink(sock_path=opts.osmocon_sock)
666 elif opts.modem_dev is not None:
667 print("Using modem for Generic SIM Access (3GPP TS 27.007)")
668 from pySim.transport.modem_atcmd import ModemATCommandLink
669 sl = ModemATCommandLink(device=opts.modem_dev, baudrate=opts.modem_baud)
670 else: # Serial reader is default
671 print("Using serial reader interface")
672 from pySim.transport.serial import SerialSimLink
673 sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
674 return sl
675 except Exception as e:
676 print("Card reader initialization failed with exception:\n" + str(e))
677 return None
Philipp Maierff84c232020-05-12 17:24:18 +0200678
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100679
Supreeth Herle95ec7722020-03-24 13:09:03 +0100680def enc_ePDGSelection(hexstr, mcc, mnc, epdg_priority='0001', epdg_fqdn_format='00'):
681 """
682 Encode ePDGSelection so it can be stored at EF.ePDGSelection or EF.ePDGSelectionEm.
683 See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
684
685 Default values:
686 - epdg_priority: '0001' - 1st Priority
687 - epdg_fqdn_format: '00' - Operator Identifier FQDN
688 """
689
690 plmn1 = enc_plmn(mcc, mnc) + epdg_priority + epdg_fqdn_format
691 # TODO: Handle encoding of Length field for length more than 127 Bytes
692 content = '80' + ('%02x' % (len(plmn1)//2)) + plmn1
693 content = rpad(content, len(hexstr))
694 return content
695
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100696def dec_ePDGSelection(sixhexbytes):
697 """
698 Decode ePDGSelection to get EF.ePDGSelection or EF.ePDGSelectionEm.
699 See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
700 """
701
702 res = {'mcc': 0, 'mnc': 0, 'epdg_priority': 0, 'epdg_fqdn_format': ''}
703 plmn_chars = 6
704 epdg_priority_chars = 4
705 epdg_fqdn_format_chars = 2
706 # first three bytes (six ascii hex chars)
707 plmn_str = sixhexbytes[:plmn_chars]
708 # two bytes after first three bytes
709 epdg_priority_str = sixhexbytes[plmn_chars:plmn_chars + epdg_priority_chars]
710 # one byte after first five bytes
711 epdg_fqdn_format_str = sixhexbytes[plmn_chars + epdg_priority_chars:plmn_chars + epdg_priority_chars + epdg_fqdn_format_chars]
712 res['mcc'] = dec_mcc_from_plmn(plmn_str)
713 res['mnc'] = dec_mnc_from_plmn(plmn_str)
714 res['epdg_priority'] = epdg_priority_str
715 res['epdg_fqdn_format'] = epdg_fqdn_format_str == '00' and 'Operator Identifier FQDN' or 'Location based FQDN'
716 return res
717
718def format_ePDGSelection(hexstr):
719 ePDGSelection_info_tag_chars = 2
720 ePDGSelection_info_tag_str = hexstr[:2]
herlesupreeth3a261d32021-01-05 09:20:11 +0100721 s = ""
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100722 # Minimum length
723 len_chars = 2
724 # TODO: Need to determine length properly - definite length support only
725 # Inconsistency in spec: 3GPP TS 31.102 version 15.2.0 Release 15, 4.2.104
726 # As per spec, length is 5n, n - number of PLMNs
727 # But, each PLMN entry is made of PLMN (3 Bytes) + ePDG Priority (2 Bytes) + ePDG FQDN format (1 Byte)
728 # Totalling to 6 Bytes, maybe length should be 6n
729 len_str = hexstr[ePDGSelection_info_tag_chars:ePDGSelection_info_tag_chars+len_chars]
herlesupreeth3a261d32021-01-05 09:20:11 +0100730
731 # Not programmed scenario
732 if int(len_str, 16) == 255 or int(ePDGSelection_info_tag_str, 16) == 255:
733 len_chars = 0
734 ePDGSelection_info_tag_chars = 0
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100735 if len_str[0] == '8':
736 # The bits 7 to 1 denotes the number of length octets if length > 127
737 if int(len_str[1]) > 0:
738 # Update number of length octets
739 len_chars = len_chars * int(len_str[1])
740 len_str = hexstr[ePDGSelection_info_tag_chars:len_chars]
741
742 content_str = hexstr[ePDGSelection_info_tag_chars+len_chars:]
743 # Right pad to prevent index out of range - multiple of 6 bytes
744 content_str = rpad(content_str, len(content_str) + (12 - (len(content_str) % 12)))
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100745 for rec_data in hexstr_to_Nbytearr(content_str, 6):
746 rec_info = dec_ePDGSelection(rec_data)
747 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
748 rec_str = "unused"
749 else:
750 rec_str = "MCC: %03d MNC: %03d ePDG Priority: %s ePDG FQDN format: %s" % \
751 (rec_info['mcc'], rec_info['mnc'], rec_info['epdg_priority'], rec_info['epdg_fqdn_format'])
752 s += "\t%s # %s\n" % (rec_data, rec_str)
753 return s
Supreeth Herle556b0fe2020-03-25 11:26:57 +0100754
755def get_addr_type(addr):
756 """
757 Validates the given address and returns it's type (FQDN or IPv4 or IPv6)
758 Return: 0x00 (FQDN), 0x01 (IPv4), 0x02 (IPv6), None (Bad address argument given)
759
760 TODO: Handle IPv6
761 """
762
763 # Empty address string
764 if not len(addr):
765 return None
766
Supreeth Herle556b0fe2020-03-25 11:26:57 +0100767 addr_list = addr.split('.')
768
769 # Check for IPv4/IPv6
770 try:
771 import ipaddress
772 # Throws ValueError if addr is not correct
Denis 'GNUtoo' Carikli79f5b602020-02-15 04:02:57 +0700773 ipa = ipaddress.ip_address(addr)
Supreeth Herle556b0fe2020-03-25 11:26:57 +0100774
775 if ipa.version == 4:
776 return 0x01
777 elif ipa.version == 6:
778 return 0x02
779 except Exception as e:
780 invalid_ipv4 = True
781 for i in addr_list:
782 # Invalid IPv4 may qualify for a valid FQDN, so make check here
783 # e.g. 172.24.15.300
784 import re
785 if not re.match('^[0-9_]+$', i):
786 invalid_ipv4 = False
787 break
788
789 if invalid_ipv4:
790 return None
791
792 fqdn_flag = True
793 for i in addr_list:
794 # Only Alpha-numeric characters and hyphen - RFC 1035
795 import re
796 if not re.match("^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)?$", i):
797 fqdn_flag = False
798 break
799
800 # FQDN
801 if fqdn_flag:
802 return 0x00
803
804 return None
Harald Welte67d551a2021-01-21 14:50:01 +0100805
Harald Welte1e456572021-04-02 17:16:30 +0200806def sw_match(sw:str, pattern:str) -> bool:
Harald Welte67d551a2021-01-21 14:50:01 +0100807 """Match given SW against given pattern."""
808 # Create a masked version of the returned status word
809 sw_lower = sw.lower()
810 sw_masked = ""
811 for i in range(0, 4):
Philipp Maier78e32f22021-03-22 20:39:24 +0100812 if pattern[i] == '?':
Harald Welte67d551a2021-01-21 14:50:01 +0100813 sw_masked = sw_masked + '?'
Philipp Maier78e32f22021-03-22 20:39:24 +0100814 elif pattern[i] == 'x':
Harald Welte67d551a2021-01-21 14:50:01 +0100815 sw_masked = sw_masked + 'x'
816 else:
817 sw_masked = sw_masked + sw_lower[i]
Philipp Maier78e32f22021-03-22 20:39:24 +0100818 # Compare the masked version against the pattern
Harald Welte67d551a2021-01-21 14:50:01 +0100819 return sw_masked == pattern
Philipp Maier5d3e2592021-02-22 17:22:16 +0100820
Harald Welteee3501f2021-04-02 13:00:18 +0200821def tabulate_str_list(str_list, width:int = 79, hspace:int = 2, lspace:int = 1,
822 align_left:bool = True):
823 """Pretty print a list of strings into a tabulated form.
824
825 Args:
826 width : total width in characters per line
827 space : horizontal space between cells
828 lspace : number of spaces before row
829 align_lef : Align text to the left side
830 Returns:
831 multi-line string containing formatted table
832 """
Philipp Maier5d3e2592021-02-22 17:22:16 +0100833 if str_list == None:
834 return ""
835 if len(str_list) <= 0:
836 return ""
837 longest_str = max(str_list, key=len)
838 cellwith = len(longest_str) + hspace
839 cols = width // cellwith
840 rows = (len(str_list) - 1) // cols + 1
841 table = []
842 for i in iter(range(rows)):
843 str_list_row = str_list[i::rows]
844 if (align_left):
845 format_str_cell = '%%-%ds'
846 else:
847 format_str_cell = '%%%ds'
848 format_str_row = (format_str_cell % cellwith) * len(str_list_row)
849 format_str_row = (" " * lspace) + format_str_row
850 table.append(format_str_row % tuple(str_list_row))
851 return '\n'.join(table)