blob: a733d876a7bb428161dc9c64e5a15b693c88ae84 [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
24def h2b(s):
25 return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])])
26
27def b2h(s):
28 return ''.join(['%02x'%ord(x) for x in s])
29
30def h2i(s):
31 return [(int(x,16)<<4)+int(y,16) for x,y in zip(s[0::2], s[1::2])]
32
33def i2h(s):
34 return ''.join(['%02x'%(x) for x in s])
35
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030036def h2s(s):
Vadim Yanitskiyeb06b452020-05-10 02:32:46 +070037 return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])
38 if int(x + y, 16) != 0xff])
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030039
40def s2h(s):
41 return b2h(s)
42
Supreeth Herle7d77d2d2020-05-11 09:07:08 +020043# List of bytes to string
44def i2s(s):
45 return ''.join([chr(x) for x in s])
46
Sylvain Munaut76504e02010-12-07 00:24:32 +010047def swap_nibbles(s):
48 return ''.join([x+y for x,y in zip(s[1::2], s[0::2])])
49
50def rpad(s, l, c='f'):
51 return s + c * (l - len(s))
52
53def lpad(s, l, c='f'):
54 return c * (l - len(s)) + s
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040055
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020056def half_round_up(n):
57 return (n + 1)//2
58
59# IMSI encoded format:
60# For IMSI 0123456789ABCDE:
61#
62# | byte 1 | 2 upper | 2 lower | 3 upper | 3 lower | ... | 9 upper | 9 lower |
63# | length in bytes | 0 | odd/even | 2 | 1 | ... | E | D |
64#
65# If the IMSI is less than 15 characters, it should be padded with 'f' from the end.
66#
67# The length is the total number of bytes used to encoded the IMSI. This includes the odd/even
68# parity bit. E.g. an IMSI of length 14 is 8 bytes long, not 7, as it uses bytes 2 to 9 to
69# encode itself.
70#
71# Because of this, an odd length IMSI fits exactly into len(imsi) + 1 // 2 bytes, whereas an
72# even length IMSI only uses half of the last byte.
73
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040074def enc_imsi(imsi):
75 """Converts a string imsi into the value of the EF"""
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020076 l = half_round_up(len(imsi) + 1) # Required bytes - include space for odd/even indicator
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040077 oe = len(imsi) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020078 ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe<<3)|1, rpad(imsi, 15)))
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040079 return ei
80
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040081def dec_imsi(ef):
82 """Converts an EF value to the imsi string representation"""
83 if len(ef) < 4:
84 return None
Pau Espin Pedrol665bd222017-12-29 20:30:35 +010085 l = int(ef[0:2], 16) * 2 # Length of the IMSI string
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020086 l = l - 1 # Encoded length byte includes oe nibble
87 swapped = swap_nibbles(ef[2:]).rstrip('f')
Philipp Maiercd3d6262020-05-11 21:41:56 +020088 if len(swapped) < 1:
89 return None
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040090 oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020091 if not oe:
92 # if even, only half of last byte was used
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040093 l = l-1
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020094 if l != len(swapped) - 1:
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040095 return None
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020096 imsi = swapped[1:]
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040097 return imsi
98
99def dec_iccid(ef):
100 return swap_nibbles(ef).strip('f')
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400101
102def enc_iccid(iccid):
103 return swap_nibbles(rpad(iccid, 20))
104
105def enc_plmn(mcc, mnc):
Alexander Chemerisdddbf522017-07-18 16:49:59 +0300106 """Converts integer MCC/MNC into 3 bytes for EF"""
Harald Welte7f1d3c42020-05-12 21:12:44 +0200107 if len(mnc) == 2:
108 mnc = "F%s" % mnc
109 return swap_nibbles("%s%s" % (mcc, mnc))
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +0300110
111def dec_spn(ef):
112 byte1 = int(ef[0:2])
113 hplmn_disp = (byte1&0x01 == 0x01)
114 oplmn_disp = (byte1&0x02 == 0x02)
115 name = h2s(ef[2:])
116 return (name, hplmn_disp, oplmn_disp)
117
118def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
119 byte1 = 0x00
120 if hplmn_disp: byte1 = byte1|0x01
121 if oplmn_disp: byte1 = byte1|0x02
122 return i2h([byte1])+s2h(name)
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900123
Supreeth Herlef3948532020-03-24 12:23:51 +0100124def hexstr_to_Nbytearr(s, nbytes):
125 return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2)) ]
126
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100127# Accepts hex string representing three bytes
128def dec_mcc_from_plmn(plmn):
129 ia = h2i(plmn)
130 digit1 = ia[0] & 0x0F # 1st byte, LSB
131 digit2 = (ia[0] & 0xF0) >> 4 # 1st byte, MSB
132 digit3 = ia[1] & 0x0F # 2nd byte, LSB
133 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
134 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100135 return derive_mcc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100136
137def dec_mnc_from_plmn(plmn):
138 ia = h2i(plmn)
herlesupreethbdf3d352021-02-11 06:59:29 +0100139 digit1 = (ia[1] & 0xF0) >>4 # 2nd byte, MSB
140 digit2 = ia[2] & 0x0F # 3rd byte, LSB
141 digit3 = (ia[2] & 0xF0) >> 4 # 3nd byte, MSB
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100142 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
143 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100144 return derive_mnc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100145
146def dec_act(twohexbytes):
147 act_list = [
148 {'bit': 15, 'name': "UTRAN"},
149 {'bit': 14, 'name': "E-UTRAN"},
150 {'bit': 7, 'name': "GSM"},
151 {'bit': 6, 'name': "GSM COMPACT"},
152 {'bit': 5, 'name': "cdma2000 HRPD"},
153 {'bit': 4, 'name': "cdma2000 1xRTT"},
154 ]
155 ia = h2i(twohexbytes)
156 u16t = (ia[0] << 8)|ia[1]
157 sel = []
158 for a in act_list:
159 if u16t & (1 << a['bit']):
160 sel.append(a['name'])
161 return sel
162
163def dec_xplmn_w_act(fivehexbytes):
164 res = {'mcc': 0, 'mnc': 0, 'act': []}
165 plmn_chars = 6
166 act_chars = 4
167 plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
168 act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
169 res['mcc'] = dec_mcc_from_plmn(plmn_str)
170 res['mnc'] = dec_mnc_from_plmn(plmn_str)
171 res['act'] = dec_act(act_str)
172 return res
173
174def format_xplmn_w_act(hexstr):
175 s = ""
herlesupreeth45fa6042020-09-18 15:32:20 +0200176 for rec_data in hexstr_to_Nbytearr(hexstr, 5):
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100177 rec_info = dec_xplmn_w_act(rec_data)
178 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
179 rec_str = "unused"
180 else:
Supreeth Herled24f1632019-11-30 10:37:09 +0100181 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 +0100182 s += "\t%s # %s\n" % (rec_data, rec_str)
183 return s
184
Sebastian Vivianie61170c2020-06-03 08:57:00 +0100185def dec_loci(hexstr):
186 res = {'tmsi': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'status': 0}
187 res['tmsi'] = hexstr[:8]
188 res['mcc'] = dec_mcc_from_plmn(hexstr[8:14])
189 res['mnc'] = dec_mnc_from_plmn(hexstr[8:14])
190 res['lac'] = hexstr[14:18]
191 res['status'] = h2i(hexstr[20:22])
192 return res
193
194def dec_psloci(hexstr):
195 res = {'p-tmsi': '', 'p-tmsi-sig': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'rac': '', 'status': 0}
196 res['p-tmsi'] = hexstr[:8]
197 res['p-tmsi-sig'] = hexstr[8:14]
198 res['mcc'] = dec_mcc_from_plmn(hexstr[14:20])
199 res['mnc'] = dec_mnc_from_plmn(hexstr[14:20])
200 res['lac'] = hexstr[20:24]
201 res['rac'] = hexstr[24:26]
202 res['status'] = h2i(hexstr[26:28])
203 return res
204
205def dec_epsloci(hexstr):
206 res = {'guti': '', 'mcc': 0, 'mnc': 0, 'tac': '', 'status': 0}
207 res['guti'] = hexstr[:24]
208 res['tai'] = hexstr[24:34]
209 res['mcc'] = dec_mcc_from_plmn(hexstr[24:30])
210 res['mnc'] = dec_mnc_from_plmn(hexstr[24:30])
211 res['tac'] = hexstr[30:34]
212 res['status'] = h2i(hexstr[34:36])
213 return res
214
Harald Welteca673942020-06-03 15:19:40 +0200215def dec_xplmn(threehexbytes):
216 res = {'mcc': 0, 'mnc': 0, 'act': []}
217 plmn_chars = 6
218 plmn_str = threehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
219 res['mcc'] = dec_mcc_from_plmn(plmn_str)
220 res['mnc'] = dec_mnc_from_plmn(plmn_str)
221 return res
222
223def format_xplmn(hexstr):
224 s = ""
herlesupreeth45fa6042020-09-18 15:32:20 +0200225 for rec_data in hexstr_to_Nbytearr(hexstr, 3):
Harald Welteca673942020-06-03 15:19:40 +0200226 rec_info = dec_xplmn(rec_data)
227 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
228 rec_str = "unused"
229 else:
230 rec_str = "MCC: %03d MNC: %03d" % (rec_info['mcc'], rec_info['mnc'])
231 s += "\t%s # %s\n" % (rec_data, rec_str)
232 return s
233
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900234def derive_milenage_opc(ki_hex, op_hex):
235 """
236 Run the milenage algorithm to calculate OPC from Ki and OP
237 """
238 from Crypto.Cipher import AES
239 from Crypto.Util.strxor import strxor
240 from pySim.utils import b2h
241
242 # We pass in hex string and now need to work on bytes
243 aes = AES.new(h2b(ki_hex))
244 opc_bytes = aes.encrypt(h2b(op_hex))
245 return b2h(strxor(opc_bytes, h2b(op_hex)))
246
247def calculate_luhn(cc):
248 """
249 Calculate Luhn checksum used in e.g. ICCID and IMEI
250 """
Daniel Willmanndd014ea2020-10-19 10:35:11 +0200251 num = list(map(int, str(cc)))
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900252 check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
253 return 0 if check_digit == 10 else check_digit
Philipp Maier7592eee2019-09-12 13:03:23 +0200254
255def mcc_from_imsi(imsi):
256 """
257 Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
258 """
259 if imsi == None:
260 return None
261
262 if len(imsi) > 3:
263 return imsi[:3]
264 else:
265 return None
266
267def mnc_from_imsi(imsi, long=False):
268 """
269 Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
270 """
271 if imsi == None:
272 return None
273
274 if len(imsi) > 3:
275 if long:
276 return imsi[3:6]
277 else:
278 return imsi[3:5]
279 else:
280 return None
Supreeth Herled24f1632019-11-30 10:37:09 +0100281
282def derive_mcc(digit1, digit2, digit3):
283 """
284 Derive decimal representation of the MCC (Mobile Country Code)
285 from three given digits.
286 """
287
288 mcc = 0
289
290 if digit1 != 0x0f:
291 mcc += digit1 * 100
292 if digit2 != 0x0f:
293 mcc += digit2 * 10
294 if digit3 != 0x0f:
295 mcc += digit3
296
297 return mcc
298
299def derive_mnc(digit1, digit2, digit3=0x0f):
300 """
301 Derive decimal representation of the MNC (Mobile Network Code)
302 from two or (optionally) three given digits.
303 """
304
305 mnc = 0
306
307 # 3-rd digit is optional for the MNC. If present
308 # the algorythm is the same as for the MCC.
309 if digit3 != 0x0f:
310 return derive_mcc(digit1, digit2, digit3)
311
312 if digit1 != 0x0f:
313 mnc += digit1 * 10
314 if digit2 != 0x0f:
315 mnc += digit2
316
317 return mnc
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100318
319def dec_msisdn(ef_msisdn):
320 """
321 Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
322 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
323 """
324
325 # Convert from str to (kind of) 'bytes'
326 ef_msisdn = h2b(ef_msisdn)
327
328 # Make sure mandatory fields are present
329 if len(ef_msisdn) < 14:
330 raise ValueError("EF.MSISDN is too short")
331
332 # Skip optional Alpha Identifier
333 xlen = len(ef_msisdn) - 14
334 msisdn_lhv = ef_msisdn[xlen:]
335
336 # Parse the length (in bytes) of the BCD encoded number
337 bcd_len = ord(msisdn_lhv[0])
338 # BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
339 if bcd_len == 0xff:
340 return None
341 elif bcd_len > 11 or bcd_len < 1:
342 raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
343
344 # Parse ToN / NPI
345 ton = (ord(msisdn_lhv[1]) >> 4) & 0x07
346 npi = ord(msisdn_lhv[1]) & 0x0f
347 bcd_len -= 1
348
349 # No MSISDN?
350 if not bcd_len:
351 return (npi, ton, None)
352
353 msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
354 # International number 10.5.118/3GPP TS 24.008
Vadim Yanitskiy7ba24282020-02-27 00:04:13 +0700355 if ton == 0x01:
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100356 msisdn = '+' + msisdn
357
358 return (npi, ton, msisdn)
Supreeth Herle5a541012019-12-22 08:59:16 +0100359
360def enc_msisdn(msisdn, npi=0x01, ton=0x03):
361 """
362 Encode MSISDN as LHV so it can be stored to EF.MSISDN.
363 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
364
365 Default NPI / ToN values:
366 - NPI: ISDN / telephony numbering plan (E.164 / E.163),
367 - ToN: network specific or international number (if starts with '+').
368 """
369
370 # Leading '+' indicates International Number
371 if msisdn[0] == '+':
372 msisdn = msisdn[1:]
373 ton = 0x01
374
375 # Append 'f' padding if number of digits is odd
376 if len(msisdn) % 2 > 0:
377 msisdn += 'f'
378
379 # BCD length also includes NPI/ToN header
380 bcd_len = len(msisdn) // 2 + 1
381 npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
382 bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
383
384 return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
Supreeth Herle441c4a72020-03-24 10:19:15 +0100385
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200386def dec_st(st, table="sim"):
387 """
388 Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
389 """
390
Supreeth Herledf330372020-04-20 14:48:55 +0200391 if table == "isim":
392 from pySim.ts_31_103 import EF_IST_map
393 lookup_map = EF_IST_map
394 elif table == "usim":
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200395 from pySim.ts_31_102 import EF_UST_map
396 lookup_map = EF_UST_map
397 else:
398 from pySim.ts_51_011 import EF_SST_map
399 lookup_map = EF_SST_map
400
401 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
402
403 avail_st = ""
404 # Get each byte and check for available services
405 for i in range(0, len(st_bytes)):
406 # Byte i contains info about Services num (8i+1) to num (8i+8)
407 byte = int(st_bytes[i], 16)
408 # Services in each byte are in order MSB to LSB
409 # MSB - Service (8i+8)
410 # LSB - Service (8i+1)
411 for j in range(1, 9):
412 if byte&0x01 == 0x01 and ((8*i) + j in lookup_map):
413 # Byte X contains info about Services num (8X-7) to num (8X)
414 # bit = 1: service available
415 # bit = 0: service not available
416 avail_st += '\tService %d - %s\n' % ((8*i) + j, lookup_map[(8*i) + j])
417 byte = byte >> 1
418 return avail_st
Supreeth Herle98370552020-05-11 09:04:41 +0200419
420def first_TLV_parser(bytelist):
421 '''
422 first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
423
424 parses first TLV format record in a list of bytelist
425 returns a 3-Tuple: Tag, Length, Value
426 Value is a list of bytes
427 parsing of length is ETSI'style 101.220
428 '''
429 Tag = bytelist[0]
430 if bytelist[1] == 0xFF:
431 Len = bytelist[2]*256 + bytelist[3]
432 Val = bytelist[4:4+Len]
433 else:
434 Len = bytelist[1]
435 Val = bytelist[2:2+Len]
436 return (Tag, Len, Val)
437
438def TLV_parser(bytelist):
439 '''
440 TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
441
442 loops on the input list of bytes with the "first_TLV_parser()" function
443 returns a list of 3-Tuples
444 '''
445 ret = []
446 while len(bytelist) > 0:
447 T, L, V = first_TLV_parser(bytelist)
448 if T == 0xFF:
449 # padding bytes
450 break
451 ret.append( (T, L, V) )
452 # need to manage length of L
453 if L > 0xFE:
454 bytelist = bytelist[ L+4 : ]
455 else:
456 bytelist = bytelist[ L+2 : ]
457 return ret
Supreeth Herled572ede2020-03-22 09:55:04 +0100458
Supreeth Herled84daa12020-03-24 12:20:40 +0100459def enc_st(st, service, state=1):
460 """
461 Encodes the EF S/U/IST/EST and returns the updated Service Table
462
463 Parameters:
464 st - Current value of SIM/USIM/ISIM Service Table
465 service - Service Number to encode as activated/de-activated
466 state - 1 mean activate, 0 means de-activate
467
468 Returns:
469 s - Modified value of SIM/USIM/ISIM Service Table
470
471 Default values:
472 - state: 1 - Sets the particular Service bit to 1
473 """
474 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
475
476 s = ""
477 # Check whether the requested service is present in each byte
478 for i in range(0, len(st_bytes)):
479 # Byte i contains info about Services num (8i+1) to num (8i+8)
480 if service in range((8*i) + 1, (8*i) + 9):
481 byte = int(st_bytes[i], 16)
482 # Services in each byte are in order MSB to LSB
483 # MSB - Service (8i+8)
484 # LSB - Service (8i+1)
485 mod_byte = 0x00
486 # Copy bit by bit contents of byte to mod_byte with modified bit
487 # for requested service
488 for j in range(1, 9):
489 mod_byte = mod_byte >> 1
490 if service == (8*i) + j:
491 mod_byte = state == 1 and mod_byte|0x80 or mod_byte&0x7f
492 else:
493 mod_byte = byte&0x01 == 0x01 and mod_byte|0x80 or mod_byte&0x7f
494 byte = byte >> 1
495
496 s += ('%02x' % (mod_byte))
497 else:
498 s += st_bytes[i]
499
500 return s
501
Supreeth Herle3b342c22020-03-24 16:15:02 +0100502def dec_addr_tlv(hexstr):
Supreeth Herled572ede2020-03-22 09:55:04 +0100503 """
Supreeth Herle3b342c22020-03-24 16:15:02 +0100504 Decode hex string to get EF.P-CSCF Address or EF.ePDGId or EF.ePDGIdEm.
505 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 +0100506 """
507
508 # Convert from hex str to int bytes list
Supreeth Herle3b342c22020-03-24 16:15:02 +0100509 addr_tlv_bytes = h2i(hexstr)
Supreeth Herled572ede2020-03-22 09:55:04 +0100510
511 s = ""
512
513 # Get list of tuples containing parsed TLVs
Supreeth Herle3b342c22020-03-24 16:15:02 +0100514 tlvs = TLV_parser(addr_tlv_bytes)
Supreeth Herled572ede2020-03-22 09:55:04 +0100515
516 for tlv in tlvs:
517 # tlv = (T, L, [V])
518 # T = Tag
519 # L = Length
520 # [V] = List of value
521
522 # Invalid Tag value scenario
523 if tlv[0] != 0x80:
524 continue
525
Supreeth Herled6a5ec52020-06-01 12:27:51 +0200526 # Empty field - Zero length
527 if tlv[1] == 0:
528 continue
529
Supreeth Herled572ede2020-03-22 09:55:04 +0100530 # First byte in the value has the address type
531 addr_type = tlv[2][0]
Supreeth Herle43fd03b2020-03-25 14:52:46 +0100532 # TODO: Support parsing of IPv6
Supreeth Herle3b342c22020-03-24 16:15:02 +0100533 # Address Type: 0x00 (FQDN), 0x01 (IPv4), 0x02 (IPv6), other (Reserved)
Supreeth Herled572ede2020-03-22 09:55:04 +0100534 if addr_type == 0x00: #FQDN
535 # Skip address tye byte i.e. first byte in value list
536 content = tlv[2][1:]
537 s += "\t%s # %s\n" % (i2h(content), i2s(content))
Supreeth Herle43fd03b2020-03-25 14:52:46 +0100538 elif addr_type == 0x01: #IPv4
539 # Skip address tye byte i.e. first byte in value list
540 # Skip the unused byte in Octect 4 after address type byte as per 3GPP TS 31.102
541 ipv4 = tlv[2][2:]
542 content = '.'.join(str(x) for x in ipv4)
543 s += "\t%s # %s\n" % (i2h(ipv4), content)
Supreeth Herled572ede2020-03-22 09:55:04 +0100544
545 return s
Philipp Maierff84c232020-05-12 17:24:18 +0200546
Supreeth Herle3b342c22020-03-24 16:15:02 +0100547def enc_addr_tlv(addr, addr_type='00'):
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100548 """
Supreeth Herle3b342c22020-03-24 16:15:02 +0100549 Encode address TLV object used in EF.P-CSCF Address, EF.ePDGId and EF.ePDGIdEm.
550 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 +0100551
552 Default values:
Supreeth Herle3b342c22020-03-24 16:15:02 +0100553 - addr_type: 00 - FQDN format of Address
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100554 """
555
556 s = ""
557
Supreeth Herle654eca72020-03-25 14:25:38 +0100558 # TODO: Encoding of IPv6 address
559 if addr_type == '00': #FQDN
Supreeth Herle3b342c22020-03-24 16:15:02 +0100560 hex_str = s2h(addr)
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100561 s += '80' + ('%02x' % ((len(hex_str)//2)+1)) + '00' + hex_str
Supreeth Herle654eca72020-03-25 14:25:38 +0100562 elif addr_type == '01': #IPv4
563 ipv4_list = addr.split('.')
564 ipv4_str = ""
565 for i in ipv4_list:
566 ipv4_str += ('%02x' % (int(i)))
567
568 # Unused bytes shall be set to 'ff'. i.e 4th Octet after Address Type is not used
569 # IPv4 Address is in octet 5 to octet 8 of the TLV data object
570 s += '80' + ('%02x' % ((len(ipv4_str)//2)+2)) + '01' + 'ff' + ipv4_str
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100571
572 return s
573
Harald Welte79b5ba42021-01-08 21:22:38 +0100574def sanitize_pin_adm(pin_adm, pin_adm_hex = None):
Philipp Maiere8536c02020-05-11 21:35:01 +0200575 """
576 The ADM pin can be supplied either in its hexadecimal form or as
577 ascii string. This function checks the supplied opts parameter and
578 returns the pin_adm as hex encoded string, regardles in which form
579 it was originally supplied by the user
580 """
581
Harald Welte79b5ba42021-01-08 21:22:38 +0100582 if pin_adm is not None:
583 if len(pin_adm) <= 8:
584 pin_adm = ''.join(['%02x'%(ord(x)) for x in pin_adm])
Philipp Maiere8536c02020-05-11 21:35:01 +0200585 pin_adm = rpad(pin_adm, 16)
586
587 else:
588 raise ValueError("PIN-ADM needs to be <=8 digits (ascii)")
589
Harald Welte79b5ba42021-01-08 21:22:38 +0100590 if pin_adm_hex is not None:
591 if len(pin_adm_hex) == 16:
592 pin_adm = pin_adm_hex
Philipp Maiere8536c02020-05-11 21:35:01 +0200593 # Ensure that it's hex-encoded
594 try:
595 try_encode = h2b(pin_adm)
596 except ValueError:
597 raise ValueError("PIN-ADM needs to be hex encoded using this option")
598 else:
599 raise ValueError("PIN-ADM needs to be exactly 16 digits (hex encoded)")
600
601 return pin_adm
602
Philipp Maierff84c232020-05-12 17:24:18 +0200603def init_reader(opts):
604 """
605 Init card reader driver
606 """
Philipp Maierc8caec22021-02-22 16:07:53 +0100607 try:
608 if opts.pcsc_dev is not None:
609 print("Using PC/SC reader interface")
610 from pySim.transport.pcsc import PcscSimLink
611 sl = PcscSimLink(opts.pcsc_dev)
612 elif opts.osmocon_sock is not None:
613 print("Using Calypso-based (OsmocomBB) reader interface")
614 from pySim.transport.calypso import CalypsoSimLink
615 sl = CalypsoSimLink(sock_path=opts.osmocon_sock)
616 elif opts.modem_dev is not None:
617 print("Using modem for Generic SIM Access (3GPP TS 27.007)")
618 from pySim.transport.modem_atcmd import ModemATCommandLink
619 sl = ModemATCommandLink(device=opts.modem_dev, baudrate=opts.modem_baud)
620 else: # Serial reader is default
621 print("Using serial reader interface")
622 from pySim.transport.serial import SerialSimLink
623 sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
624 return sl
625 except Exception as e:
626 print("Card reader initialization failed with exception:\n" + str(e))
627 return None
Philipp Maierff84c232020-05-12 17:24:18 +0200628
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100629
Supreeth Herle95ec7722020-03-24 13:09:03 +0100630def enc_ePDGSelection(hexstr, mcc, mnc, epdg_priority='0001', epdg_fqdn_format='00'):
631 """
632 Encode ePDGSelection so it can be stored at EF.ePDGSelection or EF.ePDGSelectionEm.
633 See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
634
635 Default values:
636 - epdg_priority: '0001' - 1st Priority
637 - epdg_fqdn_format: '00' - Operator Identifier FQDN
638 """
639
640 plmn1 = enc_plmn(mcc, mnc) + epdg_priority + epdg_fqdn_format
641 # TODO: Handle encoding of Length field for length more than 127 Bytes
642 content = '80' + ('%02x' % (len(plmn1)//2)) + plmn1
643 content = rpad(content, len(hexstr))
644 return content
645
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100646def dec_ePDGSelection(sixhexbytes):
647 """
648 Decode ePDGSelection to get EF.ePDGSelection or EF.ePDGSelectionEm.
649 See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
650 """
651
652 res = {'mcc': 0, 'mnc': 0, 'epdg_priority': 0, 'epdg_fqdn_format': ''}
653 plmn_chars = 6
654 epdg_priority_chars = 4
655 epdg_fqdn_format_chars = 2
656 # first three bytes (six ascii hex chars)
657 plmn_str = sixhexbytes[:plmn_chars]
658 # two bytes after first three bytes
659 epdg_priority_str = sixhexbytes[plmn_chars:plmn_chars + epdg_priority_chars]
660 # one byte after first five bytes
661 epdg_fqdn_format_str = sixhexbytes[plmn_chars + epdg_priority_chars:plmn_chars + epdg_priority_chars + epdg_fqdn_format_chars]
662 res['mcc'] = dec_mcc_from_plmn(plmn_str)
663 res['mnc'] = dec_mnc_from_plmn(plmn_str)
664 res['epdg_priority'] = epdg_priority_str
665 res['epdg_fqdn_format'] = epdg_fqdn_format_str == '00' and 'Operator Identifier FQDN' or 'Location based FQDN'
666 return res
667
668def format_ePDGSelection(hexstr):
669 ePDGSelection_info_tag_chars = 2
670 ePDGSelection_info_tag_str = hexstr[:2]
herlesupreeth3a261d32021-01-05 09:20:11 +0100671 s = ""
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100672 # Minimum length
673 len_chars = 2
674 # TODO: Need to determine length properly - definite length support only
675 # Inconsistency in spec: 3GPP TS 31.102 version 15.2.0 Release 15, 4.2.104
676 # As per spec, length is 5n, n - number of PLMNs
677 # But, each PLMN entry is made of PLMN (3 Bytes) + ePDG Priority (2 Bytes) + ePDG FQDN format (1 Byte)
678 # Totalling to 6 Bytes, maybe length should be 6n
679 len_str = hexstr[ePDGSelection_info_tag_chars:ePDGSelection_info_tag_chars+len_chars]
herlesupreeth3a261d32021-01-05 09:20:11 +0100680
681 # Not programmed scenario
682 if int(len_str, 16) == 255 or int(ePDGSelection_info_tag_str, 16) == 255:
683 len_chars = 0
684 ePDGSelection_info_tag_chars = 0
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100685 if len_str[0] == '8':
686 # The bits 7 to 1 denotes the number of length octets if length > 127
687 if int(len_str[1]) > 0:
688 # Update number of length octets
689 len_chars = len_chars * int(len_str[1])
690 len_str = hexstr[ePDGSelection_info_tag_chars:len_chars]
691
692 content_str = hexstr[ePDGSelection_info_tag_chars+len_chars:]
693 # Right pad to prevent index out of range - multiple of 6 bytes
694 content_str = rpad(content_str, len(content_str) + (12 - (len(content_str) % 12)))
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100695 for rec_data in hexstr_to_Nbytearr(content_str, 6):
696 rec_info = dec_ePDGSelection(rec_data)
697 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
698 rec_str = "unused"
699 else:
700 rec_str = "MCC: %03d MNC: %03d ePDG Priority: %s ePDG FQDN format: %s" % \
701 (rec_info['mcc'], rec_info['mnc'], rec_info['epdg_priority'], rec_info['epdg_fqdn_format'])
702 s += "\t%s # %s\n" % (rec_data, rec_str)
703 return s
Supreeth Herle556b0fe2020-03-25 11:26:57 +0100704
705def get_addr_type(addr):
706 """
707 Validates the given address and returns it's type (FQDN or IPv4 or IPv6)
708 Return: 0x00 (FQDN), 0x01 (IPv4), 0x02 (IPv6), None (Bad address argument given)
709
710 TODO: Handle IPv6
711 """
712
713 # Empty address string
714 if not len(addr):
715 return None
716
717 import sys
718 # Handle python3 and python2 - unicode
719 if sys.version_info[0] < 3:
720 addr_str = unicode(addr)
721 else:
722 addr_str = addr
723
724 addr_list = addr.split('.')
725
726 # Check for IPv4/IPv6
727 try:
728 import ipaddress
729 # Throws ValueError if addr is not correct
730 ipa = ipaddress.ip_address(addr_str)
731
732 if ipa.version == 4:
733 return 0x01
734 elif ipa.version == 6:
735 return 0x02
736 except Exception as e:
737 invalid_ipv4 = True
738 for i in addr_list:
739 # Invalid IPv4 may qualify for a valid FQDN, so make check here
740 # e.g. 172.24.15.300
741 import re
742 if not re.match('^[0-9_]+$', i):
743 invalid_ipv4 = False
744 break
745
746 if invalid_ipv4:
747 return None
748
749 fqdn_flag = True
750 for i in addr_list:
751 # Only Alpha-numeric characters and hyphen - RFC 1035
752 import re
753 if not re.match("^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)?$", i):
754 fqdn_flag = False
755 break
756
757 # FQDN
758 if fqdn_flag:
759 return 0x00
760
761 return None