blob: 8f7fda706f0f89b0ef66520650f5788397a2af4a [file] [log] [blame]
Sylvain Munaut76504e02010-12-07 00:24:32 +01001#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4""" pySim: Card programmation logic
5"""
6
7#
8# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
Harald Welte3156d902011-03-22 21:48:19 +01009# Copyright (C) 2011 Harald Welte <laforge@gnumonks.org>
Sylvain Munaut76504e02010-12-07 00:24:32 +010010#
11# This program is free software: you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation, either version 2 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program. If not, see <http://www.gnu.org/licenses/>.
23#
24
Harald Welte3156d902011-03-22 21:48:19 +010025from pySim.utils import b2h, h2b, swap_nibbles, rpad, lpad
Sylvain Munaut76504e02010-12-07 00:24:32 +010026
27
28class Card(object):
29
30 def __init__(self, scc):
31 self._scc = scc
32
33 def _e_iccid(self, iccid):
Harald Welte2c0ff3a2011-12-07 12:34:13 +010034 return swap_nibbles(rpad(iccid, 20))
Sylvain Munaut76504e02010-12-07 00:24:32 +010035
36 def _e_imsi(self, imsi):
37 """Converts a string imsi into the value of the EF"""
38 l = (len(imsi) + 1) // 2 # Required bytes
39 oe = len(imsi) & 1 # Odd (1) / Even (0)
40 ei = '%02x' % l + swap_nibbles(lpad('%01x%s' % ((oe<<3)|1, imsi), 16))
41 return ei
42
43 def _e_plmn(self, mcc, mnc):
44 """Converts integer MCC/MNC into 6 bytes for EF"""
45 return swap_nibbles(lpad('%d' % mcc, 3) + lpad('%d' % mnc, 3))
46
47 def reset(self):
48 self._scc.reset_card()
49
50
51class _MagicSimBase(Card):
52 """
53 Theses cards uses several record based EFs to store the provider infos,
54 each possible provider uses a specific record number in each EF. The
55 indexes used are ( where N is the number of providers supported ) :
56 - [2 .. N+1] for the operator name
57 - [1 .. N] for the programable EFs
58
59 * 3f00/7f4d/8f0c : Operator Name
60
61 bytes 0-15 : provider name, padded with 0xff
62 byte 16 : length of the provider name
63 byte 17 : 01 for valid records, 00 otherwise
64
65 * 3f00/7f4d/8f0d : Programmable Binary EFs
66
67 * 3f00/7f4d/8f0e : Programmable Record EFs
68
69 """
70
71 @classmethod
72 def autodetect(kls, scc):
73 try:
74 for p, l, t in kls._files.values():
75 if not t:
76 continue
77 if scc.record_size(['3f00', '7f4d', p]) != l:
78 return None
79 except:
80 return None
81
82 return kls(scc)
83
84 def _get_count(self):
85 """
86 Selects the file and returns the total number of entries
87 and entry size
88 """
89 f = self._files['name']
90
91 r = self._scc.select_file(['3f00', '7f4d', f[0]])
92 rec_len = int(r[-1][28:30], 16)
93 tlen = int(r[-1][4:8],16)
94 rec_cnt = (tlen / rec_len) - 1;
95
96 if (rec_cnt < 1) or (rec_len != f[1]):
97 raise RuntimeError('Bad card type')
98
99 return rec_cnt
100
101 def program(self, p):
102 # Go to dir
103 self._scc.select_file(['3f00', '7f4d'])
104
105 # Home PLMN in PLMN_Sel format
106 hplmn = self._e_plmn(p['mcc'], p['mnc'])
107
108 # Operator name ( 3f00/7f4d/8f0c )
109 self._scc.update_record(self._files['name'][0], 2,
110 rpad(b2h(p['name']), 32) + ('%02x' % len(p['name'])) + '01'
111 )
112
113 # ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d )
114 v = ''
115
116 # inline Ki
117 if self._ki_file is None:
118 v += p['ki']
119
120 # ICCID
121 v += '3f00' + '2fe2' + '0a' + self._e_iccid(p['iccid'])
122
123 # IMSI
124 v += '7f20' + '6f07' + '09' + self._e_imsi(p['imsi'])
125
126 # Ki
127 if self._ki_file:
128 v += self._ki_file + '10' + p['ki']
129
130 # PLMN_Sel
131 v+= '6f30' + '18' + rpad(hplmn, 36)
132
133 self._scc.update_record(self._files['b_ef'][0], 1,
134 rpad(v, self._files['b_ef'][1]*2)
135 )
136
137 # SMSP ( 3f00/7f4d/8f0e )
138 # FIXME
139
140 # Write PLMN_Sel forcefully as well
141 r = self._scc.select_file(['3f00', '7f20', '6f30'])
142 tl = int(r[-1][4:8], 16)
143
144 hplmn = self._e_plmn(p['mcc'], p['mnc'])
145 self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
146
147 def erase(self):
148 # Dummy
149 df = {}
150 for k, v in self._files.iteritems():
151 ofs = 1
152 fv = v[1] * 'ff'
153 if k == 'name':
154 ofs = 2
155 fv = fv[0:-4] + '0000'
156 df[v[0]] = (fv, ofs)
157
158 # Write
159 for n in range(0,self._get_count()):
160 for k, (msg, ofs) in df.iteritems():
161 self._scc.update_record(['3f00', '7f4d', k], n + ofs, msg)
162
163
164class SuperSim(_MagicSimBase):
165
166 name = 'supersim'
167
168 _files = {
169 'name' : ('8f0c', 18, True),
170 'b_ef' : ('8f0d', 74, True),
171 'r_ef' : ('8f0e', 50, True),
172 }
173
174 _ki_file = None
175
176
177class MagicSim(_MagicSimBase):
178
179 name = 'magicsim'
180
181 _files = {
182 'name' : ('8f0c', 18, True),
183 'b_ef' : ('8f0d', 130, True),
184 'r_ef' : ('8f0e', 102, False),
185 }
186
187 _ki_file = '6f1b'
188
189
190class FakeMagicSim(Card):
191 """
192 Theses cards have a record based EF 3f00/000c that contains the provider
193 informations. See the program method for its format. The records go from
194 1 to N.
195 """
196
197 name = 'fakemagicsim'
198
199 @classmethod
200 def autodetect(kls, scc):
201 try:
202 if scc.record_size(['3f00', '000c']) != 0x5a:
203 return None
204 except:
205 return None
206
207 return kls(scc)
208
209 def _get_infos(self):
210 """
211 Selects the file and returns the total number of entries
212 and entry size
213 """
214
215 r = self._scc.select_file(['3f00', '000c'])
216 rec_len = int(r[-1][28:30], 16)
217 tlen = int(r[-1][4:8],16)
218 rec_cnt = (tlen / rec_len) - 1;
219
220 if (rec_cnt < 1) or (rec_len != 0x5a):
221 raise RuntimeError('Bad card type')
222
223 return rec_cnt, rec_len
224
225 def program(self, p):
226 # Home PLMN
227 r = self._scc.select_file(['3f00', '7f20', '6f30'])
228 tl = int(r[-1][4:8], 16)
229
230 hplmn = self._e_plmn(p['mcc'], p['mnc'])
231 self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
232
233 # Get total number of entries and entry size
234 rec_cnt, rec_len = self._get_infos()
235
236 # Set first entry
237 entry = (
238 '81' + # 1b Status: Valid & Active
239 rpad(b2h(p['name'][0:14]), 28) + # 14b Entry Name
240 self._e_iccid(p['iccid']) + # 10b ICCID
241 self._e_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
242 p['ki'] + # 16b Ki
243 24*'f' + 'fd' + 24*'f' + # 25b (unknown ...)
244 rpad(p['smsp'], 20) + # 10b SMSP (padded with ff if needed)
245 10*'f' # 5b (unknown ...)
246 )
247 self._scc.update_record('000c', 1, entry)
248
249 def erase(self):
250 # Get total number of entries and entry size
251 rec_cnt, rec_len = self._get_infos()
252
253 # Erase all entries
254 entry = 'ff' * rec_len
255 for i in range(0, rec_cnt):
256 self._scc.update_record('000c', 1+i, entry)
257
Harald Welte3156d902011-03-22 21:48:19 +0100258class GrcardSim(Card):
259 """
260 Greencard (grcard.cn) HZCOS GSM SIM
261 These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
262 and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
263 """
264
265 name = 'grcardsim'
266
267 @classmethod
268 def autodetect(kls, scc):
269 return None
270
271 def program(self, p):
272 # We don't really know yet what ADM PIN 4 is about
273 #self._scc.verify_chv(4, h2b("4444444444444444"))
274
275 # Authenticate using ADM PIN 5
276 self._scc.verify_chv(5, h2b("4444444444444444"))
277
278 # EF.ICCID
279 r = self._scc.select_file(['3f00', '2fe2'])
280 data, sw = self._scc.update_binary('2fe2', self._e_iccid(p['iccid']))
281
282 # EF.IMSI
283 r = self._scc.select_file(['3f00', '7f20', '6f07'])
284 data, sw = self._scc.update_binary('6f07', self._e_imsi(p['imsi']))
285
286 # EF.ACC
287 #r = self._scc.select_file(['3f00', '7f20', '6f78'])
288 #self._scc.update_binary('6f78', self._e_imsi(p['imsi'])
289
290 # EF.SMSP
291 r = self._scc.select_file(['3f00', '7f10', '6f42'])
292 data, sw = self._scc.update_record('6f42', 1, rpad(p['smsp'], 80))
293
294 # Set the Ki using proprietary command
295 pdu = '80d4020010' + p['ki']
296 data, sw = self._scc._tp.send_apdu(pdu)
297
298 # EF.HPLMN
299 r = self._scc.select_file(['3f00', '7f20', '6f30'])
300 size = int(r[-1][4:8], 16)
301 hplmn = self._e_plmn(p['mcc'], p['mnc'])
302 self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
303
304 # EF.SPN (Service Provider Name)
305 r = self._scc.select_file(['3f00', '7f20', '6f30'])
306 size = int(r[-1][4:8], 16)
307 # FIXME
308
309 # FIXME: EF.MSISDN
310
311 def erase(self):
312 return
Sylvain Munaut76504e02010-12-07 00:24:32 +0100313
314 # In order for autodetection ...
Harald Welte3156d902011-03-22 21:48:19 +0100315_cards_classes = [ FakeMagicSim, SuperSim, MagicSim, GrcardSim ]