blob: 43616a97754ebeb7b7eb947f86d56f88102d7684 [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')
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040088 oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020089 if not oe:
90 # if even, only half of last byte was used
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040091 l = l-1
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020092 if l != len(swapped) - 1:
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040093 return None
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020094 imsi = swapped[1:]
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040095 return imsi
96
97def dec_iccid(ef):
98 return swap_nibbles(ef).strip('f')
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040099
100def enc_iccid(iccid):
101 return swap_nibbles(rpad(iccid, 20))
102
103def enc_plmn(mcc, mnc):
Alexander Chemerisdddbf522017-07-18 16:49:59 +0300104 """Converts integer MCC/MNC into 3 bytes for EF"""
Harald Welte7f1d3c42020-05-12 21:12:44 +0200105 if len(mnc) == 2:
106 mnc = "F%s" % mnc
107 return swap_nibbles("%s%s" % (mcc, mnc))
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +0300108
109def dec_spn(ef):
110 byte1 = int(ef[0:2])
111 hplmn_disp = (byte1&0x01 == 0x01)
112 oplmn_disp = (byte1&0x02 == 0x02)
113 name = h2s(ef[2:])
114 return (name, hplmn_disp, oplmn_disp)
115
116def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
117 byte1 = 0x00
118 if hplmn_disp: byte1 = byte1|0x01
119 if oplmn_disp: byte1 = byte1|0x02
120 return i2h([byte1])+s2h(name)
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900121
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100122def hexstr_to_fivebytearr(s):
123 return [s[i:i+10] for i in range(0, len(s), 10) ]
124
125# Accepts hex string representing three bytes
126def dec_mcc_from_plmn(plmn):
127 ia = h2i(plmn)
128 digit1 = ia[0] & 0x0F # 1st byte, LSB
129 digit2 = (ia[0] & 0xF0) >> 4 # 1st byte, MSB
130 digit3 = ia[1] & 0x0F # 2nd byte, LSB
131 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
132 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100133 return derive_mcc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100134
135def dec_mnc_from_plmn(plmn):
136 ia = h2i(plmn)
137 digit1 = ia[2] & 0x0F # 3rd byte, LSB
138 digit2 = (ia[2] & 0xF0) >> 4 # 3rd byte, MSB
139 digit3 = (ia[1] & 0xF0) >> 4 # 2nd byte, MSB
140 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
141 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100142 return derive_mnc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100143
144def dec_act(twohexbytes):
145 act_list = [
146 {'bit': 15, 'name': "UTRAN"},
147 {'bit': 14, 'name': "E-UTRAN"},
148 {'bit': 7, 'name': "GSM"},
149 {'bit': 6, 'name': "GSM COMPACT"},
150 {'bit': 5, 'name': "cdma2000 HRPD"},
151 {'bit': 4, 'name': "cdma2000 1xRTT"},
152 ]
153 ia = h2i(twohexbytes)
154 u16t = (ia[0] << 8)|ia[1]
155 sel = []
156 for a in act_list:
157 if u16t & (1 << a['bit']):
158 sel.append(a['name'])
159 return sel
160
161def dec_xplmn_w_act(fivehexbytes):
162 res = {'mcc': 0, 'mnc': 0, 'act': []}
163 plmn_chars = 6
164 act_chars = 4
165 plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
166 act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
167 res['mcc'] = dec_mcc_from_plmn(plmn_str)
168 res['mnc'] = dec_mnc_from_plmn(plmn_str)
169 res['act'] = dec_act(act_str)
170 return res
171
172def format_xplmn_w_act(hexstr):
173 s = ""
174 for rec_data in hexstr_to_fivebytearr(hexstr):
175 rec_info = dec_xplmn_w_act(rec_data)
176 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
177 rec_str = "unused"
178 else:
Supreeth Herled24f1632019-11-30 10:37:09 +0100179 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 +0100180 s += "\t%s # %s\n" % (rec_data, rec_str)
181 return s
182
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900183def derive_milenage_opc(ki_hex, op_hex):
184 """
185 Run the milenage algorithm to calculate OPC from Ki and OP
186 """
187 from Crypto.Cipher import AES
188 from Crypto.Util.strxor import strxor
189 from pySim.utils import b2h
190
191 # We pass in hex string and now need to work on bytes
192 aes = AES.new(h2b(ki_hex))
193 opc_bytes = aes.encrypt(h2b(op_hex))
194 return b2h(strxor(opc_bytes, h2b(op_hex)))
195
196def calculate_luhn(cc):
197 """
198 Calculate Luhn checksum used in e.g. ICCID and IMEI
199 """
200 num = map(int, str(cc))
201 check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
202 return 0 if check_digit == 10 else check_digit
Philipp Maier7592eee2019-09-12 13:03:23 +0200203
204def mcc_from_imsi(imsi):
205 """
206 Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
207 """
208 if imsi == None:
209 return None
210
211 if len(imsi) > 3:
212 return imsi[:3]
213 else:
214 return None
215
216def mnc_from_imsi(imsi, long=False):
217 """
218 Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
219 """
220 if imsi == None:
221 return None
222
223 if len(imsi) > 3:
224 if long:
225 return imsi[3:6]
226 else:
227 return imsi[3:5]
228 else:
229 return None
Supreeth Herled24f1632019-11-30 10:37:09 +0100230
231def derive_mcc(digit1, digit2, digit3):
232 """
233 Derive decimal representation of the MCC (Mobile Country Code)
234 from three given digits.
235 """
236
237 mcc = 0
238
239 if digit1 != 0x0f:
240 mcc += digit1 * 100
241 if digit2 != 0x0f:
242 mcc += digit2 * 10
243 if digit3 != 0x0f:
244 mcc += digit3
245
246 return mcc
247
248def derive_mnc(digit1, digit2, digit3=0x0f):
249 """
250 Derive decimal representation of the MNC (Mobile Network Code)
251 from two or (optionally) three given digits.
252 """
253
254 mnc = 0
255
256 # 3-rd digit is optional for the MNC. If present
257 # the algorythm is the same as for the MCC.
258 if digit3 != 0x0f:
259 return derive_mcc(digit1, digit2, digit3)
260
261 if digit1 != 0x0f:
262 mnc += digit1 * 10
263 if digit2 != 0x0f:
264 mnc += digit2
265
266 return mnc
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100267
268def dec_msisdn(ef_msisdn):
269 """
270 Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
271 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
272 """
273
274 # Convert from str to (kind of) 'bytes'
275 ef_msisdn = h2b(ef_msisdn)
276
277 # Make sure mandatory fields are present
278 if len(ef_msisdn) < 14:
279 raise ValueError("EF.MSISDN is too short")
280
281 # Skip optional Alpha Identifier
282 xlen = len(ef_msisdn) - 14
283 msisdn_lhv = ef_msisdn[xlen:]
284
285 # Parse the length (in bytes) of the BCD encoded number
286 bcd_len = ord(msisdn_lhv[0])
287 # BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
288 if bcd_len == 0xff:
289 return None
290 elif bcd_len > 11 or bcd_len < 1:
291 raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
292
293 # Parse ToN / NPI
294 ton = (ord(msisdn_lhv[1]) >> 4) & 0x07
295 npi = ord(msisdn_lhv[1]) & 0x0f
296 bcd_len -= 1
297
298 # No MSISDN?
299 if not bcd_len:
300 return (npi, ton, None)
301
302 msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
303 # International number 10.5.118/3GPP TS 24.008
Vadim Yanitskiy7ba24282020-02-27 00:04:13 +0700304 if ton == 0x01:
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100305 msisdn = '+' + msisdn
306
307 return (npi, ton, msisdn)
Supreeth Herle5a541012019-12-22 08:59:16 +0100308
309def enc_msisdn(msisdn, npi=0x01, ton=0x03):
310 """
311 Encode MSISDN as LHV so it can be stored to EF.MSISDN.
312 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
313
314 Default NPI / ToN values:
315 - NPI: ISDN / telephony numbering plan (E.164 / E.163),
316 - ToN: network specific or international number (if starts with '+').
317 """
318
319 # Leading '+' indicates International Number
320 if msisdn[0] == '+':
321 msisdn = msisdn[1:]
322 ton = 0x01
323
324 # Append 'f' padding if number of digits is odd
325 if len(msisdn) % 2 > 0:
326 msisdn += 'f'
327
328 # BCD length also includes NPI/ToN header
329 bcd_len = len(msisdn) // 2 + 1
330 npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
331 bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
332
333 return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
Supreeth Herle441c4a72020-03-24 10:19:15 +0100334
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200335def dec_st(st, table="sim"):
336 """
337 Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
338 """
339
Supreeth Herledf330372020-04-20 14:48:55 +0200340 if table == "isim":
341 from pySim.ts_31_103 import EF_IST_map
342 lookup_map = EF_IST_map
343 elif table == "usim":
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200344 from pySim.ts_31_102 import EF_UST_map
345 lookup_map = EF_UST_map
346 else:
347 from pySim.ts_51_011 import EF_SST_map
348 lookup_map = EF_SST_map
349
350 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
351
352 avail_st = ""
353 # Get each byte and check for available services
354 for i in range(0, len(st_bytes)):
355 # Byte i contains info about Services num (8i+1) to num (8i+8)
356 byte = int(st_bytes[i], 16)
357 # Services in each byte are in order MSB to LSB
358 # MSB - Service (8i+8)
359 # LSB - Service (8i+1)
360 for j in range(1, 9):
361 if byte&0x01 == 0x01 and ((8*i) + j in lookup_map):
362 # Byte X contains info about Services num (8X-7) to num (8X)
363 # bit = 1: service available
364 # bit = 0: service not available
365 avail_st += '\tService %d - %s\n' % ((8*i) + j, lookup_map[(8*i) + j])
366 byte = byte >> 1
367 return avail_st
Supreeth Herle98370552020-05-11 09:04:41 +0200368
369def first_TLV_parser(bytelist):
370 '''
371 first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
372
373 parses first TLV format record in a list of bytelist
374 returns a 3-Tuple: Tag, Length, Value
375 Value is a list of bytes
376 parsing of length is ETSI'style 101.220
377 '''
378 Tag = bytelist[0]
379 if bytelist[1] == 0xFF:
380 Len = bytelist[2]*256 + bytelist[3]
381 Val = bytelist[4:4+Len]
382 else:
383 Len = bytelist[1]
384 Val = bytelist[2:2+Len]
385 return (Tag, Len, Val)
386
387def TLV_parser(bytelist):
388 '''
389 TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
390
391 loops on the input list of bytes with the "first_TLV_parser()" function
392 returns a list of 3-Tuples
393 '''
394 ret = []
395 while len(bytelist) > 0:
396 T, L, V = first_TLV_parser(bytelist)
397 if T == 0xFF:
398 # padding bytes
399 break
400 ret.append( (T, L, V) )
401 # need to manage length of L
402 if L > 0xFE:
403 bytelist = bytelist[ L+4 : ]
404 else:
405 bytelist = bytelist[ L+2 : ]
406 return ret
Supreeth Herled572ede2020-03-22 09:55:04 +0100407
408def dec_epdgid(hexstr):
409 """
410 Decode ePDG Id to get EF.ePDGId or EF.ePDGIdEm.
411 See 3GPP TS 31.102 version 13.4.0 Release 13, section 4.2.102 and 4.2.104.
412 """
413
414 # Convert from hex str to int bytes list
415 epdgid_bytes = h2i(hexstr)
416
417 s = ""
418
419 # Get list of tuples containing parsed TLVs
420 tlvs = TLV_parser(epdgid_bytes)
421
422 for tlv in tlvs:
423 # tlv = (T, L, [V])
424 # T = Tag
425 # L = Length
426 # [V] = List of value
427
428 # Invalid Tag value scenario
429 if tlv[0] != 0x80:
430 continue
431
432 # First byte in the value has the address type
433 addr_type = tlv[2][0]
434 # TODO: Support parsing of IPv4 and IPv6
435 if addr_type == 0x00: #FQDN
436 # Skip address tye byte i.e. first byte in value list
437 content = tlv[2][1:]
438 s += "\t%s # %s\n" % (i2h(content), i2s(content))
439
440 return s
Philipp Maierff84c232020-05-12 17:24:18 +0200441
442def init_reader(opts):
443 """
444 Init card reader driver
445 """
446 if opts.pcsc_dev is not None:
447 print("Using PC/SC reader interface")
448 from pySim.transport.pcsc import PcscSimLink
449 sl = PcscSimLink(opts.pcsc_dev)
450 elif opts.osmocon_sock is not None:
451 print("Using Calypso-based (OsmocomBB) reader interface")
452 from pySim.transport.calypso import CalypsoSimLink
453 sl = CalypsoSimLink(sock_path=opts.osmocon_sock)
454 else: # Serial reader is default
455 print("Using serial reader interface")
456 from pySim.transport.serial import SerialSimLink
457 sl = SerialSimLink(device=opts.device, baudrate=opts.baudrate)
458
459 return sl