blob: a3e4cb14498882afe35ce1558684d77d6278c75b [file] [log] [blame]
Sylvain Munaut76504e02010-12-07 00:24:32 +01001#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4""" pySim: various utilities
5"""
6
7#
8# 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
24
25def h2b(s):
26 return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])])
27
28def b2h(s):
29 return ''.join(['%02x'%ord(x) for x in s])
30
31def h2i(s):
32 return [(int(x,16)<<4)+int(y,16) for x,y in zip(s[0::2], s[1::2])]
33
34def i2h(s):
35 return ''.join(['%02x'%(x) for x in s])
36
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030037def h2s(s):
Vadim Yanitskiyeb06b452020-05-10 02:32:46 +070038 return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])
39 if int(x + y, 16) != 0xff])
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030040
41def s2h(s):
42 return b2h(s)
43
Supreeth Herle7d77d2d2020-05-11 09:07:08 +020044# List of bytes to string
45def i2s(s):
46 return ''.join([chr(x) for x in s])
47
Sylvain Munaut76504e02010-12-07 00:24:32 +010048def swap_nibbles(s):
49 return ''.join([x+y for x,y in zip(s[1::2], s[0::2])])
50
51def rpad(s, l, c='f'):
52 return s + c * (l - len(s))
53
54def lpad(s, l, c='f'):
55 return c * (l - len(s)) + s
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040056
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020057def half_round_up(n):
58 return (n + 1)//2
59
60# IMSI encoded format:
61# For IMSI 0123456789ABCDE:
62#
63# | byte 1 | 2 upper | 2 lower | 3 upper | 3 lower | ... | 9 upper | 9 lower |
64# | length in bytes | 0 | odd/even | 2 | 1 | ... | E | D |
65#
66# If the IMSI is less than 15 characters, it should be padded with 'f' from the end.
67#
68# The length is the total number of bytes used to encoded the IMSI. This includes the odd/even
69# parity bit. E.g. an IMSI of length 14 is 8 bytes long, not 7, as it uses bytes 2 to 9 to
70# encode itself.
71#
72# Because of this, an odd length IMSI fits exactly into len(imsi) + 1 // 2 bytes, whereas an
73# even length IMSI only uses half of the last byte.
74
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040075def enc_imsi(imsi):
76 """Converts a string imsi into the value of the EF"""
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020077 l = half_round_up(len(imsi) + 1) # Required bytes - include space for odd/even indicator
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040078 oe = len(imsi) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020079 ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe<<3)|1, rpad(imsi, 15)))
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040080 return ei
81
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040082def dec_imsi(ef):
83 """Converts an EF value to the imsi string representation"""
84 if len(ef) < 4:
85 return None
Pau Espin Pedrol665bd222017-12-29 20:30:35 +010086 l = int(ef[0:2], 16) * 2 # Length of the IMSI string
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020087 l = l - 1 # Encoded length byte includes oe nibble
88 swapped = swap_nibbles(ef[2:]).rstrip('f')
Philipp Maiercd3d6262020-05-11 21:41:56 +020089 if len(swapped) < 1:
90 return None
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040091 oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020092 if not oe:
93 # if even, only half of last byte was used
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040094 l = l-1
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020095 if l != len(swapped) - 1:
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040096 return None
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020097 imsi = swapped[1:]
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040098 return imsi
99
100def dec_iccid(ef):
101 return swap_nibbles(ef).strip('f')
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400102
103def enc_iccid(iccid):
104 return swap_nibbles(rpad(iccid, 20))
105
106def enc_plmn(mcc, mnc):
Alexander Chemerisdddbf522017-07-18 16:49:59 +0300107 """Converts integer MCC/MNC into 3 bytes for EF"""
Harald Welte7f1d3c42020-05-12 21:12:44 +0200108 if len(mnc) == 2:
109 mnc = "F%s" % mnc
110 return swap_nibbles("%s%s" % (mcc, mnc))
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +0300111
112def dec_spn(ef):
113 byte1 = int(ef[0:2])
114 hplmn_disp = (byte1&0x01 == 0x01)
115 oplmn_disp = (byte1&0x02 == 0x02)
116 name = h2s(ef[2:])
117 return (name, hplmn_disp, oplmn_disp)
118
119def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
120 byte1 = 0x00
121 if hplmn_disp: byte1 = byte1|0x01
122 if oplmn_disp: byte1 = byte1|0x02
123 return i2h([byte1])+s2h(name)
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900124
Supreeth Herlef3948532020-03-24 12:23:51 +0100125def hexstr_to_Nbytearr(s, nbytes):
126 return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2)) ]
127
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100128# Accepts hex string representing three bytes
129def dec_mcc_from_plmn(plmn):
130 ia = h2i(plmn)
131 digit1 = ia[0] & 0x0F # 1st byte, LSB
132 digit2 = (ia[0] & 0xF0) >> 4 # 1st byte, MSB
133 digit3 = ia[1] & 0x0F # 2nd byte, LSB
134 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
135 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100136 return derive_mcc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100137
138def dec_mnc_from_plmn(plmn):
139 ia = h2i(plmn)
140 digit1 = ia[2] & 0x0F # 3rd byte, LSB
141 digit2 = (ia[2] & 0xF0) >> 4 # 3rd byte, MSB
142 digit3 = (ia[1] & 0xF0) >> 4 # 2nd byte, MSB
143 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
144 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100145 return derive_mnc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100146
147def dec_act(twohexbytes):
148 act_list = [
149 {'bit': 15, 'name': "UTRAN"},
150 {'bit': 14, 'name': "E-UTRAN"},
151 {'bit': 7, 'name': "GSM"},
152 {'bit': 6, 'name': "GSM COMPACT"},
153 {'bit': 5, 'name': "cdma2000 HRPD"},
154 {'bit': 4, 'name': "cdma2000 1xRTT"},
155 ]
156 ia = h2i(twohexbytes)
157 u16t = (ia[0] << 8)|ia[1]
158 sel = []
159 for a in act_list:
160 if u16t & (1 << a['bit']):
161 sel.append(a['name'])
162 return sel
163
164def dec_xplmn_w_act(fivehexbytes):
165 res = {'mcc': 0, 'mnc': 0, 'act': []}
166 plmn_chars = 6
167 act_chars = 4
168 plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
169 act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
170 res['mcc'] = dec_mcc_from_plmn(plmn_str)
171 res['mnc'] = dec_mnc_from_plmn(plmn_str)
172 res['act'] = dec_act(act_str)
173 return res
174
175def format_xplmn_w_act(hexstr):
176 s = ""
herlesupreeth45fa6042020-09-18 15:32:20 +0200177 for rec_data in hexstr_to_Nbytearr(hexstr, 5):
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100178 rec_info = dec_xplmn_w_act(rec_data)
179 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
180 rec_str = "unused"
181 else:
Supreeth Herled24f1632019-11-30 10:37:09 +0100182 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 +0100183 s += "\t%s # %s\n" % (rec_data, rec_str)
184 return s
185
Sebastian Vivianie61170c2020-06-03 08:57:00 +0100186def dec_loci(hexstr):
187 res = {'tmsi': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'status': 0}
188 res['tmsi'] = hexstr[:8]
189 res['mcc'] = dec_mcc_from_plmn(hexstr[8:14])
190 res['mnc'] = dec_mnc_from_plmn(hexstr[8:14])
191 res['lac'] = hexstr[14:18]
192 res['status'] = h2i(hexstr[20:22])
193 return res
194
195def dec_psloci(hexstr):
196 res = {'p-tmsi': '', 'p-tmsi-sig': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'rac': '', 'status': 0}
197 res['p-tmsi'] = hexstr[:8]
198 res['p-tmsi-sig'] = hexstr[8:14]
199 res['mcc'] = dec_mcc_from_plmn(hexstr[14:20])
200 res['mnc'] = dec_mnc_from_plmn(hexstr[14:20])
201 res['lac'] = hexstr[20:24]
202 res['rac'] = hexstr[24:26]
203 res['status'] = h2i(hexstr[26:28])
204 return res
205
206def dec_epsloci(hexstr):
207 res = {'guti': '', 'mcc': 0, 'mnc': 0, 'tac': '', 'status': 0}
208 res['guti'] = hexstr[:24]
209 res['tai'] = hexstr[24:34]
210 res['mcc'] = dec_mcc_from_plmn(hexstr[24:30])
211 res['mnc'] = dec_mnc_from_plmn(hexstr[24:30])
212 res['tac'] = hexstr[30:34]
213 res['status'] = h2i(hexstr[34:36])
214 return res
215
Harald Welteca673942020-06-03 15:19:40 +0200216def dec_xplmn(threehexbytes):
217 res = {'mcc': 0, 'mnc': 0, 'act': []}
218 plmn_chars = 6
219 plmn_str = threehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
220 res['mcc'] = dec_mcc_from_plmn(plmn_str)
221 res['mnc'] = dec_mnc_from_plmn(plmn_str)
222 return res
223
224def format_xplmn(hexstr):
225 s = ""
herlesupreeth45fa6042020-09-18 15:32:20 +0200226 for rec_data in hexstr_to_Nbytearr(hexstr, 3):
Harald Welteca673942020-06-03 15:19:40 +0200227 rec_info = dec_xplmn(rec_data)
228 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
229 rec_str = "unused"
230 else:
231 rec_str = "MCC: %03d MNC: %03d" % (rec_info['mcc'], rec_info['mnc'])
232 s += "\t%s # %s\n" % (rec_data, rec_str)
233 return s
234
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900235def derive_milenage_opc(ki_hex, op_hex):
236 """
237 Run the milenage algorithm to calculate OPC from Ki and OP
238 """
239 from Crypto.Cipher import AES
240 from Crypto.Util.strxor import strxor
241 from pySim.utils import b2h
242
243 # We pass in hex string and now need to work on bytes
244 aes = AES.new(h2b(ki_hex))
245 opc_bytes = aes.encrypt(h2b(op_hex))
246 return b2h(strxor(opc_bytes, h2b(op_hex)))
247
248def calculate_luhn(cc):
249 """
250 Calculate Luhn checksum used in e.g. ICCID and IMEI
251 """
Daniel Willmanndd014ea2020-10-19 10:35:11 +0200252 num = list(map(int, str(cc)))
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900253 check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
254 return 0 if check_digit == 10 else check_digit
Philipp Maier7592eee2019-09-12 13:03:23 +0200255
256def mcc_from_imsi(imsi):
257 """
258 Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
259 """
260 if imsi == None:
261 return None
262
263 if len(imsi) > 3:
264 return imsi[:3]
265 else:
266 return None
267
268def mnc_from_imsi(imsi, long=False):
269 """
270 Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
271 """
272 if imsi == None:
273 return None
274
275 if len(imsi) > 3:
276 if long:
277 return imsi[3:6]
278 else:
279 return imsi[3:5]
280 else:
281 return None
Supreeth Herled24f1632019-11-30 10:37:09 +0100282
283def derive_mcc(digit1, digit2, digit3):
284 """
285 Derive decimal representation of the MCC (Mobile Country Code)
286 from three given digits.
287 """
288
289 mcc = 0
290
291 if digit1 != 0x0f:
292 mcc += digit1 * 100
293 if digit2 != 0x0f:
294 mcc += digit2 * 10
295 if digit3 != 0x0f:
296 mcc += digit3
297
298 return mcc
299
300def derive_mnc(digit1, digit2, digit3=0x0f):
301 """
302 Derive decimal representation of the MNC (Mobile Network Code)
303 from two or (optionally) three given digits.
304 """
305
306 mnc = 0
307
308 # 3-rd digit is optional for the MNC. If present
309 # the algorythm is the same as for the MCC.
310 if digit3 != 0x0f:
311 return derive_mcc(digit1, digit2, digit3)
312
313 if digit1 != 0x0f:
314 mnc += digit1 * 10
315 if digit2 != 0x0f:
316 mnc += digit2
317
318 return mnc
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100319
320def dec_msisdn(ef_msisdn):
321 """
322 Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
323 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
324 """
325
326 # Convert from str to (kind of) 'bytes'
327 ef_msisdn = h2b(ef_msisdn)
328
329 # Make sure mandatory fields are present
330 if len(ef_msisdn) < 14:
331 raise ValueError("EF.MSISDN is too short")
332
333 # Skip optional Alpha Identifier
334 xlen = len(ef_msisdn) - 14
335 msisdn_lhv = ef_msisdn[xlen:]
336
337 # Parse the length (in bytes) of the BCD encoded number
338 bcd_len = ord(msisdn_lhv[0])
339 # BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
340 if bcd_len == 0xff:
341 return None
342 elif bcd_len > 11 or bcd_len < 1:
343 raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
344
345 # Parse ToN / NPI
346 ton = (ord(msisdn_lhv[1]) >> 4) & 0x07
347 npi = ord(msisdn_lhv[1]) & 0x0f
348 bcd_len -= 1
349
350 # No MSISDN?
351 if not bcd_len:
352 return (npi, ton, None)
353
354 msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
355 # International number 10.5.118/3GPP TS 24.008
Vadim Yanitskiy7ba24282020-02-27 00:04:13 +0700356 if ton == 0x01:
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100357 msisdn = '+' + msisdn
358
359 return (npi, ton, msisdn)
Supreeth Herle5a541012019-12-22 08:59:16 +0100360
361def enc_msisdn(msisdn, npi=0x01, ton=0x03):
362 """
363 Encode MSISDN as LHV so it can be stored to EF.MSISDN.
364 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
365
366 Default NPI / ToN values:
367 - NPI: ISDN / telephony numbering plan (E.164 / E.163),
368 - ToN: network specific or international number (if starts with '+').
369 """
370
371 # Leading '+' indicates International Number
372 if msisdn[0] == '+':
373 msisdn = msisdn[1:]
374 ton = 0x01
375
376 # Append 'f' padding if number of digits is odd
377 if len(msisdn) % 2 > 0:
378 msisdn += 'f'
379
380 # BCD length also includes NPI/ToN header
381 bcd_len = len(msisdn) // 2 + 1
382 npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
383 bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
384
385 return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
Supreeth Herle441c4a72020-03-24 10:19:15 +0100386
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200387def dec_st(st, table="sim"):
388 """
389 Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
390 """
391
Supreeth Herledf330372020-04-20 14:48:55 +0200392 if table == "isim":
393 from pySim.ts_31_103 import EF_IST_map
394 lookup_map = EF_IST_map
395 elif table == "usim":
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200396 from pySim.ts_31_102 import EF_UST_map
397 lookup_map = EF_UST_map
398 else:
399 from pySim.ts_51_011 import EF_SST_map
400 lookup_map = EF_SST_map
401
402 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
403
404 avail_st = ""
405 # Get each byte and check for available services
406 for i in range(0, len(st_bytes)):
407 # Byte i contains info about Services num (8i+1) to num (8i+8)
408 byte = int(st_bytes[i], 16)
409 # Services in each byte are in order MSB to LSB
410 # MSB - Service (8i+8)
411 # LSB - Service (8i+1)
412 for j in range(1, 9):
413 if byte&0x01 == 0x01 and ((8*i) + j in lookup_map):
414 # Byte X contains info about Services num (8X-7) to num (8X)
415 # bit = 1: service available
416 # bit = 0: service not available
417 avail_st += '\tService %d - %s\n' % ((8*i) + j, lookup_map[(8*i) + j])
418 byte = byte >> 1
419 return avail_st
Supreeth Herle98370552020-05-11 09:04:41 +0200420
421def first_TLV_parser(bytelist):
422 '''
423 first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
424
425 parses first TLV format record in a list of bytelist
426 returns a 3-Tuple: Tag, Length, Value
427 Value is a list of bytes
428 parsing of length is ETSI'style 101.220
429 '''
430 Tag = bytelist[0]
431 if bytelist[1] == 0xFF:
432 Len = bytelist[2]*256 + bytelist[3]
433 Val = bytelist[4:4+Len]
434 else:
435 Len = bytelist[1]
436 Val = bytelist[2:2+Len]
437 return (Tag, Len, Val)
438
439def TLV_parser(bytelist):
440 '''
441 TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
442
443 loops on the input list of bytes with the "first_TLV_parser()" function
444 returns a list of 3-Tuples
445 '''
446 ret = []
447 while len(bytelist) > 0:
448 T, L, V = first_TLV_parser(bytelist)
449 if T == 0xFF:
450 # padding bytes
451 break
452 ret.append( (T, L, V) )
453 # need to manage length of L
454 if L > 0xFE:
455 bytelist = bytelist[ L+4 : ]
456 else:
457 bytelist = bytelist[ L+2 : ]
458 return ret
Supreeth Herled572ede2020-03-22 09:55:04 +0100459
Supreeth Herled84daa12020-03-24 12:20:40 +0100460def enc_st(st, service, state=1):
461 """
462 Encodes the EF S/U/IST/EST and returns the updated Service Table
463
464 Parameters:
465 st - Current value of SIM/USIM/ISIM Service Table
466 service - Service Number to encode as activated/de-activated
467 state - 1 mean activate, 0 means de-activate
468
469 Returns:
470 s - Modified value of SIM/USIM/ISIM Service Table
471
472 Default values:
473 - state: 1 - Sets the particular Service bit to 1
474 """
475 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
476
477 s = ""
478 # Check whether the requested service is present in each byte
479 for i in range(0, len(st_bytes)):
480 # Byte i contains info about Services num (8i+1) to num (8i+8)
481 if service in range((8*i) + 1, (8*i) + 9):
482 byte = int(st_bytes[i], 16)
483 # Services in each byte are in order MSB to LSB
484 # MSB - Service (8i+8)
485 # LSB - Service (8i+1)
486 mod_byte = 0x00
487 # Copy bit by bit contents of byte to mod_byte with modified bit
488 # for requested service
489 for j in range(1, 9):
490 mod_byte = mod_byte >> 1
491 if service == (8*i) + j:
492 mod_byte = state == 1 and mod_byte|0x80 or mod_byte&0x7f
493 else:
494 mod_byte = byte&0x01 == 0x01 and mod_byte|0x80 or mod_byte&0x7f
495 byte = byte >> 1
496
497 s += ('%02x' % (mod_byte))
498 else:
499 s += st_bytes[i]
500
501 return s
502
Supreeth Herled572ede2020-03-22 09:55:04 +0100503def dec_epdgid(hexstr):
504 """
505 Decode ePDG Id to get EF.ePDGId or EF.ePDGIdEm.
506 See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.102 and 4.2.104.
507 """
508
509 # Convert from hex str to int bytes list
510 epdgid_bytes = h2i(hexstr)
511
512 s = ""
513
514 # Get list of tuples containing parsed TLVs
515 tlvs = TLV_parser(epdgid_bytes)
516
517 for tlv in tlvs:
518 # tlv = (T, L, [V])
519 # T = Tag
520 # L = Length
521 # [V] = List of value
522
523 # Invalid Tag value scenario
524 if tlv[0] != 0x80:
525 continue
526
Supreeth Herled6a5ec52020-06-01 12:27:51 +0200527 # Empty field - Zero length
528 if tlv[1] == 0:
529 continue
530
Supreeth Herled572ede2020-03-22 09:55:04 +0100531 # First byte in the value has the address type
532 addr_type = tlv[2][0]
533 # TODO: Support parsing of IPv4 and IPv6
534 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))
538
539 return s
Philipp Maierff84c232020-05-12 17:24:18 +0200540
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100541def enc_epdgid(epdg_addr, addr_type='00'):
542 """
543 Encode ePDG Id so it can be stored to EF.ePDGId or EF.ePDGIdEm.
544 See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.102 and 4.2.104.
545
546 Default values:
547 - addr_type: 00 - FQDN format of ePDG Address
548 """
549
550 s = ""
551
552 # TODO: Encoding of IPv4 and IPv6 address
553 if addr_type == '00':
554 hex_str = s2h(epdg_addr)
555 s += '80' + ('%02x' % ((len(hex_str)//2)+1)) + '00' + hex_str
556
557 return s
558
Philipp Maiere8536c02020-05-11 21:35:01 +0200559def sanitize_pin_adm(opts):
560 """
561 The ADM pin can be supplied either in its hexadecimal form or as
562 ascii string. This function checks the supplied opts parameter and
563 returns the pin_adm as hex encoded string, regardles in which form
564 it was originally supplied by the user
565 """
566
567 pin_adm = None
568
569 if opts.pin_adm is not None:
570 if len(opts.pin_adm) <= 8:
571 pin_adm = ''.join(['%02x'%(ord(x)) for x in opts.pin_adm])
572 pin_adm = rpad(pin_adm, 16)
573
574 else:
575 raise ValueError("PIN-ADM needs to be <=8 digits (ascii)")
576
577 if opts.pin_adm_hex is not None:
578 if len(opts.pin_adm_hex) == 16:
579 pin_adm = opts.pin_adm_hex
580 # Ensure that it's hex-encoded
581 try:
582 try_encode = h2b(pin_adm)
583 except ValueError:
584 raise ValueError("PIN-ADM needs to be hex encoded using this option")
585 else:
586 raise ValueError("PIN-ADM needs to be exactly 16 digits (hex encoded)")
587
588 return pin_adm
589
Philipp Maierff84c232020-05-12 17:24:18 +0200590def init_reader(opts):
591 """
592 Init card reader driver
593 """
594 if opts.pcsc_dev is not None:
595 print("Using PC/SC reader interface")
596 from pySim.transport.pcsc import PcscSimLink
597 sl = PcscSimLink(opts.pcsc_dev)
598 elif opts.osmocon_sock is not None:
599 print("Using Calypso-based (OsmocomBB) reader interface")
600 from pySim.transport.calypso import CalypsoSimLink
601 sl = CalypsoSimLink(sock_path=opts.osmocon_sock)
Vadim Yanitskiy29ca8042020-05-09 21:23:37 +0700602 elif opts.modem_dev is not None:
603 print("Using modem for Generic SIM Access (3GPP TS 27.007)")
604 from pySim.transport.modem_atcmd import ModemATCommandLink
605 sl = ModemATCommandLink(device=opts.modem_dev, baudrate=opts.modem_baud)
Philipp Maierff84c232020-05-12 17:24:18 +0200606 else: # Serial reader is default
607 print("Using serial reader interface")
608 from pySim.transport.serial import SerialSimLink
609 sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
610
611 return sl
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100612
613def dec_ePDGSelection(sixhexbytes):
614 """
615 Decode ePDGSelection to get EF.ePDGSelection or EF.ePDGSelectionEm.
616 See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
617 """
618
619 res = {'mcc': 0, 'mnc': 0, 'epdg_priority': 0, 'epdg_fqdn_format': ''}
620 plmn_chars = 6
621 epdg_priority_chars = 4
622 epdg_fqdn_format_chars = 2
623 # first three bytes (six ascii hex chars)
624 plmn_str = sixhexbytes[:plmn_chars]
625 # two bytes after first three bytes
626 epdg_priority_str = sixhexbytes[plmn_chars:plmn_chars + epdg_priority_chars]
627 # one byte after first five bytes
628 epdg_fqdn_format_str = sixhexbytes[plmn_chars + epdg_priority_chars:plmn_chars + epdg_priority_chars + epdg_fqdn_format_chars]
629 res['mcc'] = dec_mcc_from_plmn(plmn_str)
630 res['mnc'] = dec_mnc_from_plmn(plmn_str)
631 res['epdg_priority'] = epdg_priority_str
632 res['epdg_fqdn_format'] = epdg_fqdn_format_str == '00' and 'Operator Identifier FQDN' or 'Location based FQDN'
633 return res
634
635def format_ePDGSelection(hexstr):
636 ePDGSelection_info_tag_chars = 2
637 ePDGSelection_info_tag_str = hexstr[:2]
638 # Minimum length
639 len_chars = 2
640 # TODO: Need to determine length properly - definite length support only
641 # Inconsistency in spec: 3GPP TS 31.102 version 15.2.0 Release 15, 4.2.104
642 # As per spec, length is 5n, n - number of PLMNs
643 # But, each PLMN entry is made of PLMN (3 Bytes) + ePDG Priority (2 Bytes) + ePDG FQDN format (1 Byte)
644 # Totalling to 6 Bytes, maybe length should be 6n
645 len_str = hexstr[ePDGSelection_info_tag_chars:ePDGSelection_info_tag_chars+len_chars]
646 if len_str[0] == '8':
647 # The bits 7 to 1 denotes the number of length octets if length > 127
648 if int(len_str[1]) > 0:
649 # Update number of length octets
650 len_chars = len_chars * int(len_str[1])
651 len_str = hexstr[ePDGSelection_info_tag_chars:len_chars]
652
653 content_str = hexstr[ePDGSelection_info_tag_chars+len_chars:]
654 # Right pad to prevent index out of range - multiple of 6 bytes
655 content_str = rpad(content_str, len(content_str) + (12 - (len(content_str) % 12)))
656 s = ""
657 for rec_data in hexstr_to_Nbytearr(content_str, 6):
658 rec_info = dec_ePDGSelection(rec_data)
659 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
660 rec_str = "unused"
661 else:
662 rec_str = "MCC: %03d MNC: %03d ePDG Priority: %s ePDG FQDN format: %s" % \
663 (rec_info['mcc'], rec_info['mnc'], rec_info['epdg_priority'], rec_info['epdg_fqdn_format'])
664 s += "\t%s # %s\n" % (rec_data, rec_str)
665 return s