blob: 6a093faba59c10f44c462d1cce3cb93bc22f3f1b [file] [log] [blame]
Sylvain Munaut76504e02010-12-07 00:24:32 +01001# -*- coding: utf-8 -*-
2
3""" pySim: various utilities
4"""
5
Harald Welte52255572021-04-03 09:56:32 +02006from typing import Optional, List, Dict, Any, Tuple
7
Sylvain Munaut76504e02010-12-07 00:24:32 +01008# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation, either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23
Harald Welte52255572021-04-03 09:56:32 +020024# just to differentiate strings of hex nibbles from everything else
25Hexstr = str
Sylvain Munaut76504e02010-12-07 00:24:32 +010026
Harald Welte52255572021-04-03 09:56:32 +020027def h2b(s:Hexstr) -> bytearray:
Harald Welte4f6ca432021-02-01 17:51:56 +010028 """convert from a string of hex nibbles to a sequence of bytes"""
29 return bytearray.fromhex(s)
Sylvain Munaut76504e02010-12-07 00:24:32 +010030
Harald Welte52255572021-04-03 09:56:32 +020031def b2h(b:bytearray) -> Hexstr:
Harald Welte4f6ca432021-02-01 17:51:56 +010032 """convert from a sequence of bytes to a string of hex nibbles"""
33 return ''.join(['%02x'%(x) for x in b])
Sylvain Munaut76504e02010-12-07 00:24:32 +010034
Harald Welte52255572021-04-03 09:56:32 +020035def h2i(s:Hexstr) -> List[int]:
Harald Welteee3501f2021-04-02 13:00:18 +020036 """convert from a string of hex nibbles to a list of integers"""
Sylvain Munaut76504e02010-12-07 00:24:32 +010037 return [(int(x,16)<<4)+int(y,16) for x,y in zip(s[0::2], s[1::2])]
38
Harald Welte52255572021-04-03 09:56:32 +020039def i2h(s:List[int]) -> Hexstr:
Harald Welteee3501f2021-04-02 13:00:18 +020040 """convert from a list of integers to a string of hex nibbles"""
Sylvain Munaut76504e02010-12-07 00:24:32 +010041 return ''.join(['%02x'%(x) for x in s])
42
Harald Welte52255572021-04-03 09:56:32 +020043def h2s(s:Hexstr) -> str:
Harald Welteee3501f2021-04-02 13:00:18 +020044 """convert from a string of hex nibbles to an ASCII string"""
Vadim Yanitskiyeb06b452020-05-10 02:32:46 +070045 return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])
46 if int(x + y, 16) != 0xff])
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030047
Harald Welte52255572021-04-03 09:56:32 +020048def s2h(s:str) -> Hexstr:
Harald Welteee3501f2021-04-02 13:00:18 +020049 """convert from an ASCII string to a string of hex nibbles"""
Harald Welte4f6ca432021-02-01 17:51:56 +010050 b = bytearray()
51 b.extend(map(ord, s))
52 return b2h(b)
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030053
Supreeth Herle7d77d2d2020-05-11 09:07:08 +020054# List of bytes to string
Harald Welte52255572021-04-03 09:56:32 +020055def i2s(s:List[int]) -> str:
Harald Welteee3501f2021-04-02 13:00:18 +020056 """convert from a list of integers to an ASCII string"""
Supreeth Herle7d77d2d2020-05-11 09:07:08 +020057 return ''.join([chr(x) for x in s])
58
Harald Welte52255572021-04-03 09:56:32 +020059def swap_nibbles(s:Hexstr) -> Hexstr:
Harald Welteee3501f2021-04-02 13:00:18 +020060 """swap the nibbles in a hex string"""
Sylvain Munaut76504e02010-12-07 00:24:32 +010061 return ''.join([x+y for x,y in zip(s[1::2], s[0::2])])
62
Harald Welteee3501f2021-04-02 13:00:18 +020063def rpad(s:str, l:int, c='f') -> str:
64 """pad string on the right side.
65 Args:
66 s : string to pad
67 l : total length to pad to
68 c : padding character
69 Returns:
70 String 's' padded with as many 'c' as needed to reach total length of 'l'
71 """
Sylvain Munaut76504e02010-12-07 00:24:32 +010072 return s + c * (l - len(s))
73
Harald Welteee3501f2021-04-02 13:00:18 +020074def lpad(s:str, l:int, c='f') -> str:
75 """pad string on the left side.
76 Args:
77 s : string to pad
78 l : total length to pad to
79 c : padding character
80 Returns:
81 String 's' padded with as many 'c' as needed to reach total length of 'l'
82 """
Sylvain Munaut76504e02010-12-07 00:24:32 +010083 return c * (l - len(s)) + s
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040084
Harald Welteee3501f2021-04-02 13:00:18 +020085def half_round_up(n:int) -> int:
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020086 return (n + 1)//2
87
88# IMSI encoded format:
89# For IMSI 0123456789ABCDE:
90#
91# | byte 1 | 2 upper | 2 lower | 3 upper | 3 lower | ... | 9 upper | 9 lower |
92# | length in bytes | 0 | odd/even | 2 | 1 | ... | E | D |
93#
94# If the IMSI is less than 15 characters, it should be padded with 'f' from the end.
95#
96# The length is the total number of bytes used to encoded the IMSI. This includes the odd/even
97# parity bit. E.g. an IMSI of length 14 is 8 bytes long, not 7, as it uses bytes 2 to 9 to
98# encode itself.
99#
100# Because of this, an odd length IMSI fits exactly into len(imsi) + 1 // 2 bytes, whereas an
101# even length IMSI only uses half of the last byte.
102
Harald Welteee3501f2021-04-02 13:00:18 +0200103def enc_imsi(imsi:str):
104 """Converts a string IMSI into the encoded value of the EF"""
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200105 l = half_round_up(len(imsi) + 1) # Required bytes - include space for odd/even indicator
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400106 oe = len(imsi) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200107 ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe<<3)|1, rpad(imsi, 15)))
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400108 return ei
109
Harald Welte52255572021-04-03 09:56:32 +0200110def dec_imsi(ef:Hexstr) -> Optional[str]:
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400111 """Converts an EF value to the imsi string representation"""
112 if len(ef) < 4:
113 return None
Pau Espin Pedrol665bd222017-12-29 20:30:35 +0100114 l = int(ef[0:2], 16) * 2 # Length of the IMSI string
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200115 l = l - 1 # Encoded length byte includes oe nibble
116 swapped = swap_nibbles(ef[2:]).rstrip('f')
Philipp Maiercd3d6262020-05-11 21:41:56 +0200117 if len(swapped) < 1:
118 return None
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400119 oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200120 if not oe:
121 # if even, only half of last byte was used
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400122 l = l-1
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200123 if l != len(swapped) - 1:
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400124 return None
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200125 imsi = swapped[1:]
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400126 return imsi
127
Harald Welte52255572021-04-03 09:56:32 +0200128def dec_iccid(ef:Hexstr) -> str:
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400129 return swap_nibbles(ef).strip('f')
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400130
Harald Welte52255572021-04-03 09:56:32 +0200131def enc_iccid(iccid:str) -> Hexstr:
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400132 return swap_nibbles(rpad(iccid, 20))
133
134def enc_plmn(mcc, mnc):
Alexander Chemerisdddbf522017-07-18 16:49:59 +0300135 """Converts integer MCC/MNC into 3 bytes for EF"""
Harald Welte7f1d3c42020-05-12 21:12:44 +0200136 if len(mnc) == 2:
Vadim Yanitskiyc8458e22021-03-12 00:34:10 +0100137 mnc += "F" # pad to 3 digits if needed
138 return (mcc[1] + mcc[0]) + (mnc[2] + mcc[2]) + (mnc[1] + mnc[0])
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +0300139
Harald Welted7a7e172021-04-07 00:30:10 +0200140def dec_plmn(threehexbytes:Hexstr) -> dict:
141 res = {'mcc': 0, 'mnc': 0 }
142 res['mcc'] = dec_mcc_from_plmn(threehexbytes)
143 res['mnc'] = dec_mnc_from_plmn(threehexbytes)
144 return res
145
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +0300146def dec_spn(ef):
147 byte1 = int(ef[0:2])
148 hplmn_disp = (byte1&0x01 == 0x01)
149 oplmn_disp = (byte1&0x02 == 0x02)
150 name = h2s(ef[2:])
151 return (name, hplmn_disp, oplmn_disp)
152
153def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
154 byte1 = 0x00
155 if hplmn_disp: byte1 = byte1|0x01
156 if oplmn_disp: byte1 = byte1|0x02
157 return i2h([byte1])+s2h(name)
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900158
Supreeth Herlef3948532020-03-24 12:23:51 +0100159def hexstr_to_Nbytearr(s, nbytes):
160 return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2)) ]
161
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100162# Accepts hex string representing three bytes
Harald Welte52255572021-04-03 09:56:32 +0200163def dec_mcc_from_plmn(plmn:Hexstr) -> int:
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100164 ia = h2i(plmn)
165 digit1 = ia[0] & 0x0F # 1st byte, LSB
166 digit2 = (ia[0] & 0xF0) >> 4 # 1st byte, MSB
167 digit3 = ia[1] & 0x0F # 2nd byte, LSB
168 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
169 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100170 return derive_mcc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100171
Harald Welte52255572021-04-03 09:56:32 +0200172def dec_mnc_from_plmn(plmn:Hexstr) -> int:
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100173 ia = h2i(plmn)
Vadim Yanitskiyb271be32021-03-11 23:56:58 +0100174 digit1 = ia[2] & 0x0F # 3rd byte, LSB
175 digit2 = (ia[2] & 0xF0) >> 4 # 3rd byte, MSB
176 digit3 = (ia[1] & 0xF0) >> 4 # 2nd byte, MSB
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100177 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
178 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100179 return derive_mnc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100180
Harald Welte52255572021-04-03 09:56:32 +0200181def dec_act(twohexbytes:Hexstr) -> List[str]:
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100182 act_list = [
183 {'bit': 15, 'name': "UTRAN"},
184 {'bit': 14, 'name': "E-UTRAN"},
185 {'bit': 7, 'name': "GSM"},
186 {'bit': 6, 'name': "GSM COMPACT"},
187 {'bit': 5, 'name': "cdma2000 HRPD"},
188 {'bit': 4, 'name': "cdma2000 1xRTT"},
189 ]
190 ia = h2i(twohexbytes)
191 u16t = (ia[0] << 8)|ia[1]
192 sel = []
193 for a in act_list:
194 if u16t & (1 << a['bit']):
195 sel.append(a['name'])
196 return sel
197
Harald Welte52255572021-04-03 09:56:32 +0200198def dec_xplmn_w_act(fivehexbytes:Hexstr) -> Dict[str,Any]:
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100199 res = {'mcc': 0, 'mnc': 0, 'act': []}
200 plmn_chars = 6
201 act_chars = 4
202 plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
203 act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
204 res['mcc'] = dec_mcc_from_plmn(plmn_str)
205 res['mnc'] = dec_mnc_from_plmn(plmn_str)
206 res['act'] = dec_act(act_str)
207 return res
208
209def format_xplmn_w_act(hexstr):
210 s = ""
herlesupreeth45fa6042020-09-18 15:32:20 +0200211 for rec_data in hexstr_to_Nbytearr(hexstr, 5):
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100212 rec_info = dec_xplmn_w_act(rec_data)
213 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
214 rec_str = "unused"
215 else:
Supreeth Herled24f1632019-11-30 10:37:09 +0100216 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 +0100217 s += "\t%s # %s\n" % (rec_data, rec_str)
218 return s
219
Sebastian Vivianie61170c2020-06-03 08:57:00 +0100220def dec_loci(hexstr):
221 res = {'tmsi': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'status': 0}
222 res['tmsi'] = hexstr[:8]
223 res['mcc'] = dec_mcc_from_plmn(hexstr[8:14])
224 res['mnc'] = dec_mnc_from_plmn(hexstr[8:14])
225 res['lac'] = hexstr[14:18]
226 res['status'] = h2i(hexstr[20:22])
227 return res
228
229def dec_psloci(hexstr):
230 res = {'p-tmsi': '', 'p-tmsi-sig': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'rac': '', 'status': 0}
231 res['p-tmsi'] = hexstr[:8]
232 res['p-tmsi-sig'] = hexstr[8:14]
233 res['mcc'] = dec_mcc_from_plmn(hexstr[14:20])
234 res['mnc'] = dec_mnc_from_plmn(hexstr[14:20])
235 res['lac'] = hexstr[20:24]
236 res['rac'] = hexstr[24:26]
237 res['status'] = h2i(hexstr[26:28])
238 return res
239
240def dec_epsloci(hexstr):
241 res = {'guti': '', 'mcc': 0, 'mnc': 0, 'tac': '', 'status': 0}
242 res['guti'] = hexstr[:24]
243 res['tai'] = hexstr[24:34]
244 res['mcc'] = dec_mcc_from_plmn(hexstr[24:30])
245 res['mnc'] = dec_mnc_from_plmn(hexstr[24:30])
246 res['tac'] = hexstr[30:34]
247 res['status'] = h2i(hexstr[34:36])
248 return res
249
Harald Welte52255572021-04-03 09:56:32 +0200250def dec_xplmn(threehexbytes:Hexstr) -> dict:
Harald Welteca673942020-06-03 15:19:40 +0200251 res = {'mcc': 0, 'mnc': 0, 'act': []}
252 plmn_chars = 6
253 plmn_str = threehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
254 res['mcc'] = dec_mcc_from_plmn(plmn_str)
255 res['mnc'] = dec_mnc_from_plmn(plmn_str)
256 return res
257
Harald Welte52255572021-04-03 09:56:32 +0200258def format_xplmn(hexstr:Hexstr) -> str:
Harald Welteca673942020-06-03 15:19:40 +0200259 s = ""
herlesupreeth45fa6042020-09-18 15:32:20 +0200260 for rec_data in hexstr_to_Nbytearr(hexstr, 3):
Harald Welteca673942020-06-03 15:19:40 +0200261 rec_info = dec_xplmn(rec_data)
262 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
263 rec_str = "unused"
264 else:
265 rec_str = "MCC: %03d MNC: %03d" % (rec_info['mcc'], rec_info['mnc'])
266 s += "\t%s # %s\n" % (rec_data, rec_str)
267 return s
268
Harald Welte52255572021-04-03 09:56:32 +0200269def derive_milenage_opc(ki_hex:Hexstr, op_hex:Hexstr) -> Hexstr:
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900270 """
271 Run the milenage algorithm to calculate OPC from Ki and OP
272 """
273 from Crypto.Cipher import AES
274 from Crypto.Util.strxor import strxor
275 from pySim.utils import b2h
276
277 # We pass in hex string and now need to work on bytes
Harald Welteeab8d2a2021-03-05 18:30:23 +0100278 ki_bytes = bytes(h2b(ki_hex))
279 op_bytes = bytes(h2b(op_hex))
Harald Welteab34fa82021-03-05 18:39:59 +0100280 aes = AES.new(ki_bytes, AES.MODE_ECB)
Harald Welteeab8d2a2021-03-05 18:30:23 +0100281 opc_bytes = aes.encrypt(op_bytes)
282 return b2h(strxor(opc_bytes, op_bytes))
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900283
Harald Welte52255572021-04-03 09:56:32 +0200284def calculate_luhn(cc) -> int:
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900285 """
286 Calculate Luhn checksum used in e.g. ICCID and IMEI
287 """
Daniel Willmanndd014ea2020-10-19 10:35:11 +0200288 num = list(map(int, str(cc)))
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900289 check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
290 return 0 if check_digit == 10 else check_digit
Philipp Maier7592eee2019-09-12 13:03:23 +0200291
Harald Welte52255572021-04-03 09:56:32 +0200292def mcc_from_imsi(imsi:str) -> Optional[str]:
Philipp Maier7592eee2019-09-12 13:03:23 +0200293 """
294 Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
295 """
296 if imsi == None:
297 return None
298
299 if len(imsi) > 3:
300 return imsi[:3]
301 else:
302 return None
303
Harald Welte52255572021-04-03 09:56:32 +0200304def mnc_from_imsi(imsi:str, long:bool=False) -> Optional[str]:
Philipp Maier7592eee2019-09-12 13:03:23 +0200305 """
306 Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
307 """
308 if imsi == None:
309 return None
310
311 if len(imsi) > 3:
312 if long:
313 return imsi[3:6]
314 else:
315 return imsi[3:5]
316 else:
317 return None
Supreeth Herled24f1632019-11-30 10:37:09 +0100318
Harald Welte52255572021-04-03 09:56:32 +0200319def derive_mcc(digit1:int, digit2:int, digit3:int) -> int:
Supreeth Herled24f1632019-11-30 10:37:09 +0100320 """
321 Derive decimal representation of the MCC (Mobile Country Code)
322 from three given digits.
323 """
324
325 mcc = 0
326
327 if digit1 != 0x0f:
328 mcc += digit1 * 100
329 if digit2 != 0x0f:
330 mcc += digit2 * 10
331 if digit3 != 0x0f:
332 mcc += digit3
333
334 return mcc
335
Harald Welte52255572021-04-03 09:56:32 +0200336def derive_mnc(digit1:int, digit2:int, digit3:int=0x0f) -> int:
Supreeth Herled24f1632019-11-30 10:37:09 +0100337 """
338 Derive decimal representation of the MNC (Mobile Network Code)
339 from two or (optionally) three given digits.
340 """
341
342 mnc = 0
343
344 # 3-rd digit is optional for the MNC. If present
345 # the algorythm is the same as for the MCC.
346 if digit3 != 0x0f:
347 return derive_mcc(digit1, digit2, digit3)
348
349 if digit1 != 0x0f:
350 mnc += digit1 * 10
351 if digit2 != 0x0f:
352 mnc += digit2
353
354 return mnc
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100355
Harald Welte52255572021-04-03 09:56:32 +0200356def dec_msisdn(ef_msisdn:Hexstr) -> Optional[Tuple[int,int,Optional[str]]]:
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100357 """
358 Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
359 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
360 """
361
362 # Convert from str to (kind of) 'bytes'
363 ef_msisdn = h2b(ef_msisdn)
364
365 # Make sure mandatory fields are present
366 if len(ef_msisdn) < 14:
367 raise ValueError("EF.MSISDN is too short")
368
369 # Skip optional Alpha Identifier
370 xlen = len(ef_msisdn) - 14
371 msisdn_lhv = ef_msisdn[xlen:]
372
373 # Parse the length (in bytes) of the BCD encoded number
Harald Welte4f6ca432021-02-01 17:51:56 +0100374 bcd_len = msisdn_lhv[0]
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100375 # BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
376 if bcd_len == 0xff:
377 return None
378 elif bcd_len > 11 or bcd_len < 1:
379 raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
380
381 # Parse ToN / NPI
Harald Welte4f6ca432021-02-01 17:51:56 +0100382 ton = (msisdn_lhv[1] >> 4) & 0x07
383 npi = msisdn_lhv[1] & 0x0f
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100384 bcd_len -= 1
385
386 # No MSISDN?
387 if not bcd_len:
388 return (npi, ton, None)
389
390 msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
391 # International number 10.5.118/3GPP TS 24.008
Vadim Yanitskiy7ba24282020-02-27 00:04:13 +0700392 if ton == 0x01:
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100393 msisdn = '+' + msisdn
394
395 return (npi, ton, msisdn)
Supreeth Herle5a541012019-12-22 08:59:16 +0100396
Harald Welte52255572021-04-03 09:56:32 +0200397def enc_msisdn(msisdn:str, npi:int=0x01, ton:int=0x03) -> Hexstr:
Supreeth Herle5a541012019-12-22 08:59:16 +0100398 """
399 Encode MSISDN as LHV so it can be stored to EF.MSISDN.
400 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
401
402 Default NPI / ToN values:
403 - NPI: ISDN / telephony numbering plan (E.164 / E.163),
404 - ToN: network specific or international number (if starts with '+').
405 """
406
407 # Leading '+' indicates International Number
408 if msisdn[0] == '+':
409 msisdn = msisdn[1:]
410 ton = 0x01
411
412 # Append 'f' padding if number of digits is odd
413 if len(msisdn) % 2 > 0:
414 msisdn += 'f'
415
416 # BCD length also includes NPI/ToN header
417 bcd_len = len(msisdn) // 2 + 1
418 npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
419 bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
420
421 return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
Supreeth Herle441c4a72020-03-24 10:19:15 +0100422
Harald Welte52255572021-04-03 09:56:32 +0200423def dec_st(st, table="sim") -> str:
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200424 """
425 Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
426 """
427
Supreeth Herledf330372020-04-20 14:48:55 +0200428 if table == "isim":
429 from pySim.ts_31_103 import EF_IST_map
430 lookup_map = EF_IST_map
431 elif table == "usim":
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200432 from pySim.ts_31_102 import EF_UST_map
433 lookup_map = EF_UST_map
434 else:
435 from pySim.ts_51_011 import EF_SST_map
436 lookup_map = EF_SST_map
437
438 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
439
440 avail_st = ""
441 # Get each byte and check for available services
442 for i in range(0, len(st_bytes)):
443 # Byte i contains info about Services num (8i+1) to num (8i+8)
444 byte = int(st_bytes[i], 16)
445 # Services in each byte are in order MSB to LSB
446 # MSB - Service (8i+8)
447 # LSB - Service (8i+1)
448 for j in range(1, 9):
449 if byte&0x01 == 0x01 and ((8*i) + j in lookup_map):
450 # Byte X contains info about Services num (8X-7) to num (8X)
451 # bit = 1: service available
452 # bit = 0: service not available
453 avail_st += '\tService %d - %s\n' % ((8*i) + j, lookup_map[(8*i) + j])
454 byte = byte >> 1
455 return avail_st
Supreeth Herle98370552020-05-11 09:04:41 +0200456
457def first_TLV_parser(bytelist):
458 '''
459 first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
460
461 parses first TLV format record in a list of bytelist
462 returns a 3-Tuple: Tag, Length, Value
463 Value is a list of bytes
464 parsing of length is ETSI'style 101.220
465 '''
466 Tag = bytelist[0]
467 if bytelist[1] == 0xFF:
468 Len = bytelist[2]*256 + bytelist[3]
469 Val = bytelist[4:4+Len]
470 else:
471 Len = bytelist[1]
472 Val = bytelist[2:2+Len]
473 return (Tag, Len, Val)
474
475def TLV_parser(bytelist):
476 '''
477 TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
478
479 loops on the input list of bytes with the "first_TLV_parser()" function
480 returns a list of 3-Tuples
481 '''
482 ret = []
483 while len(bytelist) > 0:
484 T, L, V = first_TLV_parser(bytelist)
485 if T == 0xFF:
486 # padding bytes
487 break
488 ret.append( (T, L, V) )
489 # need to manage length of L
490 if L > 0xFE:
491 bytelist = bytelist[ L+4 : ]
492 else:
493 bytelist = bytelist[ L+2 : ]
494 return ret
Supreeth Herled572ede2020-03-22 09:55:04 +0100495
Supreeth Herled84daa12020-03-24 12:20:40 +0100496def enc_st(st, service, state=1):
497 """
498 Encodes the EF S/U/IST/EST and returns the updated Service Table
499
500 Parameters:
501 st - Current value of SIM/USIM/ISIM Service Table
502 service - Service Number to encode as activated/de-activated
503 state - 1 mean activate, 0 means de-activate
504
505 Returns:
506 s - Modified value of SIM/USIM/ISIM Service Table
507
508 Default values:
509 - state: 1 - Sets the particular Service bit to 1
510 """
511 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
512
513 s = ""
514 # Check whether the requested service is present in each byte
515 for i in range(0, len(st_bytes)):
516 # Byte i contains info about Services num (8i+1) to num (8i+8)
517 if service in range((8*i) + 1, (8*i) + 9):
518 byte = int(st_bytes[i], 16)
519 # Services in each byte are in order MSB to LSB
520 # MSB - Service (8i+8)
521 # LSB - Service (8i+1)
522 mod_byte = 0x00
523 # Copy bit by bit contents of byte to mod_byte with modified bit
524 # for requested service
525 for j in range(1, 9):
526 mod_byte = mod_byte >> 1
527 if service == (8*i) + j:
528 mod_byte = state == 1 and mod_byte|0x80 or mod_byte&0x7f
529 else:
530 mod_byte = byte&0x01 == 0x01 and mod_byte|0x80 or mod_byte&0x7f
531 byte = byte >> 1
532
533 s += ('%02x' % (mod_byte))
534 else:
535 s += st_bytes[i]
536
537 return s
538
Supreeth Herle3b342c22020-03-24 16:15:02 +0100539def dec_addr_tlv(hexstr):
Supreeth Herled572ede2020-03-22 09:55:04 +0100540 """
Supreeth Herle3b342c22020-03-24 16:15:02 +0100541 Decode hex string to get EF.P-CSCF Address or EF.ePDGId or EF.ePDGIdEm.
542 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 +0100543 """
544
545 # Convert from hex str to int bytes list
Supreeth Herle3b342c22020-03-24 16:15:02 +0100546 addr_tlv_bytes = h2i(hexstr)
Supreeth Herled572ede2020-03-22 09:55:04 +0100547
548 s = ""
549
550 # Get list of tuples containing parsed TLVs
Supreeth Herle3b342c22020-03-24 16:15:02 +0100551 tlvs = TLV_parser(addr_tlv_bytes)
Supreeth Herled572ede2020-03-22 09:55:04 +0100552
553 for tlv in tlvs:
554 # tlv = (T, L, [V])
555 # T = Tag
556 # L = Length
557 # [V] = List of value
558
559 # Invalid Tag value scenario
560 if tlv[0] != 0x80:
561 continue
562
Supreeth Herled6a5ec52020-06-01 12:27:51 +0200563 # Empty field - Zero length
564 if tlv[1] == 0:
565 continue
566
Supreeth Herled572ede2020-03-22 09:55:04 +0100567 # First byte in the value has the address type
568 addr_type = tlv[2][0]
Supreeth Herle43fd03b2020-03-25 14:52:46 +0100569 # TODO: Support parsing of IPv6
Supreeth Herle3b342c22020-03-24 16:15:02 +0100570 # Address Type: 0x00 (FQDN), 0x01 (IPv4), 0x02 (IPv6), other (Reserved)
Supreeth Herled572ede2020-03-22 09:55:04 +0100571 if addr_type == 0x00: #FQDN
572 # Skip address tye byte i.e. first byte in value list
573 content = tlv[2][1:]
574 s += "\t%s # %s\n" % (i2h(content), i2s(content))
Supreeth Herle43fd03b2020-03-25 14:52:46 +0100575 elif addr_type == 0x01: #IPv4
576 # Skip address tye byte i.e. first byte in value list
577 # Skip the unused byte in Octect 4 after address type byte as per 3GPP TS 31.102
578 ipv4 = tlv[2][2:]
579 content = '.'.join(str(x) for x in ipv4)
580 s += "\t%s # %s\n" % (i2h(ipv4), content)
Supreeth Herled572ede2020-03-22 09:55:04 +0100581
582 return s
Philipp Maierff84c232020-05-12 17:24:18 +0200583
Supreeth Herle3b342c22020-03-24 16:15:02 +0100584def enc_addr_tlv(addr, addr_type='00'):
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100585 """
Supreeth Herle3b342c22020-03-24 16:15:02 +0100586 Encode address TLV object used in EF.P-CSCF Address, EF.ePDGId and EF.ePDGIdEm.
587 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 +0100588
589 Default values:
Supreeth Herle3b342c22020-03-24 16:15:02 +0100590 - addr_type: 00 - FQDN format of Address
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100591 """
592
593 s = ""
594
Supreeth Herle654eca72020-03-25 14:25:38 +0100595 # TODO: Encoding of IPv6 address
596 if addr_type == '00': #FQDN
Supreeth Herle3b342c22020-03-24 16:15:02 +0100597 hex_str = s2h(addr)
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100598 s += '80' + ('%02x' % ((len(hex_str)//2)+1)) + '00' + hex_str
Supreeth Herle654eca72020-03-25 14:25:38 +0100599 elif addr_type == '01': #IPv4
600 ipv4_list = addr.split('.')
601 ipv4_str = ""
602 for i in ipv4_list:
603 ipv4_str += ('%02x' % (int(i)))
604
605 # Unused bytes shall be set to 'ff'. i.e 4th Octet after Address Type is not used
606 # IPv4 Address is in octet 5 to octet 8 of the TLV data object
607 s += '80' + ('%02x' % ((len(ipv4_str)//2)+2)) + '01' + 'ff' + ipv4_str
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100608
609 return s
610
Harald Welte52255572021-04-03 09:56:32 +0200611def is_hex(string:str, minlen:int=2, maxlen:Optional[int]=None) -> bool:
Philipp Maier47236502021-03-09 21:28:25 +0100612 """
613 Check if a string is a valid hexstring
614 """
615
616 # Filter obviously bad strings
617 if not string:
618 return False
619 if len(string) < minlen or minlen < 2:
620 return False
621 if len(string) % 2:
622 return False
623 if maxlen and len(string) > maxlen:
624 return False
625
626 # Try actual encoding to be sure
627 try:
628 try_encode = h2b(string)
629 return True
630 except:
631 return False
632
Harald Welte52255572021-04-03 09:56:32 +0200633def sanitize_pin_adm(pin_adm, pin_adm_hex = None) -> Hexstr:
Philipp Maiere8536c02020-05-11 21:35:01 +0200634 """
635 The ADM pin can be supplied either in its hexadecimal form or as
636 ascii string. This function checks the supplied opts parameter and
Harald Welte236a65f2021-04-03 10:20:11 +0200637 returns the pin_adm as hex encoded string, regardless in which form
Philipp Maiere8536c02020-05-11 21:35:01 +0200638 it was originally supplied by the user
639 """
640
Harald Welte79b5ba42021-01-08 21:22:38 +0100641 if pin_adm is not None:
642 if len(pin_adm) <= 8:
643 pin_adm = ''.join(['%02x'%(ord(x)) for x in pin_adm])
Philipp Maiere8536c02020-05-11 21:35:01 +0200644 pin_adm = rpad(pin_adm, 16)
645
646 else:
647 raise ValueError("PIN-ADM needs to be <=8 digits (ascii)")
648
Harald Welte79b5ba42021-01-08 21:22:38 +0100649 if pin_adm_hex is not None:
650 if len(pin_adm_hex) == 16:
651 pin_adm = pin_adm_hex
Philipp Maiere8536c02020-05-11 21:35:01 +0200652 # Ensure that it's hex-encoded
653 try:
654 try_encode = h2b(pin_adm)
655 except ValueError:
656 raise ValueError("PIN-ADM needs to be hex encoded using this option")
657 else:
658 raise ValueError("PIN-ADM needs to be exactly 16 digits (hex encoded)")
659
660 return pin_adm
661
Supreeth Herle95ec7722020-03-24 13:09:03 +0100662def enc_ePDGSelection(hexstr, mcc, mnc, epdg_priority='0001', epdg_fqdn_format='00'):
663 """
664 Encode ePDGSelection so it can be stored at EF.ePDGSelection or EF.ePDGSelectionEm.
665 See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
666
667 Default values:
668 - epdg_priority: '0001' - 1st Priority
669 - epdg_fqdn_format: '00' - Operator Identifier FQDN
670 """
671
672 plmn1 = enc_plmn(mcc, mnc) + epdg_priority + epdg_fqdn_format
673 # TODO: Handle encoding of Length field for length more than 127 Bytes
674 content = '80' + ('%02x' % (len(plmn1)//2)) + plmn1
675 content = rpad(content, len(hexstr))
676 return content
677
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100678def dec_ePDGSelection(sixhexbytes):
679 """
680 Decode ePDGSelection to get EF.ePDGSelection or EF.ePDGSelectionEm.
681 See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
682 """
683
684 res = {'mcc': 0, 'mnc': 0, 'epdg_priority': 0, 'epdg_fqdn_format': ''}
685 plmn_chars = 6
686 epdg_priority_chars = 4
687 epdg_fqdn_format_chars = 2
688 # first three bytes (six ascii hex chars)
689 plmn_str = sixhexbytes[:plmn_chars]
690 # two bytes after first three bytes
691 epdg_priority_str = sixhexbytes[plmn_chars:plmn_chars + epdg_priority_chars]
692 # one byte after first five bytes
693 epdg_fqdn_format_str = sixhexbytes[plmn_chars + epdg_priority_chars:plmn_chars + epdg_priority_chars + epdg_fqdn_format_chars]
694 res['mcc'] = dec_mcc_from_plmn(plmn_str)
695 res['mnc'] = dec_mnc_from_plmn(plmn_str)
696 res['epdg_priority'] = epdg_priority_str
697 res['epdg_fqdn_format'] = epdg_fqdn_format_str == '00' and 'Operator Identifier FQDN' or 'Location based FQDN'
698 return res
699
700def format_ePDGSelection(hexstr):
701 ePDGSelection_info_tag_chars = 2
702 ePDGSelection_info_tag_str = hexstr[:2]
herlesupreeth3a261d32021-01-05 09:20:11 +0100703 s = ""
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100704 # Minimum length
705 len_chars = 2
706 # TODO: Need to determine length properly - definite length support only
707 # Inconsistency in spec: 3GPP TS 31.102 version 15.2.0 Release 15, 4.2.104
708 # As per spec, length is 5n, n - number of PLMNs
709 # But, each PLMN entry is made of PLMN (3 Bytes) + ePDG Priority (2 Bytes) + ePDG FQDN format (1 Byte)
710 # Totalling to 6 Bytes, maybe length should be 6n
711 len_str = hexstr[ePDGSelection_info_tag_chars:ePDGSelection_info_tag_chars+len_chars]
herlesupreeth3a261d32021-01-05 09:20:11 +0100712
713 # Not programmed scenario
714 if int(len_str, 16) == 255 or int(ePDGSelection_info_tag_str, 16) == 255:
715 len_chars = 0
716 ePDGSelection_info_tag_chars = 0
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100717 if len_str[0] == '8':
718 # The bits 7 to 1 denotes the number of length octets if length > 127
719 if int(len_str[1]) > 0:
720 # Update number of length octets
721 len_chars = len_chars * int(len_str[1])
722 len_str = hexstr[ePDGSelection_info_tag_chars:len_chars]
723
724 content_str = hexstr[ePDGSelection_info_tag_chars+len_chars:]
725 # Right pad to prevent index out of range - multiple of 6 bytes
726 content_str = rpad(content_str, len(content_str) + (12 - (len(content_str) % 12)))
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100727 for rec_data in hexstr_to_Nbytearr(content_str, 6):
728 rec_info = dec_ePDGSelection(rec_data)
729 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
730 rec_str = "unused"
731 else:
732 rec_str = "MCC: %03d MNC: %03d ePDG Priority: %s ePDG FQDN format: %s" % \
733 (rec_info['mcc'], rec_info['mnc'], rec_info['epdg_priority'], rec_info['epdg_fqdn_format'])
734 s += "\t%s # %s\n" % (rec_data, rec_str)
735 return s
Supreeth Herle556b0fe2020-03-25 11:26:57 +0100736
737def get_addr_type(addr):
738 """
739 Validates the given address and returns it's type (FQDN or IPv4 or IPv6)
740 Return: 0x00 (FQDN), 0x01 (IPv4), 0x02 (IPv6), None (Bad address argument given)
741
742 TODO: Handle IPv6
743 """
744
745 # Empty address string
746 if not len(addr):
747 return None
748
Supreeth Herle556b0fe2020-03-25 11:26:57 +0100749 addr_list = addr.split('.')
750
751 # Check for IPv4/IPv6
752 try:
753 import ipaddress
754 # Throws ValueError if addr is not correct
Denis 'GNUtoo' Carikli79f5b602020-02-15 04:02:57 +0700755 ipa = ipaddress.ip_address(addr)
Supreeth Herle556b0fe2020-03-25 11:26:57 +0100756
757 if ipa.version == 4:
758 return 0x01
759 elif ipa.version == 6:
760 return 0x02
761 except Exception as e:
762 invalid_ipv4 = True
763 for i in addr_list:
764 # Invalid IPv4 may qualify for a valid FQDN, so make check here
765 # e.g. 172.24.15.300
766 import re
767 if not re.match('^[0-9_]+$', i):
768 invalid_ipv4 = False
769 break
770
771 if invalid_ipv4:
772 return None
773
774 fqdn_flag = True
775 for i in addr_list:
776 # Only Alpha-numeric characters and hyphen - RFC 1035
777 import re
778 if not re.match("^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)?$", i):
779 fqdn_flag = False
780 break
781
782 # FQDN
783 if fqdn_flag:
784 return 0x00
785
786 return None
Harald Welte67d551a2021-01-21 14:50:01 +0100787
Harald Welte1e456572021-04-02 17:16:30 +0200788def sw_match(sw:str, pattern:str) -> bool:
Harald Welte67d551a2021-01-21 14:50:01 +0100789 """Match given SW against given pattern."""
790 # Create a masked version of the returned status word
791 sw_lower = sw.lower()
792 sw_masked = ""
793 for i in range(0, 4):
Philipp Maier78e32f22021-03-22 20:39:24 +0100794 if pattern[i] == '?':
Harald Welte67d551a2021-01-21 14:50:01 +0100795 sw_masked = sw_masked + '?'
Philipp Maier78e32f22021-03-22 20:39:24 +0100796 elif pattern[i] == 'x':
Harald Welte67d551a2021-01-21 14:50:01 +0100797 sw_masked = sw_masked + 'x'
798 else:
799 sw_masked = sw_masked + sw_lower[i]
Philipp Maier78e32f22021-03-22 20:39:24 +0100800 # Compare the masked version against the pattern
Harald Welte67d551a2021-01-21 14:50:01 +0100801 return sw_masked == pattern
Philipp Maier5d3e2592021-02-22 17:22:16 +0100802
Harald Welteee3501f2021-04-02 13:00:18 +0200803def tabulate_str_list(str_list, width:int = 79, hspace:int = 2, lspace:int = 1,
Harald Welte52255572021-04-03 09:56:32 +0200804 align_left:bool = True) -> str:
Harald Welteee3501f2021-04-02 13:00:18 +0200805 """Pretty print a list of strings into a tabulated form.
806
807 Args:
808 width : total width in characters per line
809 space : horizontal space between cells
810 lspace : number of spaces before row
811 align_lef : Align text to the left side
812 Returns:
813 multi-line string containing formatted table
814 """
Philipp Maier5d3e2592021-02-22 17:22:16 +0100815 if str_list == None:
816 return ""
817 if len(str_list) <= 0:
818 return ""
819 longest_str = max(str_list, key=len)
820 cellwith = len(longest_str) + hspace
821 cols = width // cellwith
822 rows = (len(str_list) - 1) // cols + 1
823 table = []
824 for i in iter(range(rows)):
825 str_list_row = str_list[i::rows]
826 if (align_left):
827 format_str_cell = '%%-%ds'
828 else:
829 format_str_cell = '%%%ds'
830 format_str_row = (format_str_cell % cellwith) * len(str_list_row)
831 format_str_row = (" " * lspace) + format_str_row
832 table.append(format_str_row % tuple(str_list_row))
833 return '\n'.join(table)