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