blob: f7190d43b70699c62813b8983503beca1b677299 [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):
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 Welte4f6ca432021-02-01 17:51:56 +010028def b2h(b):
29 """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
32def h2i(s):
33 return [(int(x,16)<<4)+int(y,16) for x,y in zip(s[0::2], s[1::2])]
34
35def i2h(s):
36 return ''.join(['%02x'%(x) for x in s])
37
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030038def h2s(s):
Vadim Yanitskiyeb06b452020-05-10 02:32:46 +070039 return ''.join([chr((int(x,16)<<4)+int(y,16)) for x,y in zip(s[0::2], s[1::2])
40 if int(x + y, 16) != 0xff])
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030041
42def s2h(s):
Harald Welte4f6ca432021-02-01 17:51:56 +010043 b = bytearray()
44 b.extend(map(ord, s))
45 return b2h(b)
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +030046
Supreeth Herle7d77d2d2020-05-11 09:07:08 +020047# List of bytes to string
48def i2s(s):
49 return ''.join([chr(x) for x in s])
50
Sylvain Munaut76504e02010-12-07 00:24:32 +010051def swap_nibbles(s):
52 return ''.join([x+y for x,y in zip(s[1::2], s[0::2])])
53
54def rpad(s, l, c='f'):
55 return s + c * (l - len(s))
56
57def lpad(s, l, c='f'):
58 return c * (l - len(s)) + s
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040059
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020060def half_round_up(n):
61 return (n + 1)//2
62
63# IMSI encoded format:
64# For IMSI 0123456789ABCDE:
65#
66# | byte 1 | 2 upper | 2 lower | 3 upper | 3 lower | ... | 9 upper | 9 lower |
67# | length in bytes | 0 | odd/even | 2 | 1 | ... | E | D |
68#
69# If the IMSI is less than 15 characters, it should be padded with 'f' from the end.
70#
71# The length is the total number of bytes used to encoded the IMSI. This includes the odd/even
72# parity bit. E.g. an IMSI of length 14 is 8 bytes long, not 7, as it uses bytes 2 to 9 to
73# encode itself.
74#
75# Because of this, an odd length IMSI fits exactly into len(imsi) + 1 // 2 bytes, whereas an
76# even length IMSI only uses half of the last byte.
77
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040078def enc_imsi(imsi):
79 """Converts a string imsi into the value of the EF"""
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020080 l = half_round_up(len(imsi) + 1) # Required bytes - include space for odd/even indicator
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040081 oe = len(imsi) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020082 ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe<<3)|1, rpad(imsi, 15)))
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040083 return ei
84
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040085def dec_imsi(ef):
86 """Converts an EF value to the imsi string representation"""
87 if len(ef) < 4:
88 return None
Pau Espin Pedrol665bd222017-12-29 20:30:35 +010089 l = int(ef[0:2], 16) * 2 # Length of the IMSI string
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020090 l = l - 1 # Encoded length byte includes oe nibble
91 swapped = swap_nibbles(ef[2:]).rstrip('f')
Philipp Maiercd3d6262020-05-11 21:41:56 +020092 if len(swapped) < 1:
93 return None
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040094 oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020095 if not oe:
96 # if even, only half of last byte was used
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040097 l = l-1
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020098 if l != len(swapped) - 1:
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040099 return None
Ben Fox-Moore0ec14752018-09-24 15:47:02 +0200100 imsi = swapped[1:]
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +0400101 return imsi
102
103def dec_iccid(ef):
104 return swap_nibbles(ef).strip('f')
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400105
106def enc_iccid(iccid):
107 return swap_nibbles(rpad(iccid, 20))
108
109def enc_plmn(mcc, mnc):
Alexander Chemerisdddbf522017-07-18 16:49:59 +0300110 """Converts integer MCC/MNC into 3 bytes for EF"""
Harald Welte7f1d3c42020-05-12 21:12:44 +0200111 if len(mnc) == 2:
112 mnc = "F%s" % mnc
113 return swap_nibbles("%s%s" % (mcc, mnc))
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +0300114
115def dec_spn(ef):
116 byte1 = int(ef[0:2])
117 hplmn_disp = (byte1&0x01 == 0x01)
118 oplmn_disp = (byte1&0x02 == 0x02)
119 name = h2s(ef[2:])
120 return (name, hplmn_disp, oplmn_disp)
121
122def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
123 byte1 = 0x00
124 if hplmn_disp: byte1 = byte1|0x01
125 if oplmn_disp: byte1 = byte1|0x02
126 return i2h([byte1])+s2h(name)
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900127
Supreeth Herlef3948532020-03-24 12:23:51 +0100128def hexstr_to_Nbytearr(s, nbytes):
129 return [s[i:i+(nbytes*2)] for i in range(0, len(s), (nbytes*2)) ]
130
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100131# Accepts hex string representing three bytes
132def dec_mcc_from_plmn(plmn):
133 ia = h2i(plmn)
134 digit1 = ia[0] & 0x0F # 1st byte, LSB
135 digit2 = (ia[0] & 0xF0) >> 4 # 1st byte, MSB
136 digit3 = ia[1] & 0x0F # 2nd byte, LSB
137 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
138 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100139 return derive_mcc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100140
141def dec_mnc_from_plmn(plmn):
142 ia = h2i(plmn)
herlesupreethbdf3d352021-02-11 06:59:29 +0100143 digit1 = (ia[1] & 0xF0) >>4 # 2nd byte, MSB
144 digit2 = ia[2] & 0x0F # 3rd byte, LSB
145 digit3 = (ia[2] & 0xF0) >> 4 # 3nd byte, MSB
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100146 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
147 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100148 return derive_mnc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100149
150def dec_act(twohexbytes):
151 act_list = [
152 {'bit': 15, 'name': "UTRAN"},
153 {'bit': 14, 'name': "E-UTRAN"},
154 {'bit': 7, 'name': "GSM"},
155 {'bit': 6, 'name': "GSM COMPACT"},
156 {'bit': 5, 'name': "cdma2000 HRPD"},
157 {'bit': 4, 'name': "cdma2000 1xRTT"},
158 ]
159 ia = h2i(twohexbytes)
160 u16t = (ia[0] << 8)|ia[1]
161 sel = []
162 for a in act_list:
163 if u16t & (1 << a['bit']):
164 sel.append(a['name'])
165 return sel
166
167def dec_xplmn_w_act(fivehexbytes):
168 res = {'mcc': 0, 'mnc': 0, 'act': []}
169 plmn_chars = 6
170 act_chars = 4
171 plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
172 act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
173 res['mcc'] = dec_mcc_from_plmn(plmn_str)
174 res['mnc'] = dec_mnc_from_plmn(plmn_str)
175 res['act'] = dec_act(act_str)
176 return res
177
178def format_xplmn_w_act(hexstr):
179 s = ""
herlesupreeth45fa6042020-09-18 15:32:20 +0200180 for rec_data in hexstr_to_Nbytearr(hexstr, 5):
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100181 rec_info = dec_xplmn_w_act(rec_data)
182 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
183 rec_str = "unused"
184 else:
Supreeth Herled24f1632019-11-30 10:37:09 +0100185 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 +0100186 s += "\t%s # %s\n" % (rec_data, rec_str)
187 return s
188
Sebastian Vivianie61170c2020-06-03 08:57:00 +0100189def dec_loci(hexstr):
190 res = {'tmsi': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'status': 0}
191 res['tmsi'] = hexstr[:8]
192 res['mcc'] = dec_mcc_from_plmn(hexstr[8:14])
193 res['mnc'] = dec_mnc_from_plmn(hexstr[8:14])
194 res['lac'] = hexstr[14:18]
195 res['status'] = h2i(hexstr[20:22])
196 return res
197
198def dec_psloci(hexstr):
199 res = {'p-tmsi': '', 'p-tmsi-sig': '', 'mcc': 0, 'mnc': 0, 'lac': '', 'rac': '', 'status': 0}
200 res['p-tmsi'] = hexstr[:8]
201 res['p-tmsi-sig'] = hexstr[8:14]
202 res['mcc'] = dec_mcc_from_plmn(hexstr[14:20])
203 res['mnc'] = dec_mnc_from_plmn(hexstr[14:20])
204 res['lac'] = hexstr[20:24]
205 res['rac'] = hexstr[24:26]
206 res['status'] = h2i(hexstr[26:28])
207 return res
208
209def dec_epsloci(hexstr):
210 res = {'guti': '', 'mcc': 0, 'mnc': 0, 'tac': '', 'status': 0}
211 res['guti'] = hexstr[:24]
212 res['tai'] = hexstr[24:34]
213 res['mcc'] = dec_mcc_from_plmn(hexstr[24:30])
214 res['mnc'] = dec_mnc_from_plmn(hexstr[24:30])
215 res['tac'] = hexstr[30:34]
216 res['status'] = h2i(hexstr[34:36])
217 return res
218
Harald Welteca673942020-06-03 15:19:40 +0200219def dec_xplmn(threehexbytes):
220 res = {'mcc': 0, 'mnc': 0, 'act': []}
221 plmn_chars = 6
222 plmn_str = threehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
223 res['mcc'] = dec_mcc_from_plmn(plmn_str)
224 res['mnc'] = dec_mnc_from_plmn(plmn_str)
225 return res
226
227def format_xplmn(hexstr):
228 s = ""
herlesupreeth45fa6042020-09-18 15:32:20 +0200229 for rec_data in hexstr_to_Nbytearr(hexstr, 3):
Harald Welteca673942020-06-03 15:19:40 +0200230 rec_info = dec_xplmn(rec_data)
231 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
232 rec_str = "unused"
233 else:
234 rec_str = "MCC: %03d MNC: %03d" % (rec_info['mcc'], rec_info['mnc'])
235 s += "\t%s # %s\n" % (rec_data, rec_str)
236 return s
237
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900238def derive_milenage_opc(ki_hex, op_hex):
239 """
240 Run the milenage algorithm to calculate OPC from Ki and OP
241 """
242 from Crypto.Cipher import AES
243 from Crypto.Util.strxor import strxor
244 from pySim.utils import b2h
245
246 # We pass in hex string and now need to work on bytes
Harald Welteeab8d2a2021-03-05 18:30:23 +0100247 ki_bytes = bytes(h2b(ki_hex))
248 op_bytes = bytes(h2b(op_hex))
Harald Welteab34fa82021-03-05 18:39:59 +0100249 aes = AES.new(ki_bytes, AES.MODE_ECB)
Harald Welteeab8d2a2021-03-05 18:30:23 +0100250 opc_bytes = aes.encrypt(op_bytes)
251 return b2h(strxor(opc_bytes, op_bytes))
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900252
253def calculate_luhn(cc):
254 """
255 Calculate Luhn checksum used in e.g. ICCID and IMEI
256 """
Daniel Willmanndd014ea2020-10-19 10:35:11 +0200257 num = list(map(int, str(cc)))
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900258 check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
259 return 0 if check_digit == 10 else check_digit
Philipp Maier7592eee2019-09-12 13:03:23 +0200260
261def mcc_from_imsi(imsi):
262 """
263 Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
264 """
265 if imsi == None:
266 return None
267
268 if len(imsi) > 3:
269 return imsi[:3]
270 else:
271 return None
272
273def mnc_from_imsi(imsi, long=False):
274 """
275 Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
276 """
277 if imsi == None:
278 return None
279
280 if len(imsi) > 3:
281 if long:
282 return imsi[3:6]
283 else:
284 return imsi[3:5]
285 else:
286 return None
Supreeth Herled24f1632019-11-30 10:37:09 +0100287
288def derive_mcc(digit1, digit2, digit3):
289 """
290 Derive decimal representation of the MCC (Mobile Country Code)
291 from three given digits.
292 """
293
294 mcc = 0
295
296 if digit1 != 0x0f:
297 mcc += digit1 * 100
298 if digit2 != 0x0f:
299 mcc += digit2 * 10
300 if digit3 != 0x0f:
301 mcc += digit3
302
303 return mcc
304
305def derive_mnc(digit1, digit2, digit3=0x0f):
306 """
307 Derive decimal representation of the MNC (Mobile Network Code)
308 from two or (optionally) three given digits.
309 """
310
311 mnc = 0
312
313 # 3-rd digit is optional for the MNC. If present
314 # the algorythm is the same as for the MCC.
315 if digit3 != 0x0f:
316 return derive_mcc(digit1, digit2, digit3)
317
318 if digit1 != 0x0f:
319 mnc += digit1 * 10
320 if digit2 != 0x0f:
321 mnc += digit2
322
323 return mnc
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100324
325def dec_msisdn(ef_msisdn):
326 """
327 Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
328 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
329 """
330
331 # Convert from str to (kind of) 'bytes'
332 ef_msisdn = h2b(ef_msisdn)
333
334 # Make sure mandatory fields are present
335 if len(ef_msisdn) < 14:
336 raise ValueError("EF.MSISDN is too short")
337
338 # Skip optional Alpha Identifier
339 xlen = len(ef_msisdn) - 14
340 msisdn_lhv = ef_msisdn[xlen:]
341
342 # Parse the length (in bytes) of the BCD encoded number
Harald Welte4f6ca432021-02-01 17:51:56 +0100343 bcd_len = msisdn_lhv[0]
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100344 # BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
345 if bcd_len == 0xff:
346 return None
347 elif bcd_len > 11 or bcd_len < 1:
348 raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
349
350 # Parse ToN / NPI
Harald Welte4f6ca432021-02-01 17:51:56 +0100351 ton = (msisdn_lhv[1] >> 4) & 0x07
352 npi = msisdn_lhv[1] & 0x0f
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100353 bcd_len -= 1
354
355 # No MSISDN?
356 if not bcd_len:
357 return (npi, ton, None)
358
359 msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
360 # International number 10.5.118/3GPP TS 24.008
Vadim Yanitskiy7ba24282020-02-27 00:04:13 +0700361 if ton == 0x01:
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100362 msisdn = '+' + msisdn
363
364 return (npi, ton, msisdn)
Supreeth Herle5a541012019-12-22 08:59:16 +0100365
366def enc_msisdn(msisdn, npi=0x01, ton=0x03):
367 """
368 Encode MSISDN as LHV so it can be stored to EF.MSISDN.
369 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
370
371 Default NPI / ToN values:
372 - NPI: ISDN / telephony numbering plan (E.164 / E.163),
373 - ToN: network specific or international number (if starts with '+').
374 """
375
376 # Leading '+' indicates International Number
377 if msisdn[0] == '+':
378 msisdn = msisdn[1:]
379 ton = 0x01
380
381 # Append 'f' padding if number of digits is odd
382 if len(msisdn) % 2 > 0:
383 msisdn += 'f'
384
385 # BCD length also includes NPI/ToN header
386 bcd_len = len(msisdn) // 2 + 1
387 npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
388 bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
389
390 return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
Supreeth Herle441c4a72020-03-24 10:19:15 +0100391
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200392def dec_st(st, table="sim"):
393 """
394 Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
395 """
396
Supreeth Herledf330372020-04-20 14:48:55 +0200397 if table == "isim":
398 from pySim.ts_31_103 import EF_IST_map
399 lookup_map = EF_IST_map
400 elif table == "usim":
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200401 from pySim.ts_31_102 import EF_UST_map
402 lookup_map = EF_UST_map
403 else:
404 from pySim.ts_51_011 import EF_SST_map
405 lookup_map = EF_SST_map
406
407 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
408
409 avail_st = ""
410 # Get each byte and check for available services
411 for i in range(0, len(st_bytes)):
412 # Byte i contains info about Services num (8i+1) to num (8i+8)
413 byte = int(st_bytes[i], 16)
414 # Services in each byte are in order MSB to LSB
415 # MSB - Service (8i+8)
416 # LSB - Service (8i+1)
417 for j in range(1, 9):
418 if byte&0x01 == 0x01 and ((8*i) + j in lookup_map):
419 # Byte X contains info about Services num (8X-7) to num (8X)
420 # bit = 1: service available
421 # bit = 0: service not available
422 avail_st += '\tService %d - %s\n' % ((8*i) + j, lookup_map[(8*i) + j])
423 byte = byte >> 1
424 return avail_st
Supreeth Herle98370552020-05-11 09:04:41 +0200425
426def first_TLV_parser(bytelist):
427 '''
428 first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
429
430 parses first TLV format record in a list of bytelist
431 returns a 3-Tuple: Tag, Length, Value
432 Value is a list of bytes
433 parsing of length is ETSI'style 101.220
434 '''
435 Tag = bytelist[0]
436 if bytelist[1] == 0xFF:
437 Len = bytelist[2]*256 + bytelist[3]
438 Val = bytelist[4:4+Len]
439 else:
440 Len = bytelist[1]
441 Val = bytelist[2:2+Len]
442 return (Tag, Len, Val)
443
444def TLV_parser(bytelist):
445 '''
446 TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
447
448 loops on the input list of bytes with the "first_TLV_parser()" function
449 returns a list of 3-Tuples
450 '''
451 ret = []
452 while len(bytelist) > 0:
453 T, L, V = first_TLV_parser(bytelist)
454 if T == 0xFF:
455 # padding bytes
456 break
457 ret.append( (T, L, V) )
458 # need to manage length of L
459 if L > 0xFE:
460 bytelist = bytelist[ L+4 : ]
461 else:
462 bytelist = bytelist[ L+2 : ]
463 return ret
Supreeth Herled572ede2020-03-22 09:55:04 +0100464
Supreeth Herled84daa12020-03-24 12:20:40 +0100465def enc_st(st, service, state=1):
466 """
467 Encodes the EF S/U/IST/EST and returns the updated Service Table
468
469 Parameters:
470 st - Current value of SIM/USIM/ISIM Service Table
471 service - Service Number to encode as activated/de-activated
472 state - 1 mean activate, 0 means de-activate
473
474 Returns:
475 s - Modified value of SIM/USIM/ISIM Service Table
476
477 Default values:
478 - state: 1 - Sets the particular Service bit to 1
479 """
480 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
481
482 s = ""
483 # Check whether the requested service is present in each byte
484 for i in range(0, len(st_bytes)):
485 # Byte i contains info about Services num (8i+1) to num (8i+8)
486 if service in range((8*i) + 1, (8*i) + 9):
487 byte = int(st_bytes[i], 16)
488 # Services in each byte are in order MSB to LSB
489 # MSB - Service (8i+8)
490 # LSB - Service (8i+1)
491 mod_byte = 0x00
492 # Copy bit by bit contents of byte to mod_byte with modified bit
493 # for requested service
494 for j in range(1, 9):
495 mod_byte = mod_byte >> 1
496 if service == (8*i) + j:
497 mod_byte = state == 1 and mod_byte|0x80 or mod_byte&0x7f
498 else:
499 mod_byte = byte&0x01 == 0x01 and mod_byte|0x80 or mod_byte&0x7f
500 byte = byte >> 1
501
502 s += ('%02x' % (mod_byte))
503 else:
504 s += st_bytes[i]
505
506 return s
507
Supreeth Herle3b342c22020-03-24 16:15:02 +0100508def dec_addr_tlv(hexstr):
Supreeth Herled572ede2020-03-22 09:55:04 +0100509 """
Supreeth Herle3b342c22020-03-24 16:15:02 +0100510 Decode hex string to get EF.P-CSCF Address or EF.ePDGId or EF.ePDGIdEm.
511 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 +0100512 """
513
514 # Convert from hex str to int bytes list
Supreeth Herle3b342c22020-03-24 16:15:02 +0100515 addr_tlv_bytes = h2i(hexstr)
Supreeth Herled572ede2020-03-22 09:55:04 +0100516
517 s = ""
518
519 # Get list of tuples containing parsed TLVs
Supreeth Herle3b342c22020-03-24 16:15:02 +0100520 tlvs = TLV_parser(addr_tlv_bytes)
Supreeth Herled572ede2020-03-22 09:55:04 +0100521
522 for tlv in tlvs:
523 # tlv = (T, L, [V])
524 # T = Tag
525 # L = Length
526 # [V] = List of value
527
528 # Invalid Tag value scenario
529 if tlv[0] != 0x80:
530 continue
531
Supreeth Herled6a5ec52020-06-01 12:27:51 +0200532 # Empty field - Zero length
533 if tlv[1] == 0:
534 continue
535
Supreeth Herled572ede2020-03-22 09:55:04 +0100536 # First byte in the value has the address type
537 addr_type = tlv[2][0]
Supreeth Herle43fd03b2020-03-25 14:52:46 +0100538 # TODO: Support parsing of IPv6
Supreeth Herle3b342c22020-03-24 16:15:02 +0100539 # Address Type: 0x00 (FQDN), 0x01 (IPv4), 0x02 (IPv6), other (Reserved)
Supreeth Herled572ede2020-03-22 09:55:04 +0100540 if addr_type == 0x00: #FQDN
541 # Skip address tye byte i.e. first byte in value list
542 content = tlv[2][1:]
543 s += "\t%s # %s\n" % (i2h(content), i2s(content))
Supreeth Herle43fd03b2020-03-25 14:52:46 +0100544 elif addr_type == 0x01: #IPv4
545 # Skip address tye byte i.e. first byte in value list
546 # Skip the unused byte in Octect 4 after address type byte as per 3GPP TS 31.102
547 ipv4 = tlv[2][2:]
548 content = '.'.join(str(x) for x in ipv4)
549 s += "\t%s # %s\n" % (i2h(ipv4), content)
Supreeth Herled572ede2020-03-22 09:55:04 +0100550
551 return s
Philipp Maierff84c232020-05-12 17:24:18 +0200552
Supreeth Herle3b342c22020-03-24 16:15:02 +0100553def enc_addr_tlv(addr, addr_type='00'):
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100554 """
Supreeth Herle3b342c22020-03-24 16:15:02 +0100555 Encode address TLV object used in EF.P-CSCF Address, EF.ePDGId and EF.ePDGIdEm.
556 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 +0100557
558 Default values:
Supreeth Herle3b342c22020-03-24 16:15:02 +0100559 - addr_type: 00 - FQDN format of Address
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100560 """
561
562 s = ""
563
Supreeth Herle654eca72020-03-25 14:25:38 +0100564 # TODO: Encoding of IPv6 address
565 if addr_type == '00': #FQDN
Supreeth Herle3b342c22020-03-24 16:15:02 +0100566 hex_str = s2h(addr)
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100567 s += '80' + ('%02x' % ((len(hex_str)//2)+1)) + '00' + hex_str
Supreeth Herle654eca72020-03-25 14:25:38 +0100568 elif addr_type == '01': #IPv4
569 ipv4_list = addr.split('.')
570 ipv4_str = ""
571 for i in ipv4_list:
572 ipv4_str += ('%02x' % (int(i)))
573
574 # Unused bytes shall be set to 'ff'. i.e 4th Octet after Address Type is not used
575 # IPv4 Address is in octet 5 to octet 8 of the TLV data object
576 s += '80' + ('%02x' % ((len(ipv4_str)//2)+2)) + '01' + 'ff' + ipv4_str
Supreeth Herle3c0bd7a2020-03-23 11:59:33 +0100577
578 return s
579
Harald Welte79b5ba42021-01-08 21:22:38 +0100580def sanitize_pin_adm(pin_adm, pin_adm_hex = None):
Philipp Maiere8536c02020-05-11 21:35:01 +0200581 """
582 The ADM pin can be supplied either in its hexadecimal form or as
583 ascii string. This function checks the supplied opts parameter and
584 returns the pin_adm as hex encoded string, regardles in which form
585 it was originally supplied by the user
586 """
587
Harald Welte79b5ba42021-01-08 21:22:38 +0100588 if pin_adm is not None:
589 if len(pin_adm) <= 8:
590 pin_adm = ''.join(['%02x'%(ord(x)) for x in pin_adm])
Philipp Maiere8536c02020-05-11 21:35:01 +0200591 pin_adm = rpad(pin_adm, 16)
592
593 else:
594 raise ValueError("PIN-ADM needs to be <=8 digits (ascii)")
595
Harald Welte79b5ba42021-01-08 21:22:38 +0100596 if pin_adm_hex is not None:
597 if len(pin_adm_hex) == 16:
598 pin_adm = pin_adm_hex
Philipp Maiere8536c02020-05-11 21:35:01 +0200599 # Ensure that it's hex-encoded
600 try:
601 try_encode = h2b(pin_adm)
602 except ValueError:
603 raise ValueError("PIN-ADM needs to be hex encoded using this option")
604 else:
605 raise ValueError("PIN-ADM needs to be exactly 16 digits (hex encoded)")
606
607 return pin_adm
608
Philipp Maierff84c232020-05-12 17:24:18 +0200609def init_reader(opts):
610 """
611 Init card reader driver
612 """
Philipp Maierc8caec22021-02-22 16:07:53 +0100613 try:
614 if opts.pcsc_dev is not None:
615 print("Using PC/SC reader interface")
616 from pySim.transport.pcsc import PcscSimLink
617 sl = PcscSimLink(opts.pcsc_dev)
618 elif opts.osmocon_sock is not None:
619 print("Using Calypso-based (OsmocomBB) reader interface")
620 from pySim.transport.calypso import CalypsoSimLink
621 sl = CalypsoSimLink(sock_path=opts.osmocon_sock)
622 elif opts.modem_dev is not None:
623 print("Using modem for Generic SIM Access (3GPP TS 27.007)")
624 from pySim.transport.modem_atcmd import ModemATCommandLink
625 sl = ModemATCommandLink(device=opts.modem_dev, baudrate=opts.modem_baud)
626 else: # Serial reader is default
627 print("Using serial reader interface")
628 from pySim.transport.serial import SerialSimLink
629 sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
630 return sl
631 except Exception as e:
632 print("Card reader initialization failed with exception:\n" + str(e))
633 return None
Philipp Maierff84c232020-05-12 17:24:18 +0200634
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100635
Supreeth Herle95ec7722020-03-24 13:09:03 +0100636def enc_ePDGSelection(hexstr, mcc, mnc, epdg_priority='0001', epdg_fqdn_format='00'):
637 """
638 Encode ePDGSelection so it can be stored at EF.ePDGSelection or EF.ePDGSelectionEm.
639 See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
640
641 Default values:
642 - epdg_priority: '0001' - 1st Priority
643 - epdg_fqdn_format: '00' - Operator Identifier FQDN
644 """
645
646 plmn1 = enc_plmn(mcc, mnc) + epdg_priority + epdg_fqdn_format
647 # TODO: Handle encoding of Length field for length more than 127 Bytes
648 content = '80' + ('%02x' % (len(plmn1)//2)) + plmn1
649 content = rpad(content, len(hexstr))
650 return content
651
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100652def dec_ePDGSelection(sixhexbytes):
653 """
654 Decode ePDGSelection to get EF.ePDGSelection or EF.ePDGSelectionEm.
655 See 3GPP TS 31.102 version 15.2.0 Release 15, section 4.2.104 and 4.2.106.
656 """
657
658 res = {'mcc': 0, 'mnc': 0, 'epdg_priority': 0, 'epdg_fqdn_format': ''}
659 plmn_chars = 6
660 epdg_priority_chars = 4
661 epdg_fqdn_format_chars = 2
662 # first three bytes (six ascii hex chars)
663 plmn_str = sixhexbytes[:plmn_chars]
664 # two bytes after first three bytes
665 epdg_priority_str = sixhexbytes[plmn_chars:plmn_chars + epdg_priority_chars]
666 # one byte after first five bytes
667 epdg_fqdn_format_str = sixhexbytes[plmn_chars + epdg_priority_chars:plmn_chars + epdg_priority_chars + epdg_fqdn_format_chars]
668 res['mcc'] = dec_mcc_from_plmn(plmn_str)
669 res['mnc'] = dec_mnc_from_plmn(plmn_str)
670 res['epdg_priority'] = epdg_priority_str
671 res['epdg_fqdn_format'] = epdg_fqdn_format_str == '00' and 'Operator Identifier FQDN' or 'Location based FQDN'
672 return res
673
674def format_ePDGSelection(hexstr):
675 ePDGSelection_info_tag_chars = 2
676 ePDGSelection_info_tag_str = hexstr[:2]
herlesupreeth3a261d32021-01-05 09:20:11 +0100677 s = ""
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100678 # Minimum length
679 len_chars = 2
680 # TODO: Need to determine length properly - definite length support only
681 # Inconsistency in spec: 3GPP TS 31.102 version 15.2.0 Release 15, 4.2.104
682 # As per spec, length is 5n, n - number of PLMNs
683 # But, each PLMN entry is made of PLMN (3 Bytes) + ePDG Priority (2 Bytes) + ePDG FQDN format (1 Byte)
684 # Totalling to 6 Bytes, maybe length should be 6n
685 len_str = hexstr[ePDGSelection_info_tag_chars:ePDGSelection_info_tag_chars+len_chars]
herlesupreeth3a261d32021-01-05 09:20:11 +0100686
687 # Not programmed scenario
688 if int(len_str, 16) == 255 or int(ePDGSelection_info_tag_str, 16) == 255:
689 len_chars = 0
690 ePDGSelection_info_tag_chars = 0
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100691 if len_str[0] == '8':
692 # The bits 7 to 1 denotes the number of length octets if length > 127
693 if int(len_str[1]) > 0:
694 # Update number of length octets
695 len_chars = len_chars * int(len_str[1])
696 len_str = hexstr[ePDGSelection_info_tag_chars:len_chars]
697
698 content_str = hexstr[ePDGSelection_info_tag_chars+len_chars:]
699 # Right pad to prevent index out of range - multiple of 6 bytes
700 content_str = rpad(content_str, len(content_str) + (12 - (len(content_str) % 12)))
Supreeth Herle95b4e8d2020-03-24 12:49:16 +0100701 for rec_data in hexstr_to_Nbytearr(content_str, 6):
702 rec_info = dec_ePDGSelection(rec_data)
703 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
704 rec_str = "unused"
705 else:
706 rec_str = "MCC: %03d MNC: %03d ePDG Priority: %s ePDG FQDN format: %s" % \
707 (rec_info['mcc'], rec_info['mnc'], rec_info['epdg_priority'], rec_info['epdg_fqdn_format'])
708 s += "\t%s # %s\n" % (rec_data, rec_str)
709 return s
Supreeth Herle556b0fe2020-03-25 11:26:57 +0100710
711def get_addr_type(addr):
712 """
713 Validates the given address and returns it's type (FQDN or IPv4 or IPv6)
714 Return: 0x00 (FQDN), 0x01 (IPv4), 0x02 (IPv6), None (Bad address argument given)
715
716 TODO: Handle IPv6
717 """
718
719 # Empty address string
720 if not len(addr):
721 return None
722
723 import sys
724 # Handle python3 and python2 - unicode
725 if sys.version_info[0] < 3:
726 addr_str = unicode(addr)
727 else:
728 addr_str = addr
729
730 addr_list = addr.split('.')
731
732 # Check for IPv4/IPv6
733 try:
734 import ipaddress
735 # Throws ValueError if addr is not correct
736 ipa = ipaddress.ip_address(addr_str)
737
738 if ipa.version == 4:
739 return 0x01
740 elif ipa.version == 6:
741 return 0x02
742 except Exception as e:
743 invalid_ipv4 = True
744 for i in addr_list:
745 # Invalid IPv4 may qualify for a valid FQDN, so make check here
746 # e.g. 172.24.15.300
747 import re
748 if not re.match('^[0-9_]+$', i):
749 invalid_ipv4 = False
750 break
751
752 if invalid_ipv4:
753 return None
754
755 fqdn_flag = True
756 for i in addr_list:
757 # Only Alpha-numeric characters and hyphen - RFC 1035
758 import re
759 if not re.match("^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)?$", i):
760 fqdn_flag = False
761 break
762
763 # FQDN
764 if fqdn_flag:
765 return 0x00
766
767 return None
Harald Welte67d551a2021-01-21 14:50:01 +0100768
769def sw_match(sw, pattern):
770 """Match given SW against given pattern."""
771 # Create a masked version of the returned status word
772 sw_lower = sw.lower()
773 sw_masked = ""
774 for i in range(0, 4):
775 if sw_lower[i] == '?':
776 sw_masked = sw_masked + '?'
777 elif sw_lower[i] == 'x':
778 sw_masked = sw_masked + 'x'
779 else:
780 sw_masked = sw_masked + sw_lower[i]
781 return sw_masked == pattern