blob: 55965d7e19da2744a5017b5199bec5222973f0c9 [file] [log] [blame]
Sylvain Munaut76504e02010-12-07 00:24:32 +01001# -*- coding: utf-8 -*-
2
3""" pySim: Card programmation logic
4"""
5
6#
7# Copyright (C) 2009-2010 Sylvain Munaut <tnt@246tNt.com>
Harald Welte3156d902011-03-22 21:48:19 +01008# Copyright (C) 2011 Harald Welte <laforge@gnumonks.org>
Alexander Chemeriseb6807d2017-07-18 17:04:38 +03009# Copyright (C) 2017 Alexander.Chemeris <Alexander.Chemeris@gmail.com>
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
Vadim Yanitskiy03c67f72021-05-02 02:10:39 +020025from typing import Optional, Dict, Tuple
Vadim Yanitskiy85302d62021-05-02 02:18:42 +020026import abc
Vadim Yanitskiy03c67f72021-05-02 02:10:39 +020027
Robert Falkenbergb07a3e92021-05-07 15:23:20 +020028from pySim.ts_51_011 import EF, DF, EF_AD, EF_SPN
Harald Welteca673942020-06-03 15:19:40 +020029from pySim.ts_31_102 import EF_USIM_ADF_map
Supreeth Herle5ad9aec2020-03-24 17:26:40 +010030from pySim.ts_31_103 import EF_ISIM_ADF_map
Alexander Chemeriseb6807d2017-07-18 17:04:38 +030031from pySim.utils import *
Alexander Chemeris8ad124a2018-01-10 14:17:55 +090032from smartcard.util import toBytes
Supreeth Herle79f43dd2020-03-25 11:43:19 +010033from pytlv.TLV import *
Sylvain Munaut76504e02010-12-07 00:24:32 +010034
Harald Weltec91085e2022-02-10 18:05:45 +010035
36def format_addr(addr: str, addr_type: str) -> str:
37 """
38 helper function to format an FQDN (addr_type = '00') or IPv4
39 (addr_type = '01') address string into a printable string that
40 contains the hexadecimal representation and the original address
41 string (addr)
42 """
43 res = ""
44 if addr_type == '00': # FQDN
45 res += "\t%s # %s\n" % (s2h(addr), addr)
46 elif addr_type == '01': # IPv4
47 octets = addr.split(".")
48 addr_hex = ""
49 for o in octets:
50 addr_hex += ("%02x" % int(o))
51 res += "\t%s # %s\n" % (addr_hex, addr)
52 return res
53
Philipp Maierbe18f2a2021-04-30 15:00:27 +020054
Vadim Yanitskiy04b5d9d2022-07-07 03:05:30 +070055class SimCard:
Sylvain Munaut76504e02010-12-07 00:24:32 +010056
Harald Weltec91085e2022-02-10 18:05:45 +010057 name = 'SIM'
Philipp Maierfc5f28d2021-05-05 12:18:41 +020058
Harald Weltec91085e2022-02-10 18:05:45 +010059 def __init__(self, scc):
60 self._scc = scc
61 self._adm_chv_num = 4
62 self._aids = []
Sylvain Munaut76504e02010-12-07 00:24:32 +010063
Harald Weltec91085e2022-02-10 18:05:45 +010064 def reset(self):
65 rc = self._scc.reset_card()
Vadim Yanitskiyb9544512022-04-21 16:46:03 +030066 if rc == 1:
Harald Weltec91085e2022-02-10 18:05:45 +010067 return self._scc.get_atr()
68 else:
69 return None
Sylvain Munaut76504e02010-12-07 00:24:32 +010070
Harald Weltec91085e2022-02-10 18:05:45 +010071 def erase(self):
72 print("warning: erasing is not supported for specified card type!")
73 return
Philipp Maierd58c6322020-05-12 16:47:45 +020074
Harald Weltec91085e2022-02-10 18:05:45 +010075 def file_exists(self, fid):
76 res_arr = self._scc.try_select_path(fid)
77 for res in res_arr:
78 if res[1] != '9000':
79 return False
80 return True
Harald Welteca673942020-06-03 15:19:40 +020081
Harald Weltec91085e2022-02-10 18:05:45 +010082 def verify_adm(self, key):
83 """Authenticate with ADM key"""
84 (res, sw) = self._scc.verify_chv(self._adm_chv_num, key)
85 return sw
Alexander Chemeriseb6807d2017-07-18 17:04:38 +030086
Harald Weltec91085e2022-02-10 18:05:45 +010087 def read_iccid(self):
88 (res, sw) = self._scc.read_binary(EF['ICCID'])
89 if sw == '9000':
90 return (dec_iccid(res), sw)
91 else:
92 return (None, sw)
Alexander Chemeriseb6807d2017-07-18 17:04:38 +030093
Harald Weltec91085e2022-02-10 18:05:45 +010094 def read_imsi(self):
95 (res, sw) = self._scc.read_binary(EF['IMSI'])
96 if sw == '9000':
97 return (dec_imsi(res), sw)
98 else:
99 return (None, sw)
Alexander Chemeriseb6807d2017-07-18 17:04:38 +0300100
Harald Weltec91085e2022-02-10 18:05:45 +0100101 def update_imsi(self, imsi):
102 data, sw = self._scc.update_binary(EF['IMSI'], enc_imsi(imsi))
103 return sw
Alexander Chemeriseb6807d2017-07-18 17:04:38 +0300104
Harald Weltec91085e2022-02-10 18:05:45 +0100105 def update_acc(self, acc):
106 data, sw = self._scc.update_binary(EF['ACC'], lpad(acc, 4, c='0'))
107 return sw
Alexander Chemeriseb6807d2017-07-18 17:04:38 +0300108
Harald Weltec91085e2022-02-10 18:05:45 +0100109 def read_hplmn_act(self):
110 (res, sw) = self._scc.read_binary(EF['HPLMNAcT'])
111 if sw == '9000':
112 return (format_xplmn_w_act(res), sw)
113 else:
114 return (None, sw)
Supreeth Herlea850a472020-03-19 12:44:11 +0100115
Harald Weltec91085e2022-02-10 18:05:45 +0100116 def update_hplmn_act(self, mcc, mnc, access_tech='FFFF'):
117 """
118 Update Home PLMN with access technology bit-field
Alexander Chemeriseb6807d2017-07-18 17:04:38 +0300119
Harald Weltec91085e2022-02-10 18:05:45 +0100120 See Section "10.3.37 EFHPLMNwAcT (HPLMN Selector with Access Technology)"
121 in ETSI TS 151 011 for the details of the access_tech field coding.
122 Some common values:
123 access_tech = '0080' # Only GSM is selected
124 access_tech = 'FFFF' # All technologies selected, even Reserved for Future Use ones
125 """
126 # get size and write EF.HPLMNwAcT
127 data = self._scc.read_binary(EF['HPLMNwAcT'], length=None, offset=0)
128 size = len(data[0]) // 2
129 hplmn = enc_plmn(mcc, mnc)
130 content = hplmn + access_tech
131 data, sw = self._scc.update_binary(
132 EF['HPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
133 return sw
Alexander Chemeriseb6807d2017-07-18 17:04:38 +0300134
Harald Weltec91085e2022-02-10 18:05:45 +0100135 def read_oplmn_act(self):
136 (res, sw) = self._scc.read_binary(EF['OPLMNwAcT'])
137 if sw == '9000':
138 return (format_xplmn_w_act(res), sw)
139 else:
140 return (None, sw)
Supreeth Herle1757b262020-03-19 12:43:11 +0100141
Harald Weltec91085e2022-02-10 18:05:45 +0100142 def update_oplmn_act(self, mcc, mnc, access_tech='FFFF'):
143 """get size and write EF.OPLMNwAcT, See note in update_hplmn_act()"""
144 data = self._scc.read_binary(EF['OPLMNwAcT'], length=None, offset=0)
145 size = len(data[0]) // 2
146 hplmn = enc_plmn(mcc, mnc)
147 content = hplmn + access_tech
148 data, sw = self._scc.update_binary(
149 EF['OPLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
150 return sw
Philipp Maierc8ce82a2018-07-04 17:57:20 +0200151
Harald Weltec91085e2022-02-10 18:05:45 +0100152 def read_plmn_act(self):
153 (res, sw) = self._scc.read_binary(EF['PLMNwAcT'])
154 if sw == '9000':
155 return (format_xplmn_w_act(res), sw)
156 else:
157 return (None, sw)
Supreeth Herle14084402020-03-19 12:42:10 +0100158
Harald Weltec91085e2022-02-10 18:05:45 +0100159 def update_plmn_act(self, mcc, mnc, access_tech='FFFF'):
160 """get size and write EF.PLMNwAcT, See note in update_hplmn_act()"""
161 data = self._scc.read_binary(EF['PLMNwAcT'], length=None, offset=0)
162 size = len(data[0]) // 2
163 hplmn = enc_plmn(mcc, mnc)
164 content = hplmn + access_tech
165 data, sw = self._scc.update_binary(
166 EF['PLMNwAcT'], content + 'ffffff0000' * (size // 5 - 1))
167 return sw
Philipp Maierc8ce82a2018-07-04 17:57:20 +0200168
Harald Weltec91085e2022-02-10 18:05:45 +0100169 def update_plmnsel(self, mcc, mnc):
170 data = self._scc.read_binary(EF['PLMNsel'], length=None, offset=0)
171 size = len(data[0]) // 2
172 hplmn = enc_plmn(mcc, mnc)
173 data, sw = self._scc.update_binary(
174 EF['PLMNsel'], hplmn + 'ff' * (size-3))
175 return sw
Philipp Maier5bf42602018-07-11 23:23:40 +0200176
Harald Weltec91085e2022-02-10 18:05:45 +0100177 def update_smsp(self, smsp):
178 data, sw = self._scc.update_record(EF['SMSP'], 1, rpad(smsp, 84))
179 return sw
Alexander Chemeriseb6807d2017-07-18 17:04:38 +0300180
Harald Weltec91085e2022-02-10 18:05:45 +0100181 def update_ad(self, mnc=None, opmode=None, ofm=None):
182 """
183 Update Administrative Data (AD)
Philipp Maieree908ae2019-03-21 16:21:12 +0100184
Harald Weltec91085e2022-02-10 18:05:45 +0100185 See Sec. "4.2.18 EF_AD (Administrative Data)"
186 in 3GPP TS 31.102 for the details of the EF_AD contents.
Philipp Maier7f9f64a2020-05-11 21:28:52 +0200187
Harald Weltec91085e2022-02-10 18:05:45 +0100188 Set any parameter to None to keep old value(s) on card.
Philipp Maier7f9f64a2020-05-11 21:28:52 +0200189
Harald Weltec91085e2022-02-10 18:05:45 +0100190 Parameters:
191 mnc (str): MNC of IMSI
192 opmode (Hex-str, 1 Byte): MS Operation Mode
193 ofm (Hex-str, 1 Byte): Operational Feature Monitor (OFM) aka Ciphering Indicator
Robert Falkenbergd0505bd2021-02-24 14:06:18 +0100194
Harald Weltec91085e2022-02-10 18:05:45 +0100195 Returns:
196 str: Return code of write operation
197 """
Robert Falkenbergd0505bd2021-02-24 14:06:18 +0100198
Harald Weltec91085e2022-02-10 18:05:45 +0100199 ad = EF_AD()
Robert Falkenbergd0505bd2021-02-24 14:06:18 +0100200
Harald Weltec91085e2022-02-10 18:05:45 +0100201 # read from card
202 raw_hex_data, sw = self._scc.read_binary(
203 EF['AD'], length=None, offset=0)
204 abstract_data = ad.decode_hex(raw_hex_data)
Robert Falkenbergd0505bd2021-02-24 14:06:18 +0100205
Harald Weltec91085e2022-02-10 18:05:45 +0100206 # perform updates
207 if mnc and abstract_data['extensions']:
208 mnclen = len(str(mnc))
209 if mnclen == 1:
210 mnclen = 2
211 if mnclen > 3:
212 raise RuntimeError('invalid length of mnc "{}"'.format(mnc))
213 abstract_data['extensions']['mnc_len'] = mnclen
214 if opmode:
215 opmode_num = int(opmode, 16)
216 if opmode_num in [int(v) for v in EF_AD.OP_MODE]:
217 abstract_data['ms_operation_mode'] = opmode_num
218 else:
219 raise RuntimeError('invalid opmode "{}"'.format(opmode))
220 if ofm:
221 abstract_data['ofm'] = bool(int(ofm, 16))
Robert Falkenbergd0505bd2021-02-24 14:06:18 +0100222
Harald Weltec91085e2022-02-10 18:05:45 +0100223 # write to card
224 raw_hex_data = ad.encode_hex(abstract_data)
225 data, sw = self._scc.update_binary(EF['AD'], raw_hex_data)
226 return sw
Philipp Maieree908ae2019-03-21 16:21:12 +0100227
Harald Weltec91085e2022-02-10 18:05:45 +0100228 def read_spn(self):
229 (content, sw) = self._scc.read_binary(EF['SPN'])
230 if sw == '9000':
231 abstract_data = EF_SPN().decode_hex(content)
232 show_in_hplmn = abstract_data['show_in_hplmn']
233 hide_in_oplmn = abstract_data['hide_in_oplmn']
234 name = abstract_data['spn']
235 return ((name, show_in_hplmn, hide_in_oplmn), sw)
236 else:
237 return (None, sw)
Alexander Chemeriseb6807d2017-07-18 17:04:38 +0300238
Harald Weltec91085e2022-02-10 18:05:45 +0100239 def update_spn(self, name="", show_in_hplmn=False, hide_in_oplmn=False):
240 abstract_data = {
241 'hide_in_oplmn': hide_in_oplmn,
242 'show_in_hplmn': show_in_hplmn,
243 'spn': name,
244 }
245 content = EF_SPN().encode_hex(abstract_data)
246 data, sw = self._scc.update_binary(EF['SPN'], content)
247 return sw
Alexander Chemeriseb6807d2017-07-18 17:04:38 +0300248
Harald Weltec91085e2022-02-10 18:05:45 +0100249 def read_binary(self, ef, length=None, offset=0):
250 ef_path = ef in EF and EF[ef] or ef
251 return self._scc.read_binary(ef_path, length, offset)
Supreeth Herled21349a2020-04-01 08:37:47 +0200252
Harald Weltec91085e2022-02-10 18:05:45 +0100253 def read_record(self, ef, rec_no):
254 ef_path = ef in EF and EF[ef] or ef
255 return self._scc.read_record(ef_path, rec_no)
Supreeth Herlead10d662020-04-01 08:43:08 +0200256
Harald Weltec91085e2022-02-10 18:05:45 +0100257 def read_gid1(self):
258 (res, sw) = self._scc.read_binary(EF['GID1'])
259 if sw == '9000':
260 return (res, sw)
261 else:
262 return (None, sw)
Supreeth Herle98a69272020-03-18 12:14:48 +0100263
Harald Weltec91085e2022-02-10 18:05:45 +0100264 def read_msisdn(self):
265 (res, sw) = self._scc.read_record(EF['MSISDN'], 1)
266 if sw == '9000':
267 return (dec_msisdn(res), sw)
268 else:
269 return (None, sw)
Supreeth Herle6d66af62020-03-19 12:49:16 +0100270
Harald Weltec91085e2022-02-10 18:05:45 +0100271 def read_aids(self):
272 """Fetch all the AIDs present on UICC"""
273 self._aids = []
274 try:
275 # Find out how many records the EF.DIR has
276 # and store all the AIDs in the UICC
277 rec_cnt = self._scc.record_count(EF['DIR'])
278 for i in range(0, rec_cnt):
279 rec = self._scc.read_record(EF['DIR'], i + 1)
280 if (rec[0][0:2], rec[0][4:6]) == ('61', '4f') and len(rec[0]) > 12 \
281 and rec[0][8:8 + int(rec[0][6:8], 16) * 2] not in self._aids:
282 self._aids.append(rec[0][8:8 + int(rec[0][6:8], 16) * 2])
283 except Exception as e:
284 print("Can't read AIDs from SIM -- %s" % (str(e),))
285 self._aids = []
286 return self._aids
Supreeth Herlee4e98312020-03-18 11:33:14 +0100287
Harald Weltec91085e2022-02-10 18:05:45 +0100288 @staticmethod
289 def _get_aid(adf="usim") -> str:
290 aid_map = {}
291 # First (known) halves of the U/ISIM AID
292 aid_map["usim"] = "a0000000871002"
293 aid_map["isim"] = "a0000000871004"
294 adf = adf.lower()
295 if adf in aid_map:
296 return aid_map[adf]
297 return None
Philipp Maier46c61542021-11-16 16:36:50 +0100298
Harald Weltec91085e2022-02-10 18:05:45 +0100299 def _complete_aid(self, aid) -> str:
300 """find the complete version of an ADF.U/ISIM AID"""
301 # Find full AID by partial AID:
302 if is_hex(aid):
303 for aid_known in self._aids:
304 if len(aid_known) >= len(aid) and aid == aid_known[0:len(aid)]:
305 return aid_known
306 return None
Philipp Maier46c61542021-11-16 16:36:50 +0100307
Harald Weltec91085e2022-02-10 18:05:45 +0100308 def select_adf_by_aid(self, adf="usim"):
309 """Select ADF.U/ISIM in the Card using its full AID"""
310 if is_hex(adf):
311 aid = adf
312 else:
313 aid = self._get_aid(adf)
314 if aid:
315 aid_full = self._complete_aid(aid)
316 if aid_full:
317 return self._scc.select_adf(aid_full)
318 else:
319 # If we cannot get the full AID, try with short AID
320 return self._scc.select_adf(aid)
321 return (None, None)
Supreeth Herlef9f3e5e2020-03-22 08:04:59 +0100322
Harald Weltec91085e2022-02-10 18:05:45 +0100323 def erase_binary(self, ef):
324 """Erase the contents of a file"""
325 len = self._scc.binary_size(ef)
326 self._scc.update_binary(ef, "ff" * len, offset=0, verify=True)
Philipp Maier5c2cc662020-05-12 16:27:12 +0200327
Harald Weltec91085e2022-02-10 18:05:45 +0100328 def erase_record(self, ef, rec_no):
329 """Erase the contents of a single record"""
330 len = self._scc.record_size(ef)
331 self._scc.update_record(ef, rec_no, "ff" * len,
332 force_len=False, verify=True)
Philipp Maier5c2cc662020-05-12 16:27:12 +0200333
Harald Weltec91085e2022-02-10 18:05:45 +0100334 def set_apdu_parameter(self, cla, sel_ctrl):
335 """Set apdu parameters (class byte and selection control bytes)"""
336 self._scc.cla_byte = cla
337 self._scc.sel_ctrl = sel_ctrl
Philipp Maier30b225f2021-10-29 16:41:46 +0200338
Harald Weltec91085e2022-02-10 18:05:45 +0100339 def get_apdu_parameter(self):
340 """Get apdu parameters (class byte and selection control bytes)"""
341 return (self._scc.cla_byte, self._scc.sel_ctrl)
342
Philipp Maier30b225f2021-10-29 16:41:46 +0200343
Philipp Maierbb73e512021-05-05 16:14:00 +0200344class UsimCard(SimCard):
Philipp Maierfc5f28d2021-05-05 12:18:41 +0200345
Harald Weltec91085e2022-02-10 18:05:45 +0100346 name = 'USIM'
Philipp Maierfc5f28d2021-05-05 12:18:41 +0200347
Harald Weltec91085e2022-02-10 18:05:45 +0100348 def __init__(self, ssc):
349 super(UsimCard, self).__init__(ssc)
Harald Welteca673942020-06-03 15:19:40 +0200350
Philipp Maierbda52832022-06-14 16:18:12 +0200351 # See also: ETSI TS 102 221, Table 9.3
352 self._adm_chv_num = 0xA0
353
Harald Weltec91085e2022-02-10 18:05:45 +0100354 def read_ehplmn(self):
355 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['EHPLMN'])
356 if sw == '9000':
357 return (format_xplmn(res), sw)
358 else:
359 return (None, sw)
Harald Welteca673942020-06-03 15:19:40 +0200360
Harald Weltec91085e2022-02-10 18:05:45 +0100361 def update_ehplmn(self, mcc, mnc):
362 data = self._scc.read_binary(
363 EF_USIM_ADF_map['EHPLMN'], length=None, offset=0)
364 size = len(data[0]) // 2
365 ehplmn = enc_plmn(mcc, mnc)
366 data, sw = self._scc.update_binary(EF_USIM_ADF_map['EHPLMN'], ehplmn)
367 return sw
Harald Welteca673942020-06-03 15:19:40 +0200368
Harald Weltec91085e2022-02-10 18:05:45 +0100369 def read_epdgid(self):
370 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['ePDGId'])
371 if sw == '9000':
372 try:
373 addr, addr_type = dec_addr_tlv(res)
374 except:
375 addr = None
376 addr_type = None
377 return (format_addr(addr, addr_type), sw)
378 else:
379 return (None, sw)
herlesupreethf8232db2020-09-29 10:03:06 +0200380
Harald Weltec91085e2022-02-10 18:05:45 +0100381 def update_epdgid(self, epdgid):
382 size = self._scc.binary_size(EF_USIM_ADF_map['ePDGId']) * 2
383 if len(epdgid) > 0:
384 addr_type = get_addr_type(epdgid)
385 if addr_type == None:
386 raise ValueError(
387 "Unknown ePDG Id address type or invalid address provided")
388 epdgid_tlv = rpad(enc_addr_tlv(epdgid, ('%02x' % addr_type)), size)
389 else:
390 epdgid_tlv = rpad('ff', size)
391 data, sw = self._scc.update_binary(
392 EF_USIM_ADF_map['ePDGId'], epdgid_tlv)
393 return sw
Harald Welteca673942020-06-03 15:19:40 +0200394
Harald Weltec91085e2022-02-10 18:05:45 +0100395 def read_ePDGSelection(self):
396 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['ePDGSelection'])
397 if sw == '9000':
398 return (format_ePDGSelection(res), sw)
399 else:
400 return (None, sw)
Supreeth Herle99d55552020-03-24 13:03:43 +0100401
Harald Weltec91085e2022-02-10 18:05:45 +0100402 def update_ePDGSelection(self, mcc, mnc):
403 (res, sw) = self._scc.read_binary(
404 EF_USIM_ADF_map['ePDGSelection'], length=None, offset=0)
405 if sw == '9000' and (len(mcc) == 0 or len(mnc) == 0):
406 # Reset contents
407 # 80 - Tag value
408 (res, sw) = self._scc.update_binary(
409 EF_USIM_ADF_map['ePDGSelection'], rpad('', len(res)))
410 elif sw == '9000':
411 (res, sw) = self._scc.update_binary(
412 EF_USIM_ADF_map['ePDGSelection'], enc_ePDGSelection(res, mcc, mnc))
413 return sw
Supreeth Herlef964df42020-03-24 13:15:37 +0100414
Harald Weltec91085e2022-02-10 18:05:45 +0100415 def read_ust(self):
416 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
417 if sw == '9000':
418 # Print those which are available
419 return ([res, dec_st(res, table="usim")], sw)
420 else:
421 return ([None, None], sw)
herlesupreeth4a3580b2020-09-29 10:11:36 +0200422
Harald Weltec91085e2022-02-10 18:05:45 +0100423 def update_ust(self, service, bit=1):
424 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
425 if sw == '9000':
426 content = enc_st(res, service, bit)
427 (res, sw) = self._scc.update_binary(
428 EF_USIM_ADF_map['UST'], content)
429 return sw
430
Harald Welte6ca2fa72022-02-12 16:29:31 +0100431 def update_est(self, service, bit=1):
432 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['EST'])
433 if sw == '9000':
434 content = enc_st(res, service, bit)
435 (res, sw) = self._scc.update_binary(
436 EF_USIM_ADF_map['EST'], content)
437 return sw
438
439
Supreeth Herleacc222f2020-03-24 13:26:53 +0100440
Philipp Maierbb73e512021-05-05 16:14:00 +0200441class IsimCard(SimCard):
Philipp Maierfc5f28d2021-05-05 12:18:41 +0200442
Harald Weltec91085e2022-02-10 18:05:45 +0100443 name = 'ISIM'
Philipp Maierfc5f28d2021-05-05 12:18:41 +0200444
Harald Weltec91085e2022-02-10 18:05:45 +0100445 def __init__(self, ssc):
446 super(IsimCard, self).__init__(ssc)
herlesupreethecbada92020-12-23 09:24:29 +0100447
Harald Weltec91085e2022-02-10 18:05:45 +0100448 def read_pcscf(self):
449 rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['PCSCF'])
450 pcscf_recs = ""
451 for i in range(0, rec_cnt):
452 (res, sw) = self._scc.read_record(EF_ISIM_ADF_map['PCSCF'], i + 1)
453 if sw == '9000':
454 try:
455 addr, addr_type = dec_addr_tlv(res)
456 except:
457 addr = None
458 addr_type = None
459 content = format_addr(addr, addr_type)
460 pcscf_recs += "%s" % (len(content)
461 and content or '\tNot available\n')
462 else:
463 pcscf_recs += "\tP-CSCF: Can't read, response code = %s\n" % (
464 sw)
465 return pcscf_recs
Supreeth Herle5ad9aec2020-03-24 17:26:40 +0100466
Harald Weltec91085e2022-02-10 18:05:45 +0100467 def update_pcscf(self, pcscf):
468 if len(pcscf) > 0:
469 addr_type = get_addr_type(pcscf)
470 if addr_type == None:
471 raise ValueError(
472 "Unknown PCSCF address type or invalid address provided")
473 content = enc_addr_tlv(pcscf, ('%02x' % addr_type))
474 else:
475 # Just the tag value
476 content = '80'
477 rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['PCSCF'])
478 pcscf_tlv = rpad(content, rec_size_bytes*2)
479 data, sw = self._scc.update_record(
480 EF_ISIM_ADF_map['PCSCF'], 1, pcscf_tlv)
481 return sw
Supreeth Herlecf727f22020-03-24 17:32:21 +0100482
Harald Weltec91085e2022-02-10 18:05:45 +0100483 def read_domain(self):
484 (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['DOMAIN'])
485 if sw == '9000':
486 # Skip the initial tag value ('80') byte and get length of contents
487 length = int(res[2:4], 16)
488 content = h2s(res[4:4+(length*2)])
489 return (content, sw)
490 else:
491 return (None, sw)
Supreeth Herle05b28072020-03-25 10:23:48 +0100492
Harald Weltec91085e2022-02-10 18:05:45 +0100493 def update_domain(self, domain=None, mcc=None, mnc=None):
494 hex_str = ""
495 if domain:
496 hex_str = s2h(domain)
497 elif mcc and mnc:
498 # MCC and MNC always has 3 digits in domain form
499 plmn_str = 'mnc' + lpad(mnc, 3, "0") + '.mcc' + lpad(mcc, 3, "0")
500 hex_str = s2h('ims.' + plmn_str + '.3gppnetwork.org')
Supreeth Herle79f43dd2020-03-25 11:43:19 +0100501
Harald Weltec91085e2022-02-10 18:05:45 +0100502 # Build TLV
503 tlv = TLV(['80'])
504 content = tlv.build({'80': hex_str})
Supreeth Herle79f43dd2020-03-25 11:43:19 +0100505
Harald Weltec91085e2022-02-10 18:05:45 +0100506 bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['DOMAIN'])
507 data, sw = self._scc.update_binary(
508 EF_ISIM_ADF_map['DOMAIN'], rpad(content, bin_size_bytes*2))
509 return sw
Supreeth Herle79f43dd2020-03-25 11:43:19 +0100510
Harald Weltec91085e2022-02-10 18:05:45 +0100511 def read_impi(self):
512 (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IMPI'])
513 if sw == '9000':
514 # Skip the initial tag value ('80') byte and get length of contents
515 length = int(res[2:4], 16)
516 content = h2s(res[4:4+(length*2)])
517 return (content, sw)
518 else:
519 return (None, sw)
Supreeth Herle3f67f9c2020-03-25 15:38:02 +0100520
Harald Weltec91085e2022-02-10 18:05:45 +0100521 def update_impi(self, impi=None):
522 hex_str = ""
523 if impi:
524 hex_str = s2h(impi)
525 # Build TLV
526 tlv = TLV(['80'])
527 content = tlv.build({'80': hex_str})
Supreeth Herlea5bd9682020-03-26 09:16:14 +0100528
Harald Weltec91085e2022-02-10 18:05:45 +0100529 bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['IMPI'])
530 data, sw = self._scc.update_binary(
531 EF_ISIM_ADF_map['IMPI'], rpad(content, bin_size_bytes*2))
532 return sw
Supreeth Herlea5bd9682020-03-26 09:16:14 +0100533
Harald Weltec91085e2022-02-10 18:05:45 +0100534 def read_impu(self):
535 rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['IMPU'])
536 impu_recs = ""
537 for i in range(0, rec_cnt):
538 (res, sw) = self._scc.read_record(EF_ISIM_ADF_map['IMPU'], i + 1)
539 if sw == '9000':
540 # Skip the initial tag value ('80') byte and get length of contents
541 length = int(res[2:4], 16)
542 content = h2s(res[4:4+(length*2)])
543 impu_recs += "\t%s\n" % (len(content)
544 and content or 'Not available')
545 else:
546 impu_recs += "IMS public user identity: Can't read, response code = %s\n" % (
547 sw)
548 return impu_recs
Supreeth Herle0c02d8a2020-03-26 09:00:06 +0100549
Harald Weltec91085e2022-02-10 18:05:45 +0100550 def update_impu(self, impu=None):
551 hex_str = ""
552 if impu:
553 hex_str = s2h(impu)
554 # Build TLV
555 tlv = TLV(['80'])
556 content = tlv.build({'80': hex_str})
Supreeth Herlebe7007e2020-03-26 09:27:45 +0100557
Harald Weltec91085e2022-02-10 18:05:45 +0100558 rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['IMPU'])
559 impu_tlv = rpad(content, rec_size_bytes*2)
560 data, sw = self._scc.update_record(
561 EF_ISIM_ADF_map['IMPU'], 1, impu_tlv)
562 return sw
Supreeth Herlebe7007e2020-03-26 09:27:45 +0100563
Harald Weltec91085e2022-02-10 18:05:45 +0100564 def read_iari(self):
565 rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['UICCIARI'])
566 uiari_recs = ""
567 for i in range(0, rec_cnt):
568 (res, sw) = self._scc.read_record(
569 EF_ISIM_ADF_map['UICCIARI'], i + 1)
570 if sw == '9000':
571 # Skip the initial tag value ('80') byte and get length of contents
572 length = int(res[2:4], 16)
573 content = h2s(res[4:4+(length*2)])
574 uiari_recs += "\t%s\n" % (len(content)
575 and content or 'Not available')
576 else:
577 uiari_recs += "UICC IARI: Can't read, response code = %s\n" % (
578 sw)
579 return uiari_recs
580
Harald Welte6ca2fa72022-02-12 16:29:31 +0100581 def update_ist(self, service, bit=1):
582 (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IST'])
583 if sw == '9000':
584 content = enc_st(res, service, bit)
585 (res, sw) = self._scc.update_binary(
586 EF_ISIM_ADF_map['IST'], content)
587 return sw
588
Sylvain Munaut76504e02010-12-07 00:24:32 +0100589
Philipp Maierbb73e512021-05-05 16:14:00 +0200590class MagicSimBase(abc.ABC, SimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100591 """
592 Theses cards uses several record based EFs to store the provider infos,
593 each possible provider uses a specific record number in each EF. The
594 indexes used are ( where N is the number of providers supported ) :
595 - [2 .. N+1] for the operator name
596 - [1 .. N] for the programmable EFs
Sylvain Munaut76504e02010-12-07 00:24:32 +0100597
Harald Weltec91085e2022-02-10 18:05:45 +0100598 * 3f00/7f4d/8f0c : Operator Name
Sylvain Munaut76504e02010-12-07 00:24:32 +0100599
Harald Weltec91085e2022-02-10 18:05:45 +0100600 bytes 0-15 : provider name, padded with 0xff
601 byte 16 : length of the provider name
602 byte 17 : 01 for valid records, 00 otherwise
Sylvain Munaut76504e02010-12-07 00:24:32 +0100603
Harald Weltec91085e2022-02-10 18:05:45 +0100604 * 3f00/7f4d/8f0d : Programmable Binary EFs
Sylvain Munaut76504e02010-12-07 00:24:32 +0100605
Harald Weltec91085e2022-02-10 18:05:45 +0100606 * 3f00/7f4d/8f0e : Programmable Record EFs
Sylvain Munaut76504e02010-12-07 00:24:32 +0100607
Harald Weltec91085e2022-02-10 18:05:45 +0100608 """
Sylvain Munaut76504e02010-12-07 00:24:32 +0100609
Harald Weltec91085e2022-02-10 18:05:45 +0100610 _files = {} # type: Dict[str, Tuple[str, int, bool]]
611 _ki_file = None # type: Optional[str]
Vadim Yanitskiy03c67f72021-05-02 02:10:39 +0200612
Harald Weltec91085e2022-02-10 18:05:45 +0100613 @classmethod
614 def autodetect(kls, scc):
615 try:
616 for p, l, t in kls._files.values():
617 if not t:
618 continue
619 if scc.record_size(['3f00', '7f4d', p]) != l:
620 return None
621 except:
622 return None
Sylvain Munaut76504e02010-12-07 00:24:32 +0100623
Harald Weltec91085e2022-02-10 18:05:45 +0100624 return kls(scc)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100625
Harald Weltec91085e2022-02-10 18:05:45 +0100626 def _get_count(self):
627 """
628 Selects the file and returns the total number of entries
629 and entry size
630 """
631 f = self._files['name']
Sylvain Munaut76504e02010-12-07 00:24:32 +0100632
Harald Weltec91085e2022-02-10 18:05:45 +0100633 r = self._scc.select_path(['3f00', '7f4d', f[0]])
634 rec_len = int(r[-1][28:30], 16)
635 tlen = int(r[-1][4:8], 16)
636 rec_cnt = (tlen // rec_len) - 1
Sylvain Munaut76504e02010-12-07 00:24:32 +0100637
Harald Weltec91085e2022-02-10 18:05:45 +0100638 if (rec_cnt < 1) or (rec_len != f[1]):
639 raise RuntimeError('Bad card type')
Sylvain Munaut76504e02010-12-07 00:24:32 +0100640
Harald Weltec91085e2022-02-10 18:05:45 +0100641 return rec_cnt
Sylvain Munaut76504e02010-12-07 00:24:32 +0100642
Harald Weltec91085e2022-02-10 18:05:45 +0100643 def program(self, p):
644 # Go to dir
645 self._scc.select_path(['3f00', '7f4d'])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100646
Harald Weltec91085e2022-02-10 18:05:45 +0100647 # Home PLMN in PLMN_Sel format
648 hplmn = enc_plmn(p['mcc'], p['mnc'])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100649
Harald Weltec91085e2022-02-10 18:05:45 +0100650 # Operator name ( 3f00/7f4d/8f0c )
651 self._scc.update_record(self._files['name'][0], 2,
652 rpad(b2h(p['name']), 32) + ('%02x' %
653 len(p['name'])) + '01'
654 )
Sylvain Munaut76504e02010-12-07 00:24:32 +0100655
Harald Weltec91085e2022-02-10 18:05:45 +0100656 # ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d )
657 v = ''
Sylvain Munaut76504e02010-12-07 00:24:32 +0100658
Harald Weltec91085e2022-02-10 18:05:45 +0100659 # inline Ki
660 if self._ki_file is None:
661 v += p['ki']
Sylvain Munaut76504e02010-12-07 00:24:32 +0100662
Harald Weltec91085e2022-02-10 18:05:45 +0100663 # ICCID
664 v += '3f00' + '2fe2' + '0a' + enc_iccid(p['iccid'])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100665
Harald Weltec91085e2022-02-10 18:05:45 +0100666 # IMSI
667 v += '7f20' + '6f07' + '09' + enc_imsi(p['imsi'])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100668
Harald Weltec91085e2022-02-10 18:05:45 +0100669 # Ki
670 if self._ki_file:
671 v += self._ki_file + '10' + p['ki']
Sylvain Munaut76504e02010-12-07 00:24:32 +0100672
Harald Weltec91085e2022-02-10 18:05:45 +0100673 # PLMN_Sel
674 v += '6f30' + '18' + rpad(hplmn, 36)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100675
Harald Weltec91085e2022-02-10 18:05:45 +0100676 # ACC
677 # This doesn't work with "fake" SuperSIM cards,
678 # but will hopefully work with real SuperSIMs.
679 if p.get('acc') is not None:
680 v += '6f78' + '02' + lpad(p['acc'], 4)
Alexander Chemeris21885242013-07-02 16:56:55 +0400681
Harald Weltec91085e2022-02-10 18:05:45 +0100682 self._scc.update_record(self._files['b_ef'][0], 1,
683 rpad(v, self._files['b_ef'][1]*2)
684 )
Sylvain Munaut76504e02010-12-07 00:24:32 +0100685
Harald Weltec91085e2022-02-10 18:05:45 +0100686 # SMSP ( 3f00/7f4d/8f0e )
687 # FIXME
Sylvain Munaut76504e02010-12-07 00:24:32 +0100688
Harald Weltec91085e2022-02-10 18:05:45 +0100689 # Write PLMN_Sel forcefully as well
690 r = self._scc.select_path(['3f00', '7f20', '6f30'])
691 tl = int(r[-1][4:8], 16)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100692
Harald Weltec91085e2022-02-10 18:05:45 +0100693 hplmn = enc_plmn(p['mcc'], p['mnc'])
694 self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100695
Harald Weltec91085e2022-02-10 18:05:45 +0100696 def erase(self):
697 # Dummy
698 df = {}
699 for k, v in self._files.items():
700 ofs = 1
701 fv = v[1] * 'ff'
702 if k == 'name':
703 ofs = 2
704 fv = fv[0:-4] + '0000'
705 df[v[0]] = (fv, ofs)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100706
Harald Weltec91085e2022-02-10 18:05:45 +0100707 # Write
708 for n in range(0, self._get_count()):
709 for k, (msg, ofs) in df.items():
710 self._scc.update_record(['3f00', '7f4d', k], n + ofs, msg)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100711
712
Vadim Yanitskiy85302d62021-05-02 02:18:42 +0200713class SuperSim(MagicSimBase):
Sylvain Munaut76504e02010-12-07 00:24:32 +0100714
Harald Weltec91085e2022-02-10 18:05:45 +0100715 name = 'supersim'
Sylvain Munaut76504e02010-12-07 00:24:32 +0100716
Harald Weltec91085e2022-02-10 18:05:45 +0100717 _files = {
718 'name': ('8f0c', 18, True),
719 'b_ef': ('8f0d', 74, True),
720 'r_ef': ('8f0e', 50, True),
721 }
Sylvain Munaut76504e02010-12-07 00:24:32 +0100722
Harald Weltec91085e2022-02-10 18:05:45 +0100723 _ki_file = None
Sylvain Munaut76504e02010-12-07 00:24:32 +0100724
725
Vadim Yanitskiy85302d62021-05-02 02:18:42 +0200726class MagicSim(MagicSimBase):
Sylvain Munaut76504e02010-12-07 00:24:32 +0100727
Harald Weltec91085e2022-02-10 18:05:45 +0100728 name = 'magicsim'
Sylvain Munaut76504e02010-12-07 00:24:32 +0100729
Harald Weltec91085e2022-02-10 18:05:45 +0100730 _files = {
731 'name': ('8f0c', 18, True),
732 'b_ef': ('8f0d', 130, True),
733 'r_ef': ('8f0e', 102, False),
734 }
Sylvain Munaut76504e02010-12-07 00:24:32 +0100735
Harald Weltec91085e2022-02-10 18:05:45 +0100736 _ki_file = '6f1b'
Sylvain Munaut76504e02010-12-07 00:24:32 +0100737
738
Philipp Maierbb73e512021-05-05 16:14:00 +0200739class FakeMagicSim(SimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100740 """
741 Theses cards have a record based EF 3f00/000c that contains the provider
742 information. See the program method for its format. The records go from
743 1 to N.
744 """
Sylvain Munaut76504e02010-12-07 00:24:32 +0100745
Harald Weltec91085e2022-02-10 18:05:45 +0100746 name = 'fakemagicsim'
Sylvain Munaut76504e02010-12-07 00:24:32 +0100747
Harald Weltec91085e2022-02-10 18:05:45 +0100748 @classmethod
749 def autodetect(kls, scc):
750 try:
751 if scc.record_size(['3f00', '000c']) != 0x5a:
752 return None
753 except:
754 return None
Sylvain Munaut76504e02010-12-07 00:24:32 +0100755
Harald Weltec91085e2022-02-10 18:05:45 +0100756 return kls(scc)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100757
Harald Weltec91085e2022-02-10 18:05:45 +0100758 def _get_infos(self):
759 """
760 Selects the file and returns the total number of entries
761 and entry size
762 """
Sylvain Munaut76504e02010-12-07 00:24:32 +0100763
Harald Weltec91085e2022-02-10 18:05:45 +0100764 r = self._scc.select_path(['3f00', '000c'])
765 rec_len = int(r[-1][28:30], 16)
766 tlen = int(r[-1][4:8], 16)
767 rec_cnt = (tlen // rec_len) - 1
Sylvain Munaut76504e02010-12-07 00:24:32 +0100768
Harald Weltec91085e2022-02-10 18:05:45 +0100769 if (rec_cnt < 1) or (rec_len != 0x5a):
770 raise RuntimeError('Bad card type')
Sylvain Munaut76504e02010-12-07 00:24:32 +0100771
Harald Weltec91085e2022-02-10 18:05:45 +0100772 return rec_cnt, rec_len
Sylvain Munaut76504e02010-12-07 00:24:32 +0100773
Harald Weltec91085e2022-02-10 18:05:45 +0100774 def program(self, p):
775 # Home PLMN
776 r = self._scc.select_path(['3f00', '7f20', '6f30'])
777 tl = int(r[-1][4:8], 16)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100778
Harald Weltec91085e2022-02-10 18:05:45 +0100779 hplmn = enc_plmn(p['mcc'], p['mnc'])
780 self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100781
Harald Weltec91085e2022-02-10 18:05:45 +0100782 # Get total number of entries and entry size
783 rec_cnt, rec_len = self._get_infos()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100784
Harald Weltec91085e2022-02-10 18:05:45 +0100785 # Set first entry
786 entry = (
787 '81' + # 1b Status: Valid & Active
788 rpad(s2h(p['name'][0:14]), 28) + # 14b Entry Name
789 enc_iccid(p['iccid']) + # 10b ICCID
790 enc_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
791 p['ki'] + # 16b Ki
792 lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed)
793 )
794 self._scc.update_record('000c', 1, entry)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100795
Harald Weltec91085e2022-02-10 18:05:45 +0100796 def erase(self):
797 # Get total number of entries and entry size
798 rec_cnt, rec_len = self._get_infos()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100799
Harald Weltec91085e2022-02-10 18:05:45 +0100800 # Erase all entries
801 entry = 'ff' * rec_len
802 for i in range(0, rec_cnt):
803 self._scc.update_record('000c', 1+i, entry)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100804
Sylvain Munaut5da8d4e2013-07-02 15:13:24 +0200805
Philipp Maierbb73e512021-05-05 16:14:00 +0200806class GrcardSim(SimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100807 """
808 Greencard (grcard.cn) HZCOS GSM SIM
809 These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
810 and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
811 """
Harald Welte3156d902011-03-22 21:48:19 +0100812
Harald Weltec91085e2022-02-10 18:05:45 +0100813 name = 'grcardsim'
Harald Welte3156d902011-03-22 21:48:19 +0100814
Harald Weltec91085e2022-02-10 18:05:45 +0100815 @classmethod
816 def autodetect(kls, scc):
817 return None
Harald Welte3156d902011-03-22 21:48:19 +0100818
Harald Weltec91085e2022-02-10 18:05:45 +0100819 def program(self, p):
820 # We don't really know yet what ADM PIN 4 is about
821 #self._scc.verify_chv(4, h2b("4444444444444444"))
Harald Welte3156d902011-03-22 21:48:19 +0100822
Harald Weltec91085e2022-02-10 18:05:45 +0100823 # Authenticate using ADM PIN 5
824 if p['pin_adm']:
825 pin = h2b(p['pin_adm'])
826 else:
827 pin = h2b("4444444444444444")
828 self._scc.verify_chv(5, pin)
Harald Welte3156d902011-03-22 21:48:19 +0100829
Harald Weltec91085e2022-02-10 18:05:45 +0100830 # EF.ICCID
831 r = self._scc.select_path(['3f00', '2fe2'])
832 data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
Harald Welte3156d902011-03-22 21:48:19 +0100833
Harald Weltec91085e2022-02-10 18:05:45 +0100834 # EF.IMSI
835 r = self._scc.select_path(['3f00', '7f20', '6f07'])
836 data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
Harald Welte3156d902011-03-22 21:48:19 +0100837
Harald Weltec91085e2022-02-10 18:05:45 +0100838 # EF.ACC
839 if p.get('acc') is not None:
840 data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
Harald Welte3156d902011-03-22 21:48:19 +0100841
Harald Weltec91085e2022-02-10 18:05:45 +0100842 # EF.SMSP
843 if p.get('smsp'):
844 r = self._scc.select_path(['3f00', '7f10', '6f42'])
845 data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
Harald Welte3156d902011-03-22 21:48:19 +0100846
Harald Weltec91085e2022-02-10 18:05:45 +0100847 # Set the Ki using proprietary command
848 pdu = '80d4020010' + p['ki']
849 data, sw = self._scc._tp.send_apdu(pdu)
Harald Welte3156d902011-03-22 21:48:19 +0100850
Harald Weltec91085e2022-02-10 18:05:45 +0100851 # EF.HPLMN
852 r = self._scc.select_path(['3f00', '7f20', '6f30'])
853 size = int(r[-1][4:8], 16)
854 hplmn = enc_plmn(p['mcc'], p['mnc'])
855 self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
Harald Welte3156d902011-03-22 21:48:19 +0100856
Harald Weltec91085e2022-02-10 18:05:45 +0100857 # EF.SPN (Service Provider Name)
858 r = self._scc.select_path(['3f00', '7f20', '6f30'])
859 size = int(r[-1][4:8], 16)
860 # FIXME
Harald Welte3156d902011-03-22 21:48:19 +0100861
Harald Weltec91085e2022-02-10 18:05:45 +0100862 # FIXME: EF.MSISDN
Harald Welte3156d902011-03-22 21:48:19 +0100863
Sylvain Munaut76504e02010-12-07 00:24:32 +0100864
Harald Weltee10394b2011-12-07 12:34:14 +0100865class SysmoSIMgr1(GrcardSim):
Harald Weltec91085e2022-02-10 18:05:45 +0100866 """
867 sysmocom sysmoSIM-GR1
868 These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
869 and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
870 """
871 name = 'sysmosim-gr1'
Harald Weltee10394b2011-12-07 12:34:14 +0100872
Harald Weltec91085e2022-02-10 18:05:45 +0100873 @classmethod
874 def autodetect(kls, scc):
875 try:
876 # Look for ATR
877 if scc.get_atr() == toBytes("3B 99 18 00 11 88 22 33 44 55 66 77 60"):
878 return kls(scc)
879 except:
880 return None
881 return None
882
Sylvain Munaut5da8d4e2013-07-02 15:13:24 +0200883
Harald Welteca673942020-06-03 15:19:40 +0200884class SysmoUSIMgr1(UsimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100885 """
886 sysmocom sysmoUSIM-GR1
887 """
888 name = 'sysmoUSIM-GR1'
Holger Hans Peter Freyther4d91bf42012-03-22 14:28:38 +0100889
Harald Weltec91085e2022-02-10 18:05:45 +0100890 @classmethod
891 def autodetect(kls, scc):
892 # TODO: Access the ATR
893 return None
Holger Hans Peter Freyther4d91bf42012-03-22 14:28:38 +0100894
Harald Weltec91085e2022-02-10 18:05:45 +0100895 def program(self, p):
896 # TODO: check if verify_chv could be used or what it needs
897 # self._scc.verify_chv(0x0A, [0x33,0x32,0x32,0x31,0x33,0x32,0x33,0x32])
898 # Unlock the card..
899 data, sw = self._scc._tp.send_apdu_checksw(
900 "0020000A083332323133323332")
Holger Hans Peter Freyther4d91bf42012-03-22 14:28:38 +0100901
Harald Weltec91085e2022-02-10 18:05:45 +0100902 # TODO: move into SimCardCommands
903 par = (p['ki'] + # 16b K
904 p['opc'] + # 32b OPC
905 enc_iccid(p['iccid']) + # 10b ICCID
906 enc_imsi(p['imsi']) # 9b IMSI_len + id_type(9) + IMSI
907 )
908 data, sw = self._scc._tp.send_apdu_checksw("0099000033" + par)
Holger Hans Peter Freyther4d91bf42012-03-22 14:28:38 +0100909
Sylvain Munaut053c8952013-07-02 15:12:32 +0200910
Philipp Maierbb73e512021-05-05 16:14:00 +0200911class SysmoSIMgr2(SimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100912 """
913 sysmocom sysmoSIM-GR2
914 """
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100915
Harald Weltec91085e2022-02-10 18:05:45 +0100916 name = 'sysmoSIM-GR2'
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100917
Harald Weltec91085e2022-02-10 18:05:45 +0100918 @classmethod
919 def autodetect(kls, scc):
920 try:
921 # Look for ATR
922 if scc.get_atr() == toBytes("3B 7D 94 00 00 55 55 53 0A 74 86 93 0B 24 7C 4D 54 68"):
923 return kls(scc)
924 except:
925 return None
926 return None
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100927
Harald Weltec91085e2022-02-10 18:05:45 +0100928 def program(self, p):
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100929
Harald Weltec91085e2022-02-10 18:05:45 +0100930 # select MF
931 r = self._scc.select_path(['3f00'])
Daniel Willmann5d8cd9b2020-10-19 11:01:49 +0200932
Harald Weltec91085e2022-02-10 18:05:45 +0100933 # authenticate as SUPER ADM using default key
934 self._scc.verify_chv(0x0b, h2b("3838383838383838"))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100935
Harald Weltec91085e2022-02-10 18:05:45 +0100936 # set ADM pin using proprietary command
937 # INS: D4
938 # P1: 3A for PIN, 3B for PUK
939 # P2: CHV number, as in VERIFY CHV for PIN, and as in UNBLOCK CHV for PUK
940 # P3: 08, CHV length (curiously the PUK is also 08 length, instead of 10)
941 if p['pin_adm']:
942 pin = h2b(p['pin_adm'])
943 else:
944 pin = h2b("4444444444444444")
Jan Balkec3ebd332015-01-26 12:22:55 +0100945
Harald Weltec91085e2022-02-10 18:05:45 +0100946 pdu = 'A0D43A0508' + b2h(pin)
947 data, sw = self._scc._tp.send_apdu(pdu)
Daniel Willmann5d8cd9b2020-10-19 11:01:49 +0200948
Harald Weltec91085e2022-02-10 18:05:45 +0100949 # authenticate as ADM (enough to write file, and can set PINs)
Jan Balkec3ebd332015-01-26 12:22:55 +0100950
Harald Weltec91085e2022-02-10 18:05:45 +0100951 self._scc.verify_chv(0x05, pin)
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100952
Harald Weltec91085e2022-02-10 18:05:45 +0100953 # write EF.ICCID
954 data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100955
Harald Weltec91085e2022-02-10 18:05:45 +0100956 # select DF_GSM
957 r = self._scc.select_path(['7f20'])
Daniel Willmann5d8cd9b2020-10-19 11:01:49 +0200958
Harald Weltec91085e2022-02-10 18:05:45 +0100959 # write EF.IMSI
960 data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100961
Harald Weltec91085e2022-02-10 18:05:45 +0100962 # write EF.ACC
963 if p.get('acc') is not None:
964 data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100965
Harald Weltec91085e2022-02-10 18:05:45 +0100966 # get size and write EF.HPLMN
967 r = self._scc.select_path(['6f30'])
968 size = int(r[-1][4:8], 16)
969 hplmn = enc_plmn(p['mcc'], p['mnc'])
970 self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100971
Harald Weltec91085e2022-02-10 18:05:45 +0100972 # set COMP128 version 0 in proprietary file
973 data, sw = self._scc.update_binary('0001', '001000')
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100974
Harald Weltec91085e2022-02-10 18:05:45 +0100975 # set Ki in proprietary file
976 data, sw = self._scc.update_binary('0001', p['ki'], 3)
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100977
Harald Weltec91085e2022-02-10 18:05:45 +0100978 # select DF_TELECOM
979 r = self._scc.select_path(['3f00', '7f10'])
Daniel Willmann5d8cd9b2020-10-19 11:01:49 +0200980
Harald Weltec91085e2022-02-10 18:05:45 +0100981 # write EF.SMSP
982 if p.get('smsp'):
983 data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100984
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100985
Harald Welteca673942020-06-03 15:19:40 +0200986class SysmoUSIMSJS1(UsimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100987 """
988 sysmocom sysmoUSIM-SJS1
989 """
Jan Balke3e840672015-01-26 15:36:27 +0100990
Harald Weltec91085e2022-02-10 18:05:45 +0100991 name = 'sysmoUSIM-SJS1'
Jan Balke3e840672015-01-26 15:36:27 +0100992
Harald Weltec91085e2022-02-10 18:05:45 +0100993 def __init__(self, ssc):
994 super(SysmoUSIMSJS1, self).__init__(ssc)
995 self._scc.cla_byte = "00"
996 self._scc.sel_ctrl = "0004" # request an FCP
Jan Balke3e840672015-01-26 15:36:27 +0100997
Harald Weltec91085e2022-02-10 18:05:45 +0100998 @classmethod
999 def autodetect(kls, scc):
1000 try:
1001 # Look for ATR
1002 if scc.get_atr() == toBytes("3B 9F 96 80 1F C7 80 31 A0 73 BE 21 13 67 43 20 07 18 00 00 01 A5"):
1003 return kls(scc)
1004 except:
1005 return None
1006 return None
Jan Balke3e840672015-01-26 15:36:27 +01001007
Harald Weltec91085e2022-02-10 18:05:45 +01001008 def verify_adm(self, key):
1009 # authenticate as ADM using default key (written on the card..)
1010 if not key:
1011 raise ValueError(
1012 "Please provide a PIN-ADM as there is no default one")
1013 (res, sw) = self._scc.verify_chv(0x0A, key)
1014 return sw
Harald Weltea6704252021-01-08 20:19:11 +01001015
Harald Weltec91085e2022-02-10 18:05:45 +01001016 def program(self, p):
1017 self.verify_adm(h2b(p['pin_adm']))
Jan Balke3e840672015-01-26 15:36:27 +01001018
Harald Weltec91085e2022-02-10 18:05:45 +01001019 # select MF
1020 r = self._scc.select_path(['3f00'])
Jan Balke3e840672015-01-26 15:36:27 +01001021
Harald Weltec91085e2022-02-10 18:05:45 +01001022 # write EF.ICCID
1023 data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
Philipp Maiere9604882017-03-21 17:24:31 +01001024
Harald Weltec91085e2022-02-10 18:05:45 +01001025 # select DF_GSM
1026 r = self._scc.select_path(['7f20'])
Jan Balke3e840672015-01-26 15:36:27 +01001027
Harald Weltec91085e2022-02-10 18:05:45 +01001028 # set Ki in proprietary file
1029 data, sw = self._scc.update_binary('00FF', p['ki'])
Jan Balke3e840672015-01-26 15:36:27 +01001030
Harald Weltec91085e2022-02-10 18:05:45 +01001031 # set OPc in proprietary file
1032 if 'opc' in p:
1033 content = "01" + p['opc']
1034 data, sw = self._scc.update_binary('00F7', content)
Jan Balke3e840672015-01-26 15:36:27 +01001035
Harald Weltec91085e2022-02-10 18:05:45 +01001036 # set Service Provider Name
1037 if p.get('name') is not None:
1038 self.update_spn(p['name'], True, True)
Supreeth Herle7947d922019-06-08 07:50:53 +02001039
Harald Weltec91085e2022-02-10 18:05:45 +01001040 if p.get('acc') is not None:
1041 self.update_acc(p['acc'])
Supreeth Herlec8796a32019-12-23 12:23:42 +01001042
Harald Weltec91085e2022-02-10 18:05:45 +01001043 # write EF.IMSI
1044 data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
Jan Balke3e840672015-01-26 15:36:27 +01001045
Harald Weltec91085e2022-02-10 18:05:45 +01001046 # EF.PLMNsel
1047 if p.get('mcc') and p.get('mnc'):
1048 sw = self.update_plmnsel(p['mcc'], p['mnc'])
1049 if sw != '9000':
1050 print("Programming PLMNsel failed with code %s" % sw)
Philipp Maier2d15ea02019-03-20 12:40:36 +01001051
Harald Weltec91085e2022-02-10 18:05:45 +01001052 # EF.PLMNwAcT
1053 if p.get('mcc') and p.get('mnc'):
1054 sw = self.update_plmn_act(p['mcc'], p['mnc'])
1055 if sw != '9000':
1056 print("Programming PLMNwAcT failed with code %s" % sw)
Philipp Maier2d15ea02019-03-20 12:40:36 +01001057
Harald Weltec91085e2022-02-10 18:05:45 +01001058 # EF.OPLMNwAcT
1059 if p.get('mcc') and p.get('mnc'):
1060 sw = self.update_oplmn_act(p['mcc'], p['mnc'])
1061 if sw != '9000':
1062 print("Programming OPLMNwAcT failed with code %s" % sw)
Philipp Maier2d15ea02019-03-20 12:40:36 +01001063
Harald Weltec91085e2022-02-10 18:05:45 +01001064 # EF.HPLMNwAcT
1065 if p.get('mcc') and p.get('mnc'):
1066 sw = self.update_hplmn_act(p['mcc'], p['mnc'])
1067 if sw != '9000':
1068 print("Programming HPLMNwAcT failed with code %s" % sw)
Supreeth Herlef442fb42020-01-21 12:47:32 +01001069
Harald Weltec91085e2022-02-10 18:05:45 +01001070 # EF.AD
1071 if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
1072 if p.get('mcc') and p.get('mnc'):
1073 mnc = p['mnc']
1074 else:
1075 mnc = None
1076 sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
1077 if sw != '9000':
1078 print("Programming AD failed with code %s" % sw)
Philipp Maier2d15ea02019-03-20 12:40:36 +01001079
Harald Weltec91085e2022-02-10 18:05:45 +01001080 # EF.SMSP
1081 if p.get('smsp'):
1082 r = self._scc.select_path(['3f00', '7f10'])
1083 data, sw = self._scc.update_record(
1084 '6f42', 1, lpad(p['smsp'], 104), force_len=True)
Jan Balke3e840672015-01-26 15:36:27 +01001085
Harald Weltec91085e2022-02-10 18:05:45 +01001086 # EF.MSISDN
1087 # TODO: Alpha Identifier (currently 'ff'O * 20)
1088 # TODO: Capability/Configuration1 Record Identifier
1089 # TODO: Extension1 Record Identifier
1090 if p.get('msisdn') is not None:
1091 msisdn = enc_msisdn(p['msisdn'])
1092 data = 'ff' * 20 + msisdn
Supreeth Herle5a541012019-12-22 08:59:16 +01001093
Harald Weltec91085e2022-02-10 18:05:45 +01001094 r = self._scc.select_path(['3f00', '7f10'])
1095 data, sw = self._scc.update_record('6F40', 1, data, force_len=True)
Supreeth Herle5a541012019-12-22 08:59:16 +01001096
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001097
herlesupreeth4a3580b2020-09-29 10:11:36 +02001098class FairwavesSIM(UsimCard):
Harald Weltec91085e2022-02-10 18:05:45 +01001099 """
1100 FairwavesSIM
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001101
Harald Weltec91085e2022-02-10 18:05:45 +01001102 The SIM card is operating according to the standard.
1103 For Ki/OP/OPC programming the following files are additionally open for writing:
1104 3F00/7F20/FF01 – OP/OPC:
1105 byte 1 = 0x01, bytes 2-17: OPC;
1106 byte 1 = 0x00, bytes 2-17: OP;
1107 3F00/7F20/FF02: Ki
1108 """
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001109
Harald Weltec91085e2022-02-10 18:05:45 +01001110 name = 'Fairwaves-SIM'
1111 # Propriatary files
1112 _EF_num = {
1113 'Ki': 'FF02',
1114 'OP/OPC': 'FF01',
1115 }
1116 _EF = {
1117 'Ki': DF['GSM']+[_EF_num['Ki']],
1118 'OP/OPC': DF['GSM']+[_EF_num['OP/OPC']],
1119 }
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001120
Harald Weltec91085e2022-02-10 18:05:45 +01001121 def __init__(self, ssc):
1122 super(FairwavesSIM, self).__init__(ssc)
1123 self._adm_chv_num = 0x11
1124 self._adm2_chv_num = 0x12
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001125
Harald Weltec91085e2022-02-10 18:05:45 +01001126 @classmethod
1127 def autodetect(kls, scc):
1128 try:
1129 # Look for ATR
1130 if scc.get_atr() == toBytes("3B 9F 96 80 1F C7 80 31 A0 73 BE 21 13 67 44 22 06 10 00 00 01 A9"):
1131 return kls(scc)
1132 except:
1133 return None
1134 return None
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001135
Harald Weltec91085e2022-02-10 18:05:45 +01001136 def verify_adm2(self, key):
1137 '''
1138 Authenticate with ADM2 key.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001139
Harald Weltec91085e2022-02-10 18:05:45 +01001140 Fairwaves SIM cards support hierarchical key structure and ADM2 key
1141 is a key which has access to proprietary files (Ki and OP/OPC).
1142 That said, ADM key inherits permissions of ADM2 key and thus we rarely
1143 need ADM2 key per se.
1144 '''
1145 (res, sw) = self._scc.verify_chv(self._adm2_chv_num, key)
1146 return sw
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001147
Harald Weltec91085e2022-02-10 18:05:45 +01001148 def read_ki(self):
1149 """
1150 Read Ki in proprietary file.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001151
Harald Weltec91085e2022-02-10 18:05:45 +01001152 Requires ADM1 access level
1153 """
1154 return self._scc.read_binary(self._EF['Ki'])
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001155
Harald Weltec91085e2022-02-10 18:05:45 +01001156 def update_ki(self, ki):
1157 """
1158 Set Ki in proprietary file.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001159
Harald Weltec91085e2022-02-10 18:05:45 +01001160 Requires ADM1 access level
1161 """
1162 data, sw = self._scc.update_binary(self._EF['Ki'], ki)
1163 return sw
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001164
Harald Weltec91085e2022-02-10 18:05:45 +01001165 def read_op_opc(self):
1166 """
1167 Read Ki in proprietary file.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001168
Harald Weltec91085e2022-02-10 18:05:45 +01001169 Requires ADM1 access level
1170 """
1171 (ef, sw) = self._scc.read_binary(self._EF['OP/OPC'])
1172 type = 'OP' if ef[0:2] == '00' else 'OPC'
1173 return ((type, ef[2:]), sw)
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001174
Harald Weltec91085e2022-02-10 18:05:45 +01001175 def update_op(self, op):
1176 """
1177 Set OP in proprietary file.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001178
Harald Weltec91085e2022-02-10 18:05:45 +01001179 Requires ADM1 access level
1180 """
1181 content = '00' + op
1182 data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
1183 return sw
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001184
Harald Weltec91085e2022-02-10 18:05:45 +01001185 def update_opc(self, opc):
1186 """
1187 Set OPC in proprietary file.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001188
Harald Weltec91085e2022-02-10 18:05:45 +01001189 Requires ADM1 access level
1190 """
1191 content = '01' + opc
1192 data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
1193 return sw
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001194
Harald Weltec91085e2022-02-10 18:05:45 +01001195 def program(self, p):
1196 # For some reason the card programming only works when the card
1197 # is handled as a classic SIM, even though it is an USIM, so we
1198 # reconfigure the class byte and the select control field on
1199 # the fly. When the programming is done the original values are
1200 # restored.
1201 cla_byte_orig = self._scc.cla_byte
1202 sel_ctrl_orig = self._scc.sel_ctrl
1203 self._scc.cla_byte = "a0"
1204 self._scc.sel_ctrl = "0000"
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001205
Harald Weltec91085e2022-02-10 18:05:45 +01001206 try:
1207 self._program(p)
1208 finally:
1209 # restore original cla byte and sel ctrl
1210 self._scc.cla_byte = cla_byte_orig
1211 self._scc.sel_ctrl = sel_ctrl_orig
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001212
Harald Weltec91085e2022-02-10 18:05:45 +01001213 def _program(self, p):
1214 # authenticate as ADM1
1215 if not p['pin_adm']:
1216 raise ValueError(
1217 "Please provide a PIN-ADM as there is no default one")
1218 self.verify_adm(h2b(p['pin_adm']))
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001219
Harald Weltec91085e2022-02-10 18:05:45 +01001220 # TODO: Set operator name
1221 if p.get('smsp') is not None:
1222 sw = self.update_smsp(p['smsp'])
1223 if sw != '9000':
1224 print("Programming SMSP failed with code %s" % sw)
1225 # This SIM doesn't support changing ICCID
1226 if p.get('mcc') is not None and p.get('mnc') is not None:
1227 sw = self.update_hplmn_act(p['mcc'], p['mnc'])
1228 if sw != '9000':
1229 print("Programming MCC/MNC failed with code %s" % sw)
1230 if p.get('imsi') is not None:
1231 sw = self.update_imsi(p['imsi'])
1232 if sw != '9000':
1233 print("Programming IMSI failed with code %s" % sw)
1234 if p.get('ki') is not None:
1235 sw = self.update_ki(p['ki'])
1236 if sw != '9000':
1237 print("Programming Ki failed with code %s" % sw)
1238 if p.get('opc') is not None:
1239 sw = self.update_opc(p['opc'])
1240 if sw != '9000':
1241 print("Programming OPC failed with code %s" % sw)
1242 if p.get('acc') is not None:
1243 sw = self.update_acc(p['acc'])
1244 if sw != '9000':
1245 print("Programming ACC failed with code %s" % sw)
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001246
1247
Philipp Maierbb73e512021-05-05 16:14:00 +02001248class OpenCellsSim(SimCard):
Harald Weltec91085e2022-02-10 18:05:45 +01001249 """
1250 OpenCellsSim
Todd Neal9eeadfc2018-04-25 15:36:29 -05001251
Harald Weltec91085e2022-02-10 18:05:45 +01001252 """
Todd Neal9eeadfc2018-04-25 15:36:29 -05001253
Harald Weltec91085e2022-02-10 18:05:45 +01001254 name = 'OpenCells-SIM'
Todd Neal9eeadfc2018-04-25 15:36:29 -05001255
Harald Weltec91085e2022-02-10 18:05:45 +01001256 def __init__(self, ssc):
1257 super(OpenCellsSim, self).__init__(ssc)
1258 self._adm_chv_num = 0x0A
Todd Neal9eeadfc2018-04-25 15:36:29 -05001259
Harald Weltec91085e2022-02-10 18:05:45 +01001260 @classmethod
1261 def autodetect(kls, scc):
1262 try:
1263 # Look for ATR
1264 if scc.get_atr() == toBytes("3B 9F 95 80 1F C3 80 31 E0 73 FE 21 13 57 86 81 02 86 98 44 18 A8"):
1265 return kls(scc)
1266 except:
1267 return None
1268 return None
Todd Neal9eeadfc2018-04-25 15:36:29 -05001269
Harald Weltec91085e2022-02-10 18:05:45 +01001270 def program(self, p):
1271 if not p['pin_adm']:
1272 raise ValueError(
1273 "Please provide a PIN-ADM as there is no default one")
1274 self._scc.verify_chv(0x0A, h2b(p['pin_adm']))
Todd Neal9eeadfc2018-04-25 15:36:29 -05001275
Harald Weltec91085e2022-02-10 18:05:45 +01001276 # select MF
1277 r = self._scc.select_path(['3f00'])
Todd Neal9eeadfc2018-04-25 15:36:29 -05001278
Harald Weltec91085e2022-02-10 18:05:45 +01001279 # write EF.ICCID
1280 data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
Todd Neal9eeadfc2018-04-25 15:36:29 -05001281
Harald Weltec91085e2022-02-10 18:05:45 +01001282 r = self._scc.select_path(['7ff0'])
Todd Neal9eeadfc2018-04-25 15:36:29 -05001283
Harald Weltec91085e2022-02-10 18:05:45 +01001284 # set Ki in proprietary file
1285 data, sw = self._scc.update_binary('FF02', p['ki'])
Todd Neal9eeadfc2018-04-25 15:36:29 -05001286
Harald Weltec91085e2022-02-10 18:05:45 +01001287 # set OPC in proprietary file
1288 data, sw = self._scc.update_binary('FF01', p['opc'])
Todd Neal9eeadfc2018-04-25 15:36:29 -05001289
Harald Weltec91085e2022-02-10 18:05:45 +01001290 # select DF_GSM
1291 r = self._scc.select_path(['7f20'])
Todd Neal9eeadfc2018-04-25 15:36:29 -05001292
Harald Weltec91085e2022-02-10 18:05:45 +01001293 # write EF.IMSI
1294 data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
Todd Neal9eeadfc2018-04-25 15:36:29 -05001295
Todd Neal9eeadfc2018-04-25 15:36:29 -05001296
herlesupreeth4a3580b2020-09-29 10:11:36 +02001297class WavemobileSim(UsimCard):
Harald Weltec91085e2022-02-10 18:05:45 +01001298 """
1299 WavemobileSim
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001300
Harald Weltec91085e2022-02-10 18:05:45 +01001301 """
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001302
Harald Weltec91085e2022-02-10 18:05:45 +01001303 name = 'Wavemobile-SIM'
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001304
Harald Weltec91085e2022-02-10 18:05:45 +01001305 def __init__(self, ssc):
1306 super(WavemobileSim, self).__init__(ssc)
1307 self._adm_chv_num = 0x0A
1308 self._scc.cla_byte = "00"
1309 self._scc.sel_ctrl = "0004" # request an FCP
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001310
Harald Weltec91085e2022-02-10 18:05:45 +01001311 @classmethod
1312 def autodetect(kls, scc):
1313 try:
1314 # Look for ATR
1315 if scc.get_atr() == toBytes("3B 9F 95 80 1F C7 80 31 E0 73 F6 21 13 67 4D 45 16 00 43 01 00 8F"):
1316 return kls(scc)
1317 except:
1318 return None
1319 return None
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001320
Harald Weltec91085e2022-02-10 18:05:45 +01001321 def program(self, p):
1322 if not p['pin_adm']:
1323 raise ValueError(
1324 "Please provide a PIN-ADM as there is no default one")
1325 self.verify_adm(h2b(p['pin_adm']))
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001326
Harald Weltec91085e2022-02-10 18:05:45 +01001327 # EF.ICCID
1328 # TODO: Add programming of the ICCID
1329 if p.get('iccid'):
1330 print(
1331 "Warning: Programming of the ICCID is not implemented for this type of card.")
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001332
Harald Weltec91085e2022-02-10 18:05:45 +01001333 # KI (Presumably a propritary file)
1334 # TODO: Add programming of KI
1335 if p.get('ki'):
1336 print(
1337 "Warning: Programming of the KI is not implemented for this type of card.")
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001338
Harald Weltec91085e2022-02-10 18:05:45 +01001339 # OPc (Presumably a propritary file)
1340 # TODO: Add programming of OPc
1341 if p.get('opc'):
1342 print(
1343 "Warning: Programming of the OPc is not implemented for this type of card.")
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001344
Harald Weltec91085e2022-02-10 18:05:45 +01001345 # EF.SMSP
1346 if p.get('smsp'):
1347 sw = self.update_smsp(p['smsp'])
1348 if sw != '9000':
1349 print("Programming SMSP failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001350
Harald Weltec91085e2022-02-10 18:05:45 +01001351 # EF.IMSI
1352 if p.get('imsi'):
1353 sw = self.update_imsi(p['imsi'])
1354 if sw != '9000':
1355 print("Programming IMSI failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001356
Harald Weltec91085e2022-02-10 18:05:45 +01001357 # EF.ACC
1358 if p.get('acc'):
1359 sw = self.update_acc(p['acc'])
1360 if sw != '9000':
1361 print("Programming ACC failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001362
Harald Weltec91085e2022-02-10 18:05:45 +01001363 # EF.PLMNsel
1364 if p.get('mcc') and p.get('mnc'):
1365 sw = self.update_plmnsel(p['mcc'], p['mnc'])
1366 if sw != '9000':
1367 print("Programming PLMNsel failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001368
Harald Weltec91085e2022-02-10 18:05:45 +01001369 # EF.PLMNwAcT
1370 if p.get('mcc') and p.get('mnc'):
1371 sw = self.update_plmn_act(p['mcc'], p['mnc'])
1372 if sw != '9000':
1373 print("Programming PLMNwAcT failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001374
Harald Weltec91085e2022-02-10 18:05:45 +01001375 # EF.OPLMNwAcT
1376 if p.get('mcc') and p.get('mnc'):
1377 sw = self.update_oplmn_act(p['mcc'], p['mnc'])
1378 if sw != '9000':
1379 print("Programming OPLMNwAcT failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001380
Harald Weltec91085e2022-02-10 18:05:45 +01001381 # EF.AD
1382 if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
1383 if p.get('mcc') and p.get('mnc'):
1384 mnc = p['mnc']
1385 else:
1386 mnc = None
1387 sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
1388 if sw != '9000':
1389 print("Programming AD failed with code %s" % sw)
Philipp Maier6e507a72019-04-01 16:33:48 +02001390
Harald Weltec91085e2022-02-10 18:05:45 +01001391 return None
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001392
Todd Neal9eeadfc2018-04-25 15:36:29 -05001393
herlesupreethb0c7d122020-12-23 09:25:46 +01001394class SysmoISIMSJA2(UsimCard, IsimCard):
Harald Weltec91085e2022-02-10 18:05:45 +01001395 """
1396 sysmocom sysmoISIM-SJA2
1397 """
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001398
Harald Weltec91085e2022-02-10 18:05:45 +01001399 name = 'sysmoISIM-SJA2'
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001400
Harald Weltec91085e2022-02-10 18:05:45 +01001401 def __init__(self, ssc):
1402 super(SysmoISIMSJA2, self).__init__(ssc)
1403 self._scc.cla_byte = "00"
1404 self._scc.sel_ctrl = "0004" # request an FCP
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001405
Harald Weltec91085e2022-02-10 18:05:45 +01001406 @classmethod
1407 def autodetect(kls, scc):
1408 try:
1409 # Try card model #1
1410 atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 4B A9"
1411 if scc.get_atr() == toBytes(atr):
1412 return kls(scc)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001413
Harald Weltec91085e2022-02-10 18:05:45 +01001414 # Try card model #2
1415 atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 31 33 02 51 B2"
1416 if scc.get_atr() == toBytes(atr):
1417 return kls(scc)
Philipp Maierb3e11ea2020-03-11 12:32:44 +01001418
Harald Weltec91085e2022-02-10 18:05:45 +01001419 # Try card model #3
1420 atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 52 75 31 04 51 D5"
1421 if scc.get_atr() == toBytes(atr):
1422 return kls(scc)
1423 except:
1424 return None
1425 return None
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001426
Harald Weltec91085e2022-02-10 18:05:45 +01001427 def verify_adm(self, key):
1428 # authenticate as ADM using default key (written on the card..)
1429 if not key:
1430 raise ValueError(
1431 "Please provide a PIN-ADM as there is no default one")
1432 (res, sw) = self._scc.verify_chv(0x0A, key)
1433 return sw
Harald Weltea6704252021-01-08 20:19:11 +01001434
Harald Weltec91085e2022-02-10 18:05:45 +01001435 def program(self, p):
1436 self.verify_adm(h2b(p['pin_adm']))
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001437
Harald Weltec91085e2022-02-10 18:05:45 +01001438 # This type of card does not allow to reprogram the ICCID.
1439 # Reprogramming the ICCID would mess up the card os software
1440 # license management, so the ICCID must be kept at its factory
1441 # setting!
1442 if p.get('iccid'):
1443 print(
1444 "Warning: Programming of the ICCID is not implemented for this type of card.")
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001445
Harald Weltec91085e2022-02-10 18:05:45 +01001446 # select DF_GSM
1447 self._scc.select_path(['7f20'])
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001448
Harald Weltec91085e2022-02-10 18:05:45 +01001449 # set Service Provider Name
1450 if p.get('name') is not None:
1451 self.update_spn(p['name'], True, True)
Robert Falkenberg54595362021-04-06 12:04:34 +02001452
Harald Weltec91085e2022-02-10 18:05:45 +01001453 # write EF.IMSI
1454 if p.get('imsi'):
1455 self._scc.update_binary('6f07', enc_imsi(p['imsi']))
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001456
Harald Weltec91085e2022-02-10 18:05:45 +01001457 # EF.PLMNsel
1458 if p.get('mcc') and p.get('mnc'):
1459 sw = self.update_plmnsel(p['mcc'], p['mnc'])
1460 if sw != '9000':
1461 print("Programming PLMNsel failed with code %s" % sw)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001462
Harald Weltec91085e2022-02-10 18:05:45 +01001463 # EF.PLMNwAcT
1464 if p.get('mcc') and p.get('mnc'):
1465 sw = self.update_plmn_act(p['mcc'], p['mnc'])
1466 if sw != '9000':
1467 print("Programming PLMNwAcT failed with code %s" % sw)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001468
Harald Weltec91085e2022-02-10 18:05:45 +01001469 # EF.OPLMNwAcT
1470 if p.get('mcc') and p.get('mnc'):
1471 sw = self.update_oplmn_act(p['mcc'], p['mnc'])
1472 if sw != '9000':
1473 print("Programming OPLMNwAcT failed with code %s" % sw)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001474
Harald Weltec91085e2022-02-10 18:05:45 +01001475 # EF.HPLMNwAcT
1476 if p.get('mcc') and p.get('mnc'):
1477 sw = self.update_hplmn_act(p['mcc'], p['mnc'])
1478 if sw != '9000':
1479 print("Programming HPLMNwAcT failed with code %s" % sw)
Harald Welte32f0d412020-05-05 17:35:57 +02001480
Harald Weltec91085e2022-02-10 18:05:45 +01001481 # EF.AD
1482 if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
1483 if p.get('mcc') and p.get('mnc'):
1484 mnc = p['mnc']
1485 else:
1486 mnc = None
1487 sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
1488 if sw != '9000':
1489 print("Programming AD failed with code %s" % sw)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001490
Harald Weltec91085e2022-02-10 18:05:45 +01001491 # EF.SMSP
1492 if p.get('smsp'):
1493 r = self._scc.select_path(['3f00', '7f10'])
1494 data, sw = self._scc.update_record(
1495 '6f42', 1, lpad(p['smsp'], 104), force_len=True)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001496
Harald Weltec91085e2022-02-10 18:05:45 +01001497 # EF.MSISDN
1498 # TODO: Alpha Identifier (currently 'ff'O * 20)
1499 # TODO: Capability/Configuration1 Record Identifier
1500 # TODO: Extension1 Record Identifier
1501 if p.get('msisdn') is not None:
1502 msisdn = enc_msisdn(p['msisdn'])
1503 content = 'ff' * 20 + msisdn
Supreeth Herlec6019232020-03-26 10:00:45 +01001504
Harald Weltec91085e2022-02-10 18:05:45 +01001505 r = self._scc.select_path(['3f00', '7f10'])
1506 data, sw = self._scc.update_record(
1507 '6F40', 1, content, force_len=True)
Supreeth Herlec6019232020-03-26 10:00:45 +01001508
Harald Weltec91085e2022-02-10 18:05:45 +01001509 # EF.ACC
1510 if p.get('acc'):
1511 sw = self.update_acc(p['acc'])
1512 if sw != '9000':
1513 print("Programming ACC failed with code %s" % sw)
Supreeth Herlea97944b2020-03-26 10:03:25 +01001514
Harald Weltec91085e2022-02-10 18:05:45 +01001515 # Populate AIDs
1516 self.read_aids()
Supreeth Herle80164052020-03-23 12:06:29 +01001517
Harald Weltec91085e2022-02-10 18:05:45 +01001518 # update EF-SIM_AUTH_KEY (and EF-USIM_AUTH_KEY_2G, which is
1519 # hard linked to EF-USIM_AUTH_KEY)
1520 self._scc.select_path(['3f00'])
1521 self._scc.select_path(['a515'])
1522 if p.get('ki'):
1523 self._scc.update_binary('6f20', p['ki'], 1)
1524 if p.get('opc'):
1525 self._scc.update_binary('6f20', p['opc'], 17)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001526
Harald Weltec91085e2022-02-10 18:05:45 +01001527 # update EF-USIM_AUTH_KEY in ADF.ISIM
1528 data, sw = self.select_adf_by_aid(adf="isim")
1529 if sw == '9000':
1530 if p.get('ki'):
1531 self._scc.update_binary('af20', p['ki'], 1)
1532 if p.get('opc'):
1533 self._scc.update_binary('af20', p['opc'], 17)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001534
Harald Weltec91085e2022-02-10 18:05:45 +01001535 # update EF.P-CSCF in ADF.ISIM
1536 if self.file_exists(EF_ISIM_ADF_map['PCSCF']):
1537 if p.get('pcscf'):
1538 sw = self.update_pcscf(p['pcscf'])
1539 else:
1540 sw = self.update_pcscf("")
1541 if sw != '9000':
1542 print("Programming P-CSCF failed with code %s" % sw)
Supreeth Herlecf727f22020-03-24 17:32:21 +01001543
Harald Weltec91085e2022-02-10 18:05:45 +01001544 # update EF.DOMAIN in ADF.ISIM
1545 if self.file_exists(EF_ISIM_ADF_map['DOMAIN']):
1546 if p.get('ims_hdomain'):
1547 sw = self.update_domain(domain=p['ims_hdomain'])
1548 else:
1549 sw = self.update_domain()
Supreeth Herlecf727f22020-03-24 17:32:21 +01001550
Harald Weltec91085e2022-02-10 18:05:45 +01001551 if sw != '9000':
1552 print(
1553 "Programming Home Network Domain Name failed with code %s" % sw)
Supreeth Herle79f43dd2020-03-25 11:43:19 +01001554
Harald Weltec91085e2022-02-10 18:05:45 +01001555 # update EF.IMPI in ADF.ISIM
1556 # TODO: Validate IMPI input
1557 if self.file_exists(EF_ISIM_ADF_map['IMPI']):
1558 if p.get('impi'):
1559 sw = self.update_impi(p['impi'])
1560 else:
1561 sw = self.update_impi()
1562 if sw != '9000':
1563 print("Programming IMPI failed with code %s" % sw)
Supreeth Herle79f43dd2020-03-25 11:43:19 +01001564
Harald Weltec91085e2022-02-10 18:05:45 +01001565 # update EF.IMPU in ADF.ISIM
1566 # TODO: Validate IMPU input
1567 # Support multiple IMPU if there is enough space
1568 if self.file_exists(EF_ISIM_ADF_map['IMPU']):
1569 if p.get('impu'):
1570 sw = self.update_impu(p['impu'])
1571 else:
1572 sw = self.update_impu()
1573 if sw != '9000':
1574 print("Programming IMPU failed with code %s" % sw)
Supreeth Herlea5bd9682020-03-26 09:16:14 +01001575
Harald Weltec91085e2022-02-10 18:05:45 +01001576 data, sw = self.select_adf_by_aid(adf="usim")
1577 if sw == '9000':
1578 # update EF-USIM_AUTH_KEY in ADF.USIM
1579 if p.get('ki'):
1580 self._scc.update_binary('af20', p['ki'], 1)
1581 if p.get('opc'):
1582 self._scc.update_binary('af20', p['opc'], 17)
Supreeth Herlebe7007e2020-03-26 09:27:45 +01001583
Harald Weltec91085e2022-02-10 18:05:45 +01001584 # update EF.EHPLMN in ADF.USIM
1585 if self.file_exists(EF_USIM_ADF_map['EHPLMN']):
1586 if p.get('mcc') and p.get('mnc'):
1587 sw = self.update_ehplmn(p['mcc'], p['mnc'])
1588 if sw != '9000':
1589 print("Programming EHPLMN failed with code %s" % sw)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001590
Harald Weltec91085e2022-02-10 18:05:45 +01001591 # update EF.ePDGId in ADF.USIM
1592 if self.file_exists(EF_USIM_ADF_map['ePDGId']):
1593 if p.get('epdgid'):
1594 sw = self.update_epdgid(p['epdgid'])
1595 else:
1596 sw = self.update_epdgid("")
1597 if sw != '9000':
1598 print("Programming ePDGId failed with code %s" % sw)
Supreeth Herle8e0fccd2020-03-23 12:10:56 +01001599
Harald Weltec91085e2022-02-10 18:05:45 +01001600 # update EF.ePDGSelection in ADF.USIM
1601 if self.file_exists(EF_USIM_ADF_map['ePDGSelection']):
1602 if p.get('epdgSelection'):
1603 epdg_plmn = p['epdgSelection']
1604 sw = self.update_ePDGSelection(
1605 epdg_plmn[:3], epdg_plmn[3:])
1606 else:
1607 sw = self.update_ePDGSelection("", "")
1608 if sw != '9000':
1609 print("Programming ePDGSelection failed with code %s" % sw)
Supreeth Herle8e0fccd2020-03-23 12:10:56 +01001610
Harald Weltec91085e2022-02-10 18:05:45 +01001611 # After successfully programming EF.ePDGId and EF.ePDGSelection,
1612 # Set service 106 and 107 as available in EF.UST
1613 # Disable service 95, 99, 115 if ISIM application is present
1614 if self.file_exists(EF_USIM_ADF_map['UST']):
1615 if p.get('epdgSelection') and p.get('epdgid'):
1616 sw = self.update_ust(106, 1)
1617 if sw != '9000':
1618 print("Programming UST failed with code %s" % sw)
1619 sw = self.update_ust(107, 1)
1620 if sw != '9000':
1621 print("Programming UST failed with code %s" % sw)
Supreeth Herlef964df42020-03-24 13:15:37 +01001622
Harald Weltec91085e2022-02-10 18:05:45 +01001623 sw = self.update_ust(95, 0)
1624 if sw != '9000':
1625 print("Programming UST failed with code %s" % sw)
1626 sw = self.update_ust(99, 0)
1627 if sw != '9000':
1628 print("Programming UST failed with code %s" % sw)
1629 sw = self.update_ust(115, 0)
1630 if sw != '9000':
1631 print("Programming UST failed with code %s" % sw)
Supreeth Herlef964df42020-03-24 13:15:37 +01001632
Harald Weltec91085e2022-02-10 18:05:45 +01001633 return
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001634
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001635
Todd Neal9eeadfc2018-04-25 15:36:29 -05001636# In order for autodetection ...
Harald Weltec91085e2022-02-10 18:05:45 +01001637_cards_classes = [FakeMagicSim, SuperSim, MagicSim, GrcardSim,
1638 SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1,
1639 FairwavesSIM, OpenCellsSim, WavemobileSim, SysmoISIMSJA2]
1640
Alexander Chemeris8ad124a2018-01-10 14:17:55 +09001641
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001642def card_detect(ctype, scc):
Harald Weltec91085e2022-02-10 18:05:45 +01001643 # Detect type if needed
1644 card = None
1645 ctypes = dict([(kls.name, kls) for kls in _cards_classes])
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001646
Harald Weltec91085e2022-02-10 18:05:45 +01001647 if ctype == "auto":
1648 for kls in _cards_classes:
1649 card = kls.autodetect(scc)
1650 if card:
1651 print("Autodetected card type: %s" % card.name)
1652 card.reset()
1653 break
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001654
Harald Weltec91085e2022-02-10 18:05:45 +01001655 if card is None:
1656 print("Autodetection failed")
1657 return None
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001658
Harald Weltec91085e2022-02-10 18:05:45 +01001659 elif ctype in ctypes:
1660 card = ctypes[ctype](scc)
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001661
Harald Weltec91085e2022-02-10 18:05:45 +01001662 else:
1663 raise ValueError("Unknown card type: %s" % ctype)
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001664
Harald Weltec91085e2022-02-10 18:05:45 +01001665 return card