blob: ee4d2f39ca818e11491d2240b8def1f205f753a1 [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"""
Philipp Maierbe069e22019-09-12 12:52:43 +0200105 return swap_nibbles(lpad('%d' % int(mcc), 3) + lpad('%d' % int(mnc), 3))
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +0300106
107def dec_spn(ef):
108 byte1 = int(ef[0:2])
109 hplmn_disp = (byte1&0x01 == 0x01)
110 oplmn_disp = (byte1&0x02 == 0x02)
111 name = h2s(ef[2:])
112 return (name, hplmn_disp, oplmn_disp)
113
114def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
115 byte1 = 0x00
116 if hplmn_disp: byte1 = byte1|0x01
117 if oplmn_disp: byte1 = byte1|0x02
118 return i2h([byte1])+s2h(name)
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900119
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100120def hexstr_to_fivebytearr(s):
121 return [s[i:i+10] for i in range(0, len(s), 10) ]
122
123# Accepts hex string representing three bytes
124def dec_mcc_from_plmn(plmn):
125 ia = h2i(plmn)
126 digit1 = ia[0] & 0x0F # 1st byte, LSB
127 digit2 = (ia[0] & 0xF0) >> 4 # 1st byte, MSB
128 digit3 = ia[1] & 0x0F # 2nd byte, LSB
129 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
130 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100131 return derive_mcc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100132
133def dec_mnc_from_plmn(plmn):
134 ia = h2i(plmn)
135 digit1 = ia[2] & 0x0F # 3rd byte, LSB
136 digit2 = (ia[2] & 0xF0) >> 4 # 3rd byte, MSB
137 digit3 = (ia[1] & 0xF0) >> 4 # 2nd byte, MSB
138 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
139 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100140 return derive_mnc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100141
142def dec_act(twohexbytes):
143 act_list = [
144 {'bit': 15, 'name': "UTRAN"},
145 {'bit': 14, 'name': "E-UTRAN"},
146 {'bit': 7, 'name': "GSM"},
147 {'bit': 6, 'name': "GSM COMPACT"},
148 {'bit': 5, 'name': "cdma2000 HRPD"},
149 {'bit': 4, 'name': "cdma2000 1xRTT"},
150 ]
151 ia = h2i(twohexbytes)
152 u16t = (ia[0] << 8)|ia[1]
153 sel = []
154 for a in act_list:
155 if u16t & (1 << a['bit']):
156 sel.append(a['name'])
157 return sel
158
159def dec_xplmn_w_act(fivehexbytes):
160 res = {'mcc': 0, 'mnc': 0, 'act': []}
161 plmn_chars = 6
162 act_chars = 4
163 plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
164 act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
165 res['mcc'] = dec_mcc_from_plmn(plmn_str)
166 res['mnc'] = dec_mnc_from_plmn(plmn_str)
167 res['act'] = dec_act(act_str)
168 return res
169
170def format_xplmn_w_act(hexstr):
171 s = ""
172 for rec_data in hexstr_to_fivebytearr(hexstr):
173 rec_info = dec_xplmn_w_act(rec_data)
174 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
175 rec_str = "unused"
176 else:
Supreeth Herled24f1632019-11-30 10:37:09 +0100177 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 +0100178 s += "\t%s # %s\n" % (rec_data, rec_str)
179 return s
180
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900181def derive_milenage_opc(ki_hex, op_hex):
182 """
183 Run the milenage algorithm to calculate OPC from Ki and OP
184 """
185 from Crypto.Cipher import AES
186 from Crypto.Util.strxor import strxor
187 from pySim.utils import b2h
188
189 # We pass in hex string and now need to work on bytes
190 aes = AES.new(h2b(ki_hex))
191 opc_bytes = aes.encrypt(h2b(op_hex))
192 return b2h(strxor(opc_bytes, h2b(op_hex)))
193
194def calculate_luhn(cc):
195 """
196 Calculate Luhn checksum used in e.g. ICCID and IMEI
197 """
198 num = map(int, str(cc))
199 check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
200 return 0 if check_digit == 10 else check_digit
Philipp Maier7592eee2019-09-12 13:03:23 +0200201
202def mcc_from_imsi(imsi):
203 """
204 Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
205 """
206 if imsi == None:
207 return None
208
209 if len(imsi) > 3:
210 return imsi[:3]
211 else:
212 return None
213
214def mnc_from_imsi(imsi, long=False):
215 """
216 Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
217 """
218 if imsi == None:
219 return None
220
221 if len(imsi) > 3:
222 if long:
223 return imsi[3:6]
224 else:
225 return imsi[3:5]
226 else:
227 return None
Supreeth Herled24f1632019-11-30 10:37:09 +0100228
229def derive_mcc(digit1, digit2, digit3):
230 """
231 Derive decimal representation of the MCC (Mobile Country Code)
232 from three given digits.
233 """
234
235 mcc = 0
236
237 if digit1 != 0x0f:
238 mcc += digit1 * 100
239 if digit2 != 0x0f:
240 mcc += digit2 * 10
241 if digit3 != 0x0f:
242 mcc += digit3
243
244 return mcc
245
246def derive_mnc(digit1, digit2, digit3=0x0f):
247 """
248 Derive decimal representation of the MNC (Mobile Network Code)
249 from two or (optionally) three given digits.
250 """
251
252 mnc = 0
253
254 # 3-rd digit is optional for the MNC. If present
255 # the algorythm is the same as for the MCC.
256 if digit3 != 0x0f:
257 return derive_mcc(digit1, digit2, digit3)
258
259 if digit1 != 0x0f:
260 mnc += digit1 * 10
261 if digit2 != 0x0f:
262 mnc += digit2
263
264 return mnc
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100265
266def dec_msisdn(ef_msisdn):
267 """
268 Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
269 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
270 """
271
272 # Convert from str to (kind of) 'bytes'
273 ef_msisdn = h2b(ef_msisdn)
274
275 # Make sure mandatory fields are present
276 if len(ef_msisdn) < 14:
277 raise ValueError("EF.MSISDN is too short")
278
279 # Skip optional Alpha Identifier
280 xlen = len(ef_msisdn) - 14
281 msisdn_lhv = ef_msisdn[xlen:]
282
283 # Parse the length (in bytes) of the BCD encoded number
284 bcd_len = ord(msisdn_lhv[0])
285 # BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
286 if bcd_len == 0xff:
287 return None
288 elif bcd_len > 11 or bcd_len < 1:
289 raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
290
291 # Parse ToN / NPI
292 ton = (ord(msisdn_lhv[1]) >> 4) & 0x07
293 npi = ord(msisdn_lhv[1]) & 0x0f
294 bcd_len -= 1
295
296 # No MSISDN?
297 if not bcd_len:
298 return (npi, ton, None)
299
300 msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
301 # International number 10.5.118/3GPP TS 24.008
Vadim Yanitskiy7ba24282020-02-27 00:04:13 +0700302 if ton == 0x01:
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100303 msisdn = '+' + msisdn
304
305 return (npi, ton, msisdn)
Supreeth Herle5a541012019-12-22 08:59:16 +0100306
307def enc_msisdn(msisdn, npi=0x01, ton=0x03):
308 """
309 Encode MSISDN as LHV so it can be stored to EF.MSISDN.
310 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
311
312 Default NPI / ToN values:
313 - NPI: ISDN / telephony numbering plan (E.164 / E.163),
314 - ToN: network specific or international number (if starts with '+').
315 """
316
317 # Leading '+' indicates International Number
318 if msisdn[0] == '+':
319 msisdn = msisdn[1:]
320 ton = 0x01
321
322 # Append 'f' padding if number of digits is odd
323 if len(msisdn) % 2 > 0:
324 msisdn += 'f'
325
326 # BCD length also includes NPI/ToN header
327 bcd_len = len(msisdn) // 2 + 1
328 npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
329 bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
330
331 return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
Supreeth Herle441c4a72020-03-24 10:19:15 +0100332
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200333def dec_st(st, table="sim"):
334 """
335 Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
336 """
337
Supreeth Herledf330372020-04-20 14:48:55 +0200338 if table == "isim":
339 from pySim.ts_31_103 import EF_IST_map
340 lookup_map = EF_IST_map
341 elif table == "usim":
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200342 from pySim.ts_31_102 import EF_UST_map
343 lookup_map = EF_UST_map
344 else:
345 from pySim.ts_51_011 import EF_SST_map
346 lookup_map = EF_SST_map
347
348 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
349
350 avail_st = ""
351 # Get each byte and check for available services
352 for i in range(0, len(st_bytes)):
353 # Byte i contains info about Services num (8i+1) to num (8i+8)
354 byte = int(st_bytes[i], 16)
355 # Services in each byte are in order MSB to LSB
356 # MSB - Service (8i+8)
357 # LSB - Service (8i+1)
358 for j in range(1, 9):
359 if byte&0x01 == 0x01 and ((8*i) + j in lookup_map):
360 # Byte X contains info about Services num (8X-7) to num (8X)
361 # bit = 1: service available
362 # bit = 0: service not available
363 avail_st += '\tService %d - %s\n' % ((8*i) + j, lookup_map[(8*i) + j])
364 byte = byte >> 1
365 return avail_st
Supreeth Herle98370552020-05-11 09:04:41 +0200366
367def first_TLV_parser(bytelist):
368 '''
369 first_TLV_parser([0xAA, 0x02, 0xAB, 0xCD, 0xFF, 0x00]) -> (170, 2, [171, 205])
370
371 parses first TLV format record in a list of bytelist
372 returns a 3-Tuple: Tag, Length, Value
373 Value is a list of bytes
374 parsing of length is ETSI'style 101.220
375 '''
376 Tag = bytelist[0]
377 if bytelist[1] == 0xFF:
378 Len = bytelist[2]*256 + bytelist[3]
379 Val = bytelist[4:4+Len]
380 else:
381 Len = bytelist[1]
382 Val = bytelist[2:2+Len]
383 return (Tag, Len, Val)
384
385def TLV_parser(bytelist):
386 '''
387 TLV_parser([0xAA, ..., 0xFF]) -> [(T, L, [V]), (T, L, [V]), ...]
388
389 loops on the input list of bytes with the "first_TLV_parser()" function
390 returns a list of 3-Tuples
391 '''
392 ret = []
393 while len(bytelist) > 0:
394 T, L, V = first_TLV_parser(bytelist)
395 if T == 0xFF:
396 # padding bytes
397 break
398 ret.append( (T, L, V) )
399 # need to manage length of L
400 if L > 0xFE:
401 bytelist = bytelist[ L+4 : ]
402 else:
403 bytelist = bytelist[ L+2 : ]
404 return ret