blob: 1980685fa0ec513452b523fffd5d7b6c7f9fc45e [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
Sylvain Munaut76504e02010-12-07 00:24:32 +010043def swap_nibbles(s):
44 return ''.join([x+y for x,y in zip(s[1::2], s[0::2])])
45
46def rpad(s, l, c='f'):
47 return s + c * (l - len(s))
48
49def lpad(s, l, c='f'):
50 return c * (l - len(s)) + s
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040051
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020052def half_round_up(n):
53 return (n + 1)//2
54
55# IMSI encoded format:
56# For IMSI 0123456789ABCDE:
57#
58# | byte 1 | 2 upper | 2 lower | 3 upper | 3 lower | ... | 9 upper | 9 lower |
59# | length in bytes | 0 | odd/even | 2 | 1 | ... | E | D |
60#
61# If the IMSI is less than 15 characters, it should be padded with 'f' from the end.
62#
63# The length is the total number of bytes used to encoded the IMSI. This includes the odd/even
64# parity bit. E.g. an IMSI of length 14 is 8 bytes long, not 7, as it uses bytes 2 to 9 to
65# encode itself.
66#
67# Because of this, an odd length IMSI fits exactly into len(imsi) + 1 // 2 bytes, whereas an
68# even length IMSI only uses half of the last byte.
69
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040070def enc_imsi(imsi):
71 """Converts a string imsi into the value of the EF"""
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020072 l = half_round_up(len(imsi) + 1) # Required bytes - include space for odd/even indicator
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040073 oe = len(imsi) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020074 ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe<<3)|1, rpad(imsi, 15)))
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040075 return ei
76
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040077def dec_imsi(ef):
78 """Converts an EF value to the imsi string representation"""
79 if len(ef) < 4:
80 return None
Pau Espin Pedrol665bd222017-12-29 20:30:35 +010081 l = int(ef[0:2], 16) * 2 # Length of the IMSI string
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020082 l = l - 1 # Encoded length byte includes oe nibble
83 swapped = swap_nibbles(ef[2:]).rstrip('f')
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040084 oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020085 if not oe:
86 # if even, only half of last byte was used
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040087 l = l-1
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020088 if l != len(swapped) - 1:
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040089 return None
Ben Fox-Moore0ec14752018-09-24 15:47:02 +020090 imsi = swapped[1:]
Alexander Chemeris5e96c3d2013-07-04 17:33:33 +040091 return imsi
92
93def dec_iccid(ef):
94 return swap_nibbles(ef).strip('f')
Alexander Chemeris7be92ff2013-07-10 11:18:06 +040095
96def enc_iccid(iccid):
97 return swap_nibbles(rpad(iccid, 20))
98
99def enc_plmn(mcc, mnc):
Alexander Chemerisdddbf522017-07-18 16:49:59 +0300100 """Converts integer MCC/MNC into 3 bytes for EF"""
Philipp Maierbe069e22019-09-12 12:52:43 +0200101 return swap_nibbles(lpad('%d' % int(mcc), 3) + lpad('%d' % int(mnc), 3))
Alexander Chemerisa5f0ea62017-07-18 16:48:47 +0300102
103def dec_spn(ef):
104 byte1 = int(ef[0:2])
105 hplmn_disp = (byte1&0x01 == 0x01)
106 oplmn_disp = (byte1&0x02 == 0x02)
107 name = h2s(ef[2:])
108 return (name, hplmn_disp, oplmn_disp)
109
110def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
111 byte1 = 0x00
112 if hplmn_disp: byte1 = byte1|0x01
113 if oplmn_disp: byte1 = byte1|0x02
114 return i2h([byte1])+s2h(name)
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900115
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100116def hexstr_to_fivebytearr(s):
117 return [s[i:i+10] for i in range(0, len(s), 10) ]
118
119# Accepts hex string representing three bytes
120def dec_mcc_from_plmn(plmn):
121 ia = h2i(plmn)
122 digit1 = ia[0] & 0x0F # 1st byte, LSB
123 digit2 = (ia[0] & 0xF0) >> 4 # 1st byte, MSB
124 digit3 = ia[1] & 0x0F # 2nd byte, LSB
125 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
126 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100127 return derive_mcc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100128
129def dec_mnc_from_plmn(plmn):
130 ia = h2i(plmn)
131 digit1 = ia[2] & 0x0F # 3rd byte, LSB
132 digit2 = (ia[2] & 0xF0) >> 4 # 3rd byte, MSB
133 digit3 = (ia[1] & 0xF0) >> 4 # 2nd byte, MSB
134 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
135 return 0xFFF # 4095
Supreeth Herled24f1632019-11-30 10:37:09 +0100136 return derive_mnc(digit1, digit2, digit3)
Daniel Laszlo Sitzer851e9c02018-12-04 19:40:08 +0100137
138def dec_act(twohexbytes):
139 act_list = [
140 {'bit': 15, 'name': "UTRAN"},
141 {'bit': 14, 'name': "E-UTRAN"},
142 {'bit': 7, 'name': "GSM"},
143 {'bit': 6, 'name': "GSM COMPACT"},
144 {'bit': 5, 'name': "cdma2000 HRPD"},
145 {'bit': 4, 'name': "cdma2000 1xRTT"},
146 ]
147 ia = h2i(twohexbytes)
148 u16t = (ia[0] << 8)|ia[1]
149 sel = []
150 for a in act_list:
151 if u16t & (1 << a['bit']):
152 sel.append(a['name'])
153 return sel
154
155def dec_xplmn_w_act(fivehexbytes):
156 res = {'mcc': 0, 'mnc': 0, 'act': []}
157 plmn_chars = 6
158 act_chars = 4
159 plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
160 act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
161 res['mcc'] = dec_mcc_from_plmn(plmn_str)
162 res['mnc'] = dec_mnc_from_plmn(plmn_str)
163 res['act'] = dec_act(act_str)
164 return res
165
166def format_xplmn_w_act(hexstr):
167 s = ""
168 for rec_data in hexstr_to_fivebytearr(hexstr):
169 rec_info = dec_xplmn_w_act(rec_data)
170 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
171 rec_str = "unused"
172 else:
Supreeth Herled24f1632019-11-30 10:37:09 +0100173 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 +0100174 s += "\t%s # %s\n" % (rec_data, rec_str)
175 return s
176
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900177def derive_milenage_opc(ki_hex, op_hex):
178 """
179 Run the milenage algorithm to calculate OPC from Ki and OP
180 """
181 from Crypto.Cipher import AES
182 from Crypto.Util.strxor import strxor
183 from pySim.utils import b2h
184
185 # We pass in hex string and now need to work on bytes
186 aes = AES.new(h2b(ki_hex))
187 opc_bytes = aes.encrypt(h2b(op_hex))
188 return b2h(strxor(opc_bytes, h2b(op_hex)))
189
190def calculate_luhn(cc):
191 """
192 Calculate Luhn checksum used in e.g. ICCID and IMEI
193 """
194 num = map(int, str(cc))
195 check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
196 return 0 if check_digit == 10 else check_digit
Philipp Maier7592eee2019-09-12 13:03:23 +0200197
198def mcc_from_imsi(imsi):
199 """
200 Derive the MCC (Mobile Country Code) from the first three digits of an IMSI
201 """
202 if imsi == None:
203 return None
204
205 if len(imsi) > 3:
206 return imsi[:3]
207 else:
208 return None
209
210def mnc_from_imsi(imsi, long=False):
211 """
212 Derive the MNC (Mobile Country Code) from the 4th to 6th digit of an IMSI
213 """
214 if imsi == None:
215 return None
216
217 if len(imsi) > 3:
218 if long:
219 return imsi[3:6]
220 else:
221 return imsi[3:5]
222 else:
223 return None
Supreeth Herled24f1632019-11-30 10:37:09 +0100224
225def derive_mcc(digit1, digit2, digit3):
226 """
227 Derive decimal representation of the MCC (Mobile Country Code)
228 from three given digits.
229 """
230
231 mcc = 0
232
233 if digit1 != 0x0f:
234 mcc += digit1 * 100
235 if digit2 != 0x0f:
236 mcc += digit2 * 10
237 if digit3 != 0x0f:
238 mcc += digit3
239
240 return mcc
241
242def derive_mnc(digit1, digit2, digit3=0x0f):
243 """
244 Derive decimal representation of the MNC (Mobile Network Code)
245 from two or (optionally) three given digits.
246 """
247
248 mnc = 0
249
250 # 3-rd digit is optional for the MNC. If present
251 # the algorythm is the same as for the MCC.
252 if digit3 != 0x0f:
253 return derive_mcc(digit1, digit2, digit3)
254
255 if digit1 != 0x0f:
256 mnc += digit1 * 10
257 if digit2 != 0x0f:
258 mnc += digit2
259
260 return mnc
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100261
262def dec_msisdn(ef_msisdn):
263 """
264 Decode MSISDN from EF.MSISDN or EF.ADN (same structure).
265 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
266 """
267
268 # Convert from str to (kind of) 'bytes'
269 ef_msisdn = h2b(ef_msisdn)
270
271 # Make sure mandatory fields are present
272 if len(ef_msisdn) < 14:
273 raise ValueError("EF.MSISDN is too short")
274
275 # Skip optional Alpha Identifier
276 xlen = len(ef_msisdn) - 14
277 msisdn_lhv = ef_msisdn[xlen:]
278
279 # Parse the length (in bytes) of the BCD encoded number
280 bcd_len = ord(msisdn_lhv[0])
281 # BCD length = length of dial num (max. 10 bytes) + 1 byte ToN and NPI
282 if bcd_len == 0xff:
283 return None
284 elif bcd_len > 11 or bcd_len < 1:
285 raise ValueError("Length of MSISDN (%d bytes) is out of range" % bcd_len)
286
287 # Parse ToN / NPI
288 ton = (ord(msisdn_lhv[1]) >> 4) & 0x07
289 npi = ord(msisdn_lhv[1]) & 0x0f
290 bcd_len -= 1
291
292 # No MSISDN?
293 if not bcd_len:
294 return (npi, ton, None)
295
296 msisdn = swap_nibbles(b2h(msisdn_lhv[2:][:bcd_len])).rstrip('f')
297 # International number 10.5.118/3GPP TS 24.008
Vadim Yanitskiy7ba24282020-02-27 00:04:13 +0700298 if ton == 0x01:
Supreeth Herle4b1c7632019-12-22 09:00:59 +0100299 msisdn = '+' + msisdn
300
301 return (npi, ton, msisdn)
Supreeth Herle5a541012019-12-22 08:59:16 +0100302
303def enc_msisdn(msisdn, npi=0x01, ton=0x03):
304 """
305 Encode MSISDN as LHV so it can be stored to EF.MSISDN.
306 See 3GPP TS 31.102, section 4.2.26 and 4.4.2.3.
307
308 Default NPI / ToN values:
309 - NPI: ISDN / telephony numbering plan (E.164 / E.163),
310 - ToN: network specific or international number (if starts with '+').
311 """
312
313 # Leading '+' indicates International Number
314 if msisdn[0] == '+':
315 msisdn = msisdn[1:]
316 ton = 0x01
317
318 # Append 'f' padding if number of digits is odd
319 if len(msisdn) % 2 > 0:
320 msisdn += 'f'
321
322 # BCD length also includes NPI/ToN header
323 bcd_len = len(msisdn) // 2 + 1
324 npi_ton = (npi & 0x0f) | ((ton & 0x07) << 4) | 0x80
325 bcd = rpad(swap_nibbles(msisdn), 10 * 2) # pad to 10 octets
326
327 return ('%02x' % bcd_len) + ('%02x' % npi_ton) + bcd
Supreeth Herle441c4a72020-03-24 10:19:15 +0100328
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200329def dec_st(st, table="sim"):
330 """
331 Parses the EF S/U/IST and prints the list of available services in EF S/U/IST
332 """
333
Supreeth Herledf330372020-04-20 14:48:55 +0200334 if table == "isim":
335 from pySim.ts_31_103 import EF_IST_map
336 lookup_map = EF_IST_map
337 elif table == "usim":
Supreeth Herle0c4d82d2020-04-20 13:28:31 +0200338 from pySim.ts_31_102 import EF_UST_map
339 lookup_map = EF_UST_map
340 else:
341 from pySim.ts_51_011 import EF_SST_map
342 lookup_map = EF_SST_map
343
344 st_bytes = [st[i:i+2] for i in range(0, len(st), 2) ]
345
346 avail_st = ""
347 # Get each byte and check for available services
348 for i in range(0, len(st_bytes)):
349 # Byte i contains info about Services num (8i+1) to num (8i+8)
350 byte = int(st_bytes[i], 16)
351 # Services in each byte are in order MSB to LSB
352 # MSB - Service (8i+8)
353 # LSB - Service (8i+1)
354 for j in range(1, 9):
355 if byte&0x01 == 0x01 and ((8*i) + j in lookup_map):
356 # Byte X contains info about Services num (8X-7) to num (8X)
357 # bit = 1: service available
358 # bit = 0: service not available
359 avail_st += '\tService %d - %s\n' % ((8*i) + j, lookup_map[(8*i) + j])
360 byte = byte >> 1
361 return avail_st