blob: 496b918accd75b5c8e3793a7ece4acf775f4f4d2 [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
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100125def hexstr_to_fivebytearr(s):
126 return [s[i:i+10] for i in range(0, len(s), 10) ]
127
128# 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 = ""
177 for rec_data in hexstr_to_fivebytearr(hexstr):
178 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
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900186def derive_milenage_opc(ki_hex, op_hex):
187 """
188 Run the milenage algorithm to calculate OPC from Ki and OP
189 """
190 from Crypto.Cipher import AES
191 from Crypto.Util.strxor import strxor
192 from pySim.utils import b2h
193
194 # We pass in hex string and now need to work on bytes
195 aes = AES.new(h2b(ki_hex))
196 opc_bytes = aes.encrypt(h2b(op_hex))
197 return b2h(strxor(opc_bytes, h2b(op_hex)))
198
199def calculate_luhn(cc):
200 """
201 Calculate Luhn checksum used in e.g. ICCID and IMEI
202 """
203 num = map(int, str(cc))
204 check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
205 return 0 if check_digit == 10 else check_digit
Philipp Maier7592eee2019-09-12 13:03:23 +0200206
207def mcc_from_imsi(imsi):
208 """
209 Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
210 """
211 if imsi == None:
212 return None
213
214 if len(imsi) > 3:
215 return imsi[:3]
216 else:
217 return None
218
219def mnc_from_imsi(imsi, long=False):
220 """
221 Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
222 """
223 if imsi == None:
224 return None
225
226 if len(imsi) > 3:
227 if long:
228 return imsi[3:6]
229 else:
230 return imsi[3:5]
231 else:
232 return None
Supreeth Herled24f1632019-11-30 10:37:09 +0100233
234def derive_mcc(digit1, digit2, digit3):
235 """
236 Derive decimal representation of the MCC (Mobile Country Code)
237 from three given digits.
238 """
239
240 mcc = 0
241
242 if digit1 != 0x0f:
243 mcc += digit1 * 100
244 if digit2 != 0x0f:
245 mcc += digit2 * 10
246 if digit3 != 0x0f:
247 mcc += digit3
248
249 return mcc
250
251def derive_mnc(digit1, digit2, digit3=0x0f):
252 """
253 Derive decimal representation of the MNC (Mobile Network Code)
254 from two or (optionally) three given digits.
255 """
256
257 mnc = 0
258
259 # 3-rd digit is optional for the MNC. If present
260 # the algorythm is the same as for the MCC.
261 if digit3 != 0x0f:
262 return derive_mcc(digit1, digit2, digit3)
263
264 if digit1 != 0x0f:
265 mnc += digit1 * 10
266 if digit2 != 0x0f:
267 mnc += digit2
268
269 return mnc
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100270
271def dec_msisdn(ef_msisdn):
272 """
273 Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
274 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
275 """
276
277 # Convert from str to (kind of) 'bytes'
278 ef_msisdn = h2b(ef_msisdn)
279
280 # Make sure mandatory fields are present
281 if len(ef_msisdn) < 14:
282 raise ValueError("EF.MSISDN is too short")
283
284 # Skip optional Alpha Identifier
285 xlen = len(ef_msisdn) - 14
286 msisdn_lhv = ef_msisdn[xlen:]
287
288 # Parse the length (in bytes) of the BCD encoded number
289 bcd_len = ord(msisdn_lhv[0])
290 # BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
291 if bcd_len == 0xff:
292 return None
293 elif bcd_len > 11 or bcd_len < 1:
294 raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
295
296 # Parse ToN / NPI
297 ton = (ord(msisdn_lhv[1]) >> 4) & 0x07
298 npi = ord(msisdn_lhv[1]) & 0x0f
299 bcd_len -= 1
300
301 # No MSISDN?
302 if not bcd_len:
303 return (npi, ton, None)
304
305 msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
306 # International number 10.5.118/3GPP TS 24.008
Vadim Yanitskiy7ba24282020-02-27 00:04:13 +0700307 if ton == 0x01:
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100308 msisdn = '+' + msisdn
309
310 return (npi, ton, msisdn)
Supreeth Herle5a541012019-12-22 08:59:16 +0100311
312def enc_msisdn(msisdn, npi=0x01, ton=0x03):
313 """
314 Encode MSISDN as LHV so it can be stored to EF.MSISDN.
315 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
316
317 Default NPI / ToN values:
318 - NPI: ISDN / telephony numbering plan (E.164 / E.163),
319 - ToN: network specific or international number (if starts with '+').
320 """
321
322 # Leading '+' indicates International Number
323 if msisdn[0] == '+':
324 msisdn = msisdn[1:]
325 ton = 0x01
326
327 # Append 'f' padding if number of digits is odd
328 if len(msisdn) % 2 > 0:
329 msisdn += 'f'
330
331 # BCD length also includes NPI/ToN header
332 bcd_len = len(msisdn) // 2 + 1
333 npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
334 bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
335
336 return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
Supreeth Herle441c4a72020-03-24 10:19:15 +0100337
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200338def dec_st(st, table="sim"):
339 """
340 Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
341 """
342
Supreeth Herledf330372020-04-20 14:48:55 +0200343 if table == "isim":
344 from pySim.ts_31_103 import EF_IST_map
345 lookup_map = EF_IST_map
346 elif table == "usim":
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200347 from pySim.ts_31_102 import EF_UST_map
348 lookup_map = EF_UST_map
349 else:
350 from pySim.ts_51_011 import EF_SST_map
351 lookup_map = EF_SST_map
352
353 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
354
355 avail_st = ""
356 # Get each byte and check for available services
357 for i in range(0, len(st_bytes)):
358 # Byte i contains info about Services num (8i+1) to num (8i+8)
359 byte = int(st_bytes[i], 16)
360 # Services in each byte are in order MSB to LSB
361 # MSB - Service (8i+8)
362 # LSB - Service (8i+1)
363 for j in range(1, 9):
364 if byte&0x01 == 0x01 and ((8*i) + j in lookup_map):
365 # Byte X contains info about Services num (8X-7) to num (8X)
366 # bit = 1: service available
367 # bit = 0: service not available
368 avail_st += '\tService %d - %s\n' % ((8*i) + j, lookup_map[(8*i) + j])
369 byte = byte >> 1
370 return avail_st
Supreeth Herle98370552020-05-11 09:04:41 +0200371
372def first_TLV_parser(bytelist):
373 '''
374 first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
375
376 parses first TLV format record in a list of bytelist
377 returns a 3-Tuple: Tag, Length, Value
378 Value is a list of bytes
379 parsing of length is ETSI'style 101.220
380 '''
381 Tag = bytelist[0]
382 if bytelist[1] == 0xFF:
383 Len = bytelist[2]*256 + bytelist[3]
384 Val = bytelist[4:4+Len]
385 else:
386 Len = bytelist[1]
387 Val = bytelist[2:2+Len]
388 return (Tag, Len, Val)
389
390def TLV_parser(bytelist):
391 '''
392 TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
393
394 loops on the input list of bytes with the "first_TLV_parser()" function
395 returns a list of 3-Tuples
396 '''
397 ret = []
398 while len(bytelist) > 0:
399 T, L, V = first_TLV_parser(bytelist)
400 if T == 0xFF:
401 # padding bytes
402 break
403 ret.append( (T, L, V) )
404 # need to manage length of L
405 if L > 0xFE:
406 bytelist = bytelist[ L+4 : ]
407 else:
408 bytelist = bytelist[ L+2 : ]
409 return ret
Supreeth Herled572ede2020-03-22 09:55:04 +0100410
411def dec_epdgid(hexstr):
412 """
413 Decode ePDG Id to get EF.ePDGId or EF.ePDGIdEm.
414 See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.102 and 4.2.104.
415 """
416
417 # Convert from hex str to int bytes list
418 epdgid_bytes = h2i(hexstr)
419
420 s = ""
421
422 # Get list of tuples containing parsed TLVs
423 tlvs = TLV_parser(epdgid_bytes)
424
425 for tlv in tlvs:
426 # tlv = (T, L, [V])
427 # T = Tag
428 # L = Length
429 # [V] = List of value
430
431 # Invalid Tag value scenario
432 if tlv[0] != 0x80:
433 continue
434
435 # First byte in the value has the address type
436 addr_type = tlv[2][0]
437 # TODO: Support parsing of IPv4 and IPv6
438 if addr_type == 0x00: #FQDN
439 # Skip address tye byte i.e. first byte in value list
440 content = tlv[2][1:]
441 s += "\t%s # %s\n" % (i2h(content), i2s(content))
442
443 return s
Philipp Maierff84c232020-05-12 17:24:18 +0200444
Philipp Maiere8536c02020-05-11 21:35:01 +0200445def sanitize_pin_adm(opts):
446 """
447 The ADM pin can be supplied either in its hexadecimal form or as
448 ascii string. This function checks the supplied opts parameter and
449 returns the pin_adm as hex encoded string, regardles in which form
450 it was originally supplied by the user
451 """
452
453 pin_adm = None
454
455 if opts.pin_adm is not None:
456 if len(opts.pin_adm) <= 8:
457 pin_adm = ''.join(['%02x'%(ord(x)) for x in opts.pin_adm])
458 pin_adm = rpad(pin_adm, 16)
459
460 else:
461 raise ValueError("PIN-ADM needs to be <=8 digits (ascii)")
462
463 if opts.pin_adm_hex is not None:
464 if len(opts.pin_adm_hex) == 16:
465 pin_adm = opts.pin_adm_hex
466 # Ensure that it's hex-encoded
467 try:
468 try_encode = h2b(pin_adm)
469 except ValueError:
470 raise ValueError("PIN-ADM needs to be hex encoded using this option")
471 else:
472 raise ValueError("PIN-ADM needs to be exactly 16 digits (hex encoded)")
473
474 return pin_adm
475
Philipp Maierff84c232020-05-12 17:24:18 +0200476def init_reader(opts):
477 """
478 Init card reader driver
479 """
480 if opts.pcsc_dev is not None:
481 print("Using PC/SC reader interface")
482 from pySim.transport.pcsc import PcscSimLink
483 sl = PcscSimLink(opts.pcsc_dev)
484 elif opts.osmocon_sock is not None:
485 print("Using Calypso-based (OsmocomBB) reader interface")
486 from pySim.transport.calypso import CalypsoSimLink
487 sl = CalypsoSimLink(sock_path=opts.osmocon_sock)
Vadim Yanitskiy29ca8042020-05-09 21:23:37 +0700488 elif opts.modem_dev is not None:
489 print("Using modem for Generic SIM Access (3GPP TS 27.007)")
490 from pySim.transport.modem_atcmd import ModemATCommandLink
491 sl = ModemATCommandLink(device=opts.modem_dev, baudrate=opts.modem_baud)
Philipp Maierff84c232020-05-12 17:24:18 +0200492 else: # Serial reader is default
493 print("Using serial reader interface")
494 from pySim.transport.serial import SerialSimLink
495 sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
496
497 return sl