blob: 65f10c5ca79c7db8453248a77b5979450e3e87f7 [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"""
Alexander Chemeris7be92ff2013-07-10 11:18:06 +0400101 return swap_nibbles(lpad('%d' % mcc, 3) + lpad('%d' % 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
127 mcc = digit1 * 100
128 mcc += digit2 * 10
129 mcc += digit3
130 return mcc
131
132def dec_mnc_from_plmn(plmn):
133 ia = h2i(plmn)
134 digit1 = ia[2] & 0x0F # 3rd byte, LSB
135 digit2 = (ia[2] & 0xF0) >> 4 # 3rd byte, MSB
136 digit3 = (ia[1] & 0xF0) >> 4 # 2nd byte, MSB
137 if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
138 return 0xFFF # 4095
139 mnc = 0
140 # signifies two digit MNC
141 if digit3 == 0xF:
142 mnc += digit1 * 10
143 mnc += digit2
144 else:
145 mnc += digit1 * 100
146 mnc += digit2 * 10
147 mnc += digit3
148 return mnc
149
150def dec_act(twohexbytes):
151 act_list = [
152 {'bit': 15, 'name': "UTRAN"},
153 {'bit': 14, 'name': "E-UTRAN"},
154 {'bit': 7, 'name': "GSM"},
155 {'bit': 6, 'name': "GSM COMPACT"},
156 {'bit': 5, 'name': "cdma2000 HRPD"},
157 {'bit': 4, 'name': "cdma2000 1xRTT"},
158 ]
159 ia = h2i(twohexbytes)
160 u16t = (ia[0] << 8)|ia[1]
161 sel = []
162 for a in act_list:
163 if u16t & (1 << a['bit']):
164 sel.append(a['name'])
165 return sel
166
167def dec_xplmn_w_act(fivehexbytes):
168 res = {'mcc': 0, 'mnc': 0, 'act': []}
169 plmn_chars = 6
170 act_chars = 4
171 plmn_str = fivehexbytes[:plmn_chars] # first three bytes (six ascii hex chars)
172 act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars] # two bytes after first three bytes
173 res['mcc'] = dec_mcc_from_plmn(plmn_str)
174 res['mnc'] = dec_mnc_from_plmn(plmn_str)
175 res['act'] = dec_act(act_str)
176 return res
177
178def format_xplmn_w_act(hexstr):
179 s = ""
180 for rec_data in hexstr_to_fivebytearr(hexstr):
181 rec_info = dec_xplmn_w_act(rec_data)
182 if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
183 rec_str = "unused"
184 else:
185 rec_str = "MCC: %3s MNC: %3s AcT: %s" % (rec_info['mcc'], rec_info['mnc'], ", ".join(rec_info['act']))
186 s += "\t%s # %s\n" % (rec_data, rec_str)
187 return s
188
Alexander Chemeris19fffa12018-01-11 13:06:43 +0900189def derive_milenage_opc(ki_hex, op_hex):
190 """
191 Run the milenage algorithm to calculate OPC from Ki and OP
192 """
193 from Crypto.Cipher import AES
194 from Crypto.Util.strxor import strxor
195 from pySim.utils import b2h
196
197 # We pass in hex string and now need to work on bytes
198 aes = AES.new(h2b(ki_hex))
199 opc_bytes = aes.encrypt(h2b(op_hex))
200 return b2h(strxor(opc_bytes, h2b(op_hex)))
201
202def calculate_luhn(cc):
203 """
204 Calculate Luhn checksum used in e.g. ICCID and IMEI
205 """
206 num = map(int, str(cc))
207 check_digit = 10 - sum(num[-2::-2] + [sum(divmod(d * 2, 10)) for d in num[::-2]]) % 10
208 return 0 if check_digit == 10 else check_digit