blob: b186044f0d8798a08b5cac364d90ed9cada29d9e [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
Philipp Maierbb73e512021-05-05 16:14:00 +020055class SimCard(object):
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
Harald Weltec91085e2022-02-10 18:05:45 +0100351 def read_ehplmn(self):
352 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['EHPLMN'])
353 if sw == '9000':
354 return (format_xplmn(res), sw)
355 else:
356 return (None, sw)
Harald Welteca673942020-06-03 15:19:40 +0200357
Harald Weltec91085e2022-02-10 18:05:45 +0100358 def update_ehplmn(self, mcc, mnc):
359 data = self._scc.read_binary(
360 EF_USIM_ADF_map['EHPLMN'], length=None, offset=0)
361 size = len(data[0]) // 2
362 ehplmn = enc_plmn(mcc, mnc)
363 data, sw = self._scc.update_binary(EF_USIM_ADF_map['EHPLMN'], ehplmn)
364 return sw
Harald Welteca673942020-06-03 15:19:40 +0200365
Harald Weltec91085e2022-02-10 18:05:45 +0100366 def read_epdgid(self):
367 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['ePDGId'])
368 if sw == '9000':
369 try:
370 addr, addr_type = dec_addr_tlv(res)
371 except:
372 addr = None
373 addr_type = None
374 return (format_addr(addr, addr_type), sw)
375 else:
376 return (None, sw)
herlesupreethf8232db2020-09-29 10:03:06 +0200377
Harald Weltec91085e2022-02-10 18:05:45 +0100378 def update_epdgid(self, epdgid):
379 size = self._scc.binary_size(EF_USIM_ADF_map['ePDGId']) * 2
380 if len(epdgid) > 0:
381 addr_type = get_addr_type(epdgid)
382 if addr_type == None:
383 raise ValueError(
384 "Unknown ePDG Id address type or invalid address provided")
385 epdgid_tlv = rpad(enc_addr_tlv(epdgid, ('%02x' % addr_type)), size)
386 else:
387 epdgid_tlv = rpad('ff', size)
388 data, sw = self._scc.update_binary(
389 EF_USIM_ADF_map['ePDGId'], epdgid_tlv)
390 return sw
Harald Welteca673942020-06-03 15:19:40 +0200391
Harald Weltec91085e2022-02-10 18:05:45 +0100392 def read_ePDGSelection(self):
393 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['ePDGSelection'])
394 if sw == '9000':
395 return (format_ePDGSelection(res), sw)
396 else:
397 return (None, sw)
Supreeth Herle99d55552020-03-24 13:03:43 +0100398
Harald Weltec91085e2022-02-10 18:05:45 +0100399 def update_ePDGSelection(self, mcc, mnc):
400 (res, sw) = self._scc.read_binary(
401 EF_USIM_ADF_map['ePDGSelection'], length=None, offset=0)
402 if sw == '9000' and (len(mcc) == 0 or len(mnc) == 0):
403 # Reset contents
404 # 80 - Tag value
405 (res, sw) = self._scc.update_binary(
406 EF_USIM_ADF_map['ePDGSelection'], rpad('', len(res)))
407 elif sw == '9000':
408 (res, sw) = self._scc.update_binary(
409 EF_USIM_ADF_map['ePDGSelection'], enc_ePDGSelection(res, mcc, mnc))
410 return sw
Supreeth Herlef964df42020-03-24 13:15:37 +0100411
Harald Weltec91085e2022-02-10 18:05:45 +0100412 def read_ust(self):
413 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
414 if sw == '9000':
415 # Print those which are available
416 return ([res, dec_st(res, table="usim")], sw)
417 else:
418 return ([None, None], sw)
herlesupreeth4a3580b2020-09-29 10:11:36 +0200419
Harald Weltec91085e2022-02-10 18:05:45 +0100420 def update_ust(self, service, bit=1):
421 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['UST'])
422 if sw == '9000':
423 content = enc_st(res, service, bit)
424 (res, sw) = self._scc.update_binary(
425 EF_USIM_ADF_map['UST'], content)
426 return sw
427
Harald Welte6ca2fa72022-02-12 16:29:31 +0100428 def update_est(self, service, bit=1):
429 (res, sw) = self._scc.read_binary(EF_USIM_ADF_map['EST'])
430 if sw == '9000':
431 content = enc_st(res, service, bit)
432 (res, sw) = self._scc.update_binary(
433 EF_USIM_ADF_map['EST'], content)
434 return sw
435
436
Supreeth Herleacc222f2020-03-24 13:26:53 +0100437
Philipp Maierbb73e512021-05-05 16:14:00 +0200438class IsimCard(SimCard):
Philipp Maierfc5f28d2021-05-05 12:18:41 +0200439
Harald Weltec91085e2022-02-10 18:05:45 +0100440 name = 'ISIM'
Philipp Maierfc5f28d2021-05-05 12:18:41 +0200441
Harald Weltec91085e2022-02-10 18:05:45 +0100442 def __init__(self, ssc):
443 super(IsimCard, self).__init__(ssc)
herlesupreethecbada92020-12-23 09:24:29 +0100444
Harald Weltec91085e2022-02-10 18:05:45 +0100445 def read_pcscf(self):
446 rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['PCSCF'])
447 pcscf_recs = ""
448 for i in range(0, rec_cnt):
449 (res, sw) = self._scc.read_record(EF_ISIM_ADF_map['PCSCF'], i + 1)
450 if sw == '9000':
451 try:
452 addr, addr_type = dec_addr_tlv(res)
453 except:
454 addr = None
455 addr_type = None
456 content = format_addr(addr, addr_type)
457 pcscf_recs += "%s" % (len(content)
458 and content or '\tNot available\n')
459 else:
460 pcscf_recs += "\tP-CSCF: Can't read, response code = %s\n" % (
461 sw)
462 return pcscf_recs
Supreeth Herle5ad9aec2020-03-24 17:26:40 +0100463
Harald Weltec91085e2022-02-10 18:05:45 +0100464 def update_pcscf(self, pcscf):
465 if len(pcscf) > 0:
466 addr_type = get_addr_type(pcscf)
467 if addr_type == None:
468 raise ValueError(
469 "Unknown PCSCF address type or invalid address provided")
470 content = enc_addr_tlv(pcscf, ('%02x' % addr_type))
471 else:
472 # Just the tag value
473 content = '80'
474 rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['PCSCF'])
475 pcscf_tlv = rpad(content, rec_size_bytes*2)
476 data, sw = self._scc.update_record(
477 EF_ISIM_ADF_map['PCSCF'], 1, pcscf_tlv)
478 return sw
Supreeth Herlecf727f22020-03-24 17:32:21 +0100479
Harald Weltec91085e2022-02-10 18:05:45 +0100480 def read_domain(self):
481 (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['DOMAIN'])
482 if sw == '9000':
483 # Skip the initial tag value ('80') byte and get length of contents
484 length = int(res[2:4], 16)
485 content = h2s(res[4:4+(length*2)])
486 return (content, sw)
487 else:
488 return (None, sw)
Supreeth Herle05b28072020-03-25 10:23:48 +0100489
Harald Weltec91085e2022-02-10 18:05:45 +0100490 def update_domain(self, domain=None, mcc=None, mnc=None):
491 hex_str = ""
492 if domain:
493 hex_str = s2h(domain)
494 elif mcc and mnc:
495 # MCC and MNC always has 3 digits in domain form
496 plmn_str = 'mnc' + lpad(mnc, 3, "0") + '.mcc' + lpad(mcc, 3, "0")
497 hex_str = s2h('ims.' + plmn_str + '.3gppnetwork.org')
Supreeth Herle79f43dd2020-03-25 11:43:19 +0100498
Harald Weltec91085e2022-02-10 18:05:45 +0100499 # Build TLV
500 tlv = TLV(['80'])
501 content = tlv.build({'80': hex_str})
Supreeth Herle79f43dd2020-03-25 11:43:19 +0100502
Harald Weltec91085e2022-02-10 18:05:45 +0100503 bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['DOMAIN'])
504 data, sw = self._scc.update_binary(
505 EF_ISIM_ADF_map['DOMAIN'], rpad(content, bin_size_bytes*2))
506 return sw
Supreeth Herle79f43dd2020-03-25 11:43:19 +0100507
Harald Weltec91085e2022-02-10 18:05:45 +0100508 def read_impi(self):
509 (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IMPI'])
510 if sw == '9000':
511 # Skip the initial tag value ('80') byte and get length of contents
512 length = int(res[2:4], 16)
513 content = h2s(res[4:4+(length*2)])
514 return (content, sw)
515 else:
516 return (None, sw)
Supreeth Herle3f67f9c2020-03-25 15:38:02 +0100517
Harald Weltec91085e2022-02-10 18:05:45 +0100518 def update_impi(self, impi=None):
519 hex_str = ""
520 if impi:
521 hex_str = s2h(impi)
522 # Build TLV
523 tlv = TLV(['80'])
524 content = tlv.build({'80': hex_str})
Supreeth Herlea5bd9682020-03-26 09:16:14 +0100525
Harald Weltec91085e2022-02-10 18:05:45 +0100526 bin_size_bytes = self._scc.binary_size(EF_ISIM_ADF_map['IMPI'])
527 data, sw = self._scc.update_binary(
528 EF_ISIM_ADF_map['IMPI'], rpad(content, bin_size_bytes*2))
529 return sw
Supreeth Herlea5bd9682020-03-26 09:16:14 +0100530
Harald Weltec91085e2022-02-10 18:05:45 +0100531 def read_impu(self):
532 rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['IMPU'])
533 impu_recs = ""
534 for i in range(0, rec_cnt):
535 (res, sw) = self._scc.read_record(EF_ISIM_ADF_map['IMPU'], i + 1)
536 if sw == '9000':
537 # Skip the initial tag value ('80') byte and get length of contents
538 length = int(res[2:4], 16)
539 content = h2s(res[4:4+(length*2)])
540 impu_recs += "\t%s\n" % (len(content)
541 and content or 'Not available')
542 else:
543 impu_recs += "IMS public user identity: Can't read, response code = %s\n" % (
544 sw)
545 return impu_recs
Supreeth Herle0c02d8a2020-03-26 09:00:06 +0100546
Harald Weltec91085e2022-02-10 18:05:45 +0100547 def update_impu(self, impu=None):
548 hex_str = ""
549 if impu:
550 hex_str = s2h(impu)
551 # Build TLV
552 tlv = TLV(['80'])
553 content = tlv.build({'80': hex_str})
Supreeth Herlebe7007e2020-03-26 09:27:45 +0100554
Harald Weltec91085e2022-02-10 18:05:45 +0100555 rec_size_bytes = self._scc.record_size(EF_ISIM_ADF_map['IMPU'])
556 impu_tlv = rpad(content, rec_size_bytes*2)
557 data, sw = self._scc.update_record(
558 EF_ISIM_ADF_map['IMPU'], 1, impu_tlv)
559 return sw
Supreeth Herlebe7007e2020-03-26 09:27:45 +0100560
Harald Weltec91085e2022-02-10 18:05:45 +0100561 def read_iari(self):
562 rec_cnt = self._scc.record_count(EF_ISIM_ADF_map['UICCIARI'])
563 uiari_recs = ""
564 for i in range(0, rec_cnt):
565 (res, sw) = self._scc.read_record(
566 EF_ISIM_ADF_map['UICCIARI'], i + 1)
567 if sw == '9000':
568 # Skip the initial tag value ('80') byte and get length of contents
569 length = int(res[2:4], 16)
570 content = h2s(res[4:4+(length*2)])
571 uiari_recs += "\t%s\n" % (len(content)
572 and content or 'Not available')
573 else:
574 uiari_recs += "UICC IARI: Can't read, response code = %s\n" % (
575 sw)
576 return uiari_recs
577
Harald Welte6ca2fa72022-02-12 16:29:31 +0100578 def update_ist(self, service, bit=1):
579 (res, sw) = self._scc.read_binary(EF_ISIM_ADF_map['IST'])
580 if sw == '9000':
581 content = enc_st(res, service, bit)
582 (res, sw) = self._scc.update_binary(
583 EF_ISIM_ADF_map['IST'], content)
584 return sw
585
Sylvain Munaut76504e02010-12-07 00:24:32 +0100586
Philipp Maierbb73e512021-05-05 16:14:00 +0200587class MagicSimBase(abc.ABC, SimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100588 """
589 Theses cards uses several record based EFs to store the provider infos,
590 each possible provider uses a specific record number in each EF. The
591 indexes used are ( where N is the number of providers supported ) :
592 - [2 .. N+1] for the operator name
593 - [1 .. N] for the programmable EFs
Sylvain Munaut76504e02010-12-07 00:24:32 +0100594
Harald Weltec91085e2022-02-10 18:05:45 +0100595 * 3f00/7f4d/8f0c : Operator Name
Sylvain Munaut76504e02010-12-07 00:24:32 +0100596
Harald Weltec91085e2022-02-10 18:05:45 +0100597 bytes 0-15 : provider name, padded with 0xff
598 byte 16 : length of the provider name
599 byte 17 : 01 for valid records, 00 otherwise
Sylvain Munaut76504e02010-12-07 00:24:32 +0100600
Harald Weltec91085e2022-02-10 18:05:45 +0100601 * 3f00/7f4d/8f0d : Programmable Binary EFs
Sylvain Munaut76504e02010-12-07 00:24:32 +0100602
Harald Weltec91085e2022-02-10 18:05:45 +0100603 * 3f00/7f4d/8f0e : Programmable Record EFs
Sylvain Munaut76504e02010-12-07 00:24:32 +0100604
Harald Weltec91085e2022-02-10 18:05:45 +0100605 """
Sylvain Munaut76504e02010-12-07 00:24:32 +0100606
Harald Weltec91085e2022-02-10 18:05:45 +0100607 _files = {} # type: Dict[str, Tuple[str, int, bool]]
608 _ki_file = None # type: Optional[str]
Vadim Yanitskiy03c67f72021-05-02 02:10:39 +0200609
Harald Weltec91085e2022-02-10 18:05:45 +0100610 @classmethod
611 def autodetect(kls, scc):
612 try:
613 for p, l, t in kls._files.values():
614 if not t:
615 continue
616 if scc.record_size(['3f00', '7f4d', p]) != l:
617 return None
618 except:
619 return None
Sylvain Munaut76504e02010-12-07 00:24:32 +0100620
Harald Weltec91085e2022-02-10 18:05:45 +0100621 return kls(scc)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100622
Harald Weltec91085e2022-02-10 18:05:45 +0100623 def _get_count(self):
624 """
625 Selects the file and returns the total number of entries
626 and entry size
627 """
628 f = self._files['name']
Sylvain Munaut76504e02010-12-07 00:24:32 +0100629
Harald Weltec91085e2022-02-10 18:05:45 +0100630 r = self._scc.select_path(['3f00', '7f4d', f[0]])
631 rec_len = int(r[-1][28:30], 16)
632 tlen = int(r[-1][4:8], 16)
633 rec_cnt = (tlen // rec_len) - 1
Sylvain Munaut76504e02010-12-07 00:24:32 +0100634
Harald Weltec91085e2022-02-10 18:05:45 +0100635 if (rec_cnt < 1) or (rec_len != f[1]):
636 raise RuntimeError('Bad card type')
Sylvain Munaut76504e02010-12-07 00:24:32 +0100637
Harald Weltec91085e2022-02-10 18:05:45 +0100638 return rec_cnt
Sylvain Munaut76504e02010-12-07 00:24:32 +0100639
Harald Weltec91085e2022-02-10 18:05:45 +0100640 def program(self, p):
641 # Go to dir
642 self._scc.select_path(['3f00', '7f4d'])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100643
Harald Weltec91085e2022-02-10 18:05:45 +0100644 # Home PLMN in PLMN_Sel format
645 hplmn = enc_plmn(p['mcc'], p['mnc'])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100646
Harald Weltec91085e2022-02-10 18:05:45 +0100647 # Operator name ( 3f00/7f4d/8f0c )
648 self._scc.update_record(self._files['name'][0], 2,
649 rpad(b2h(p['name']), 32) + ('%02x' %
650 len(p['name'])) + '01'
651 )
Sylvain Munaut76504e02010-12-07 00:24:32 +0100652
Harald Weltec91085e2022-02-10 18:05:45 +0100653 # ICCID/IMSI/Ki/HPLMN ( 3f00/7f4d/8f0d )
654 v = ''
Sylvain Munaut76504e02010-12-07 00:24:32 +0100655
Harald Weltec91085e2022-02-10 18:05:45 +0100656 # inline Ki
657 if self._ki_file is None:
658 v += p['ki']
Sylvain Munaut76504e02010-12-07 00:24:32 +0100659
Harald Weltec91085e2022-02-10 18:05:45 +0100660 # ICCID
661 v += '3f00' + '2fe2' + '0a' + enc_iccid(p['iccid'])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100662
Harald Weltec91085e2022-02-10 18:05:45 +0100663 # IMSI
664 v += '7f20' + '6f07' + '09' + enc_imsi(p['imsi'])
Sylvain Munaut76504e02010-12-07 00:24:32 +0100665
Harald Weltec91085e2022-02-10 18:05:45 +0100666 # Ki
667 if self._ki_file:
668 v += self._ki_file + '10' + p['ki']
Sylvain Munaut76504e02010-12-07 00:24:32 +0100669
Harald Weltec91085e2022-02-10 18:05:45 +0100670 # PLMN_Sel
671 v += '6f30' + '18' + rpad(hplmn, 36)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100672
Harald Weltec91085e2022-02-10 18:05:45 +0100673 # ACC
674 # This doesn't work with "fake" SuperSIM cards,
675 # but will hopefully work with real SuperSIMs.
676 if p.get('acc') is not None:
677 v += '6f78' + '02' + lpad(p['acc'], 4)
Alexander Chemeris21885242013-07-02 16:56:55 +0400678
Harald Weltec91085e2022-02-10 18:05:45 +0100679 self._scc.update_record(self._files['b_ef'][0], 1,
680 rpad(v, self._files['b_ef'][1]*2)
681 )
Sylvain Munaut76504e02010-12-07 00:24:32 +0100682
Harald Weltec91085e2022-02-10 18:05:45 +0100683 # SMSP ( 3f00/7f4d/8f0e )
684 # FIXME
Sylvain Munaut76504e02010-12-07 00:24:32 +0100685
Harald Weltec91085e2022-02-10 18:05:45 +0100686 # Write PLMN_Sel forcefully as well
687 r = self._scc.select_path(['3f00', '7f20', '6f30'])
688 tl = int(r[-1][4:8], 16)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100689
Harald Weltec91085e2022-02-10 18:05:45 +0100690 hplmn = enc_plmn(p['mcc'], p['mnc'])
691 self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100692
Harald Weltec91085e2022-02-10 18:05:45 +0100693 def erase(self):
694 # Dummy
695 df = {}
696 for k, v in self._files.items():
697 ofs = 1
698 fv = v[1] * 'ff'
699 if k == 'name':
700 ofs = 2
701 fv = fv[0:-4] + '0000'
702 df[v[0]] = (fv, ofs)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100703
Harald Weltec91085e2022-02-10 18:05:45 +0100704 # Write
705 for n in range(0, self._get_count()):
706 for k, (msg, ofs) in df.items():
707 self._scc.update_record(['3f00', '7f4d', k], n + ofs, msg)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100708
709
Vadim Yanitskiy85302d62021-05-02 02:18:42 +0200710class SuperSim(MagicSimBase):
Sylvain Munaut76504e02010-12-07 00:24:32 +0100711
Harald Weltec91085e2022-02-10 18:05:45 +0100712 name = 'supersim'
Sylvain Munaut76504e02010-12-07 00:24:32 +0100713
Harald Weltec91085e2022-02-10 18:05:45 +0100714 _files = {
715 'name': ('8f0c', 18, True),
716 'b_ef': ('8f0d', 74, True),
717 'r_ef': ('8f0e', 50, True),
718 }
Sylvain Munaut76504e02010-12-07 00:24:32 +0100719
Harald Weltec91085e2022-02-10 18:05:45 +0100720 _ki_file = None
Sylvain Munaut76504e02010-12-07 00:24:32 +0100721
722
Vadim Yanitskiy85302d62021-05-02 02:18:42 +0200723class MagicSim(MagicSimBase):
Sylvain Munaut76504e02010-12-07 00:24:32 +0100724
Harald Weltec91085e2022-02-10 18:05:45 +0100725 name = 'magicsim'
Sylvain Munaut76504e02010-12-07 00:24:32 +0100726
Harald Weltec91085e2022-02-10 18:05:45 +0100727 _files = {
728 'name': ('8f0c', 18, True),
729 'b_ef': ('8f0d', 130, True),
730 'r_ef': ('8f0e', 102, False),
731 }
Sylvain Munaut76504e02010-12-07 00:24:32 +0100732
Harald Weltec91085e2022-02-10 18:05:45 +0100733 _ki_file = '6f1b'
Sylvain Munaut76504e02010-12-07 00:24:32 +0100734
735
Philipp Maierbb73e512021-05-05 16:14:00 +0200736class FakeMagicSim(SimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100737 """
738 Theses cards have a record based EF 3f00/000c that contains the provider
739 information. See the program method for its format. The records go from
740 1 to N.
741 """
Sylvain Munaut76504e02010-12-07 00:24:32 +0100742
Harald Weltec91085e2022-02-10 18:05:45 +0100743 name = 'fakemagicsim'
Sylvain Munaut76504e02010-12-07 00:24:32 +0100744
Harald Weltec91085e2022-02-10 18:05:45 +0100745 @classmethod
746 def autodetect(kls, scc):
747 try:
748 if scc.record_size(['3f00', '000c']) != 0x5a:
749 return None
750 except:
751 return None
Sylvain Munaut76504e02010-12-07 00:24:32 +0100752
Harald Weltec91085e2022-02-10 18:05:45 +0100753 return kls(scc)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100754
Harald Weltec91085e2022-02-10 18:05:45 +0100755 def _get_infos(self):
756 """
757 Selects the file and returns the total number of entries
758 and entry size
759 """
Sylvain Munaut76504e02010-12-07 00:24:32 +0100760
Harald Weltec91085e2022-02-10 18:05:45 +0100761 r = self._scc.select_path(['3f00', '000c'])
762 rec_len = int(r[-1][28:30], 16)
763 tlen = int(r[-1][4:8], 16)
764 rec_cnt = (tlen // rec_len) - 1
Sylvain Munaut76504e02010-12-07 00:24:32 +0100765
Harald Weltec91085e2022-02-10 18:05:45 +0100766 if (rec_cnt < 1) or (rec_len != 0x5a):
767 raise RuntimeError('Bad card type')
Sylvain Munaut76504e02010-12-07 00:24:32 +0100768
Harald Weltec91085e2022-02-10 18:05:45 +0100769 return rec_cnt, rec_len
Sylvain Munaut76504e02010-12-07 00:24:32 +0100770
Harald Weltec91085e2022-02-10 18:05:45 +0100771 def program(self, p):
772 # Home PLMN
773 r = self._scc.select_path(['3f00', '7f20', '6f30'])
774 tl = int(r[-1][4:8], 16)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100775
Harald Weltec91085e2022-02-10 18:05:45 +0100776 hplmn = enc_plmn(p['mcc'], p['mnc'])
777 self._scc.update_binary('6f30', hplmn + 'ff' * (tl-3))
Sylvain Munaut76504e02010-12-07 00:24:32 +0100778
Harald Weltec91085e2022-02-10 18:05:45 +0100779 # Get total number of entries and entry size
780 rec_cnt, rec_len = self._get_infos()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100781
Harald Weltec91085e2022-02-10 18:05:45 +0100782 # Set first entry
783 entry = (
784 '81' + # 1b Status: Valid & Active
785 rpad(s2h(p['name'][0:14]), 28) + # 14b Entry Name
786 enc_iccid(p['iccid']) + # 10b ICCID
787 enc_imsi(p['imsi']) + # 9b IMSI_len + id_type(9) + IMSI
788 p['ki'] + # 16b Ki
789 lpad(p['smsp'], 80) # 40b SMSP (padded with ff if needed)
790 )
791 self._scc.update_record('000c', 1, entry)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100792
Harald Weltec91085e2022-02-10 18:05:45 +0100793 def erase(self):
794 # Get total number of entries and entry size
795 rec_cnt, rec_len = self._get_infos()
Sylvain Munaut76504e02010-12-07 00:24:32 +0100796
Harald Weltec91085e2022-02-10 18:05:45 +0100797 # Erase all entries
798 entry = 'ff' * rec_len
799 for i in range(0, rec_cnt):
800 self._scc.update_record('000c', 1+i, entry)
Sylvain Munaut76504e02010-12-07 00:24:32 +0100801
Sylvain Munaut5da8d4e2013-07-02 15:13:24 +0200802
Philipp Maierbb73e512021-05-05 16:14:00 +0200803class GrcardSim(SimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100804 """
805 Greencard (grcard.cn) HZCOS GSM SIM
806 These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
807 and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
808 """
Harald Welte3156d902011-03-22 21:48:19 +0100809
Harald Weltec91085e2022-02-10 18:05:45 +0100810 name = 'grcardsim'
Harald Welte3156d902011-03-22 21:48:19 +0100811
Harald Weltec91085e2022-02-10 18:05:45 +0100812 @classmethod
813 def autodetect(kls, scc):
814 return None
Harald Welte3156d902011-03-22 21:48:19 +0100815
Harald Weltec91085e2022-02-10 18:05:45 +0100816 def program(self, p):
817 # We don't really know yet what ADM PIN 4 is about
818 #self._scc.verify_chv(4, h2b("4444444444444444"))
Harald Welte3156d902011-03-22 21:48:19 +0100819
Harald Weltec91085e2022-02-10 18:05:45 +0100820 # Authenticate using ADM PIN 5
821 if p['pin_adm']:
822 pin = h2b(p['pin_adm'])
823 else:
824 pin = h2b("4444444444444444")
825 self._scc.verify_chv(5, pin)
Harald Welte3156d902011-03-22 21:48:19 +0100826
Harald Weltec91085e2022-02-10 18:05:45 +0100827 # EF.ICCID
828 r = self._scc.select_path(['3f00', '2fe2'])
829 data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
Harald Welte3156d902011-03-22 21:48:19 +0100830
Harald Weltec91085e2022-02-10 18:05:45 +0100831 # EF.IMSI
832 r = self._scc.select_path(['3f00', '7f20', '6f07'])
833 data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
Harald Welte3156d902011-03-22 21:48:19 +0100834
Harald Weltec91085e2022-02-10 18:05:45 +0100835 # EF.ACC
836 if p.get('acc') is not None:
837 data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
Harald Welte3156d902011-03-22 21:48:19 +0100838
Harald Weltec91085e2022-02-10 18:05:45 +0100839 # EF.SMSP
840 if p.get('smsp'):
841 r = self._scc.select_path(['3f00', '7f10', '6f42'])
842 data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
Harald Welte3156d902011-03-22 21:48:19 +0100843
Harald Weltec91085e2022-02-10 18:05:45 +0100844 # Set the Ki using proprietary command
845 pdu = '80d4020010' + p['ki']
846 data, sw = self._scc._tp.send_apdu(pdu)
Harald Welte3156d902011-03-22 21:48:19 +0100847
Harald Weltec91085e2022-02-10 18:05:45 +0100848 # EF.HPLMN
849 r = self._scc.select_path(['3f00', '7f20', '6f30'])
850 size = int(r[-1][4:8], 16)
851 hplmn = enc_plmn(p['mcc'], p['mnc'])
852 self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
Harald Welte3156d902011-03-22 21:48:19 +0100853
Harald Weltec91085e2022-02-10 18:05:45 +0100854 # EF.SPN (Service Provider Name)
855 r = self._scc.select_path(['3f00', '7f20', '6f30'])
856 size = int(r[-1][4:8], 16)
857 # FIXME
Harald Welte3156d902011-03-22 21:48:19 +0100858
Harald Weltec91085e2022-02-10 18:05:45 +0100859 # FIXME: EF.MSISDN
Harald Welte3156d902011-03-22 21:48:19 +0100860
Sylvain Munaut76504e02010-12-07 00:24:32 +0100861
Harald Weltee10394b2011-12-07 12:34:14 +0100862class SysmoSIMgr1(GrcardSim):
Harald Weltec91085e2022-02-10 18:05:45 +0100863 """
864 sysmocom sysmoSIM-GR1
865 These cards have a much more regular ISO 7816-4 / TS 11.11 structure,
866 and use standard UPDATE RECORD / UPDATE BINARY commands except for Ki.
867 """
868 name = 'sysmosim-gr1'
Harald Weltee10394b2011-12-07 12:34:14 +0100869
Harald Weltec91085e2022-02-10 18:05:45 +0100870 @classmethod
871 def autodetect(kls, scc):
872 try:
873 # Look for ATR
874 if scc.get_atr() == toBytes("3B 99 18 00 11 88 22 33 44 55 66 77 60"):
875 return kls(scc)
876 except:
877 return None
878 return None
879
Sylvain Munaut5da8d4e2013-07-02 15:13:24 +0200880
Harald Welteca673942020-06-03 15:19:40 +0200881class SysmoUSIMgr1(UsimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100882 """
883 sysmocom sysmoUSIM-GR1
884 """
885 name = 'sysmoUSIM-GR1'
Holger Hans Peter Freyther4d91bf42012-03-22 14:28:38 +0100886
Harald Weltec91085e2022-02-10 18:05:45 +0100887 @classmethod
888 def autodetect(kls, scc):
889 # TODO: Access the ATR
890 return None
Holger Hans Peter Freyther4d91bf42012-03-22 14:28:38 +0100891
Harald Weltec91085e2022-02-10 18:05:45 +0100892 def program(self, p):
893 # TODO: check if verify_chv could be used or what it needs
894 # self._scc.verify_chv(0x0A, [0x33,0x32,0x32,0x31,0x33,0x32,0x33,0x32])
895 # Unlock the card..
896 data, sw = self._scc._tp.send_apdu_checksw(
897 "0020000A083332323133323332")
Holger Hans Peter Freyther4d91bf42012-03-22 14:28:38 +0100898
Harald Weltec91085e2022-02-10 18:05:45 +0100899 # TODO: move into SimCardCommands
900 par = (p['ki'] + # 16b K
901 p['opc'] + # 32b OPC
902 enc_iccid(p['iccid']) + # 10b ICCID
903 enc_imsi(p['imsi']) # 9b IMSI_len + id_type(9) + IMSI
904 )
905 data, sw = self._scc._tp.send_apdu_checksw("0099000033" + par)
Holger Hans Peter Freyther4d91bf42012-03-22 14:28:38 +0100906
Sylvain Munaut053c8952013-07-02 15:12:32 +0200907
Philipp Maierbb73e512021-05-05 16:14:00 +0200908class SysmoSIMgr2(SimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100909 """
910 sysmocom sysmoSIM-GR2
911 """
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100912
Harald Weltec91085e2022-02-10 18:05:45 +0100913 name = 'sysmoSIM-GR2'
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100914
Harald Weltec91085e2022-02-10 18:05:45 +0100915 @classmethod
916 def autodetect(kls, scc):
917 try:
918 # Look for ATR
919 if scc.get_atr() == toBytes("3B 7D 94 00 00 55 55 53 0A 74 86 93 0B 24 7C 4D 54 68"):
920 return kls(scc)
921 except:
922 return None
923 return None
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100924
Harald Weltec91085e2022-02-10 18:05:45 +0100925 def program(self, p):
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100926
Harald Weltec91085e2022-02-10 18:05:45 +0100927 # select MF
928 r = self._scc.select_path(['3f00'])
Daniel Willmann5d8cd9b2020-10-19 11:01:49 +0200929
Harald Weltec91085e2022-02-10 18:05:45 +0100930 # authenticate as SUPER ADM using default key
931 self._scc.verify_chv(0x0b, h2b("3838383838383838"))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100932
Harald Weltec91085e2022-02-10 18:05:45 +0100933 # set ADM pin using proprietary command
934 # INS: D4
935 # P1: 3A for PIN, 3B for PUK
936 # P2: CHV number, as in VERIFY CHV for PIN, and as in UNBLOCK CHV for PUK
937 # P3: 08, CHV length (curiously the PUK is also 08 length, instead of 10)
938 if p['pin_adm']:
939 pin = h2b(p['pin_adm'])
940 else:
941 pin = h2b("4444444444444444")
Jan Balkec3ebd332015-01-26 12:22:55 +0100942
Harald Weltec91085e2022-02-10 18:05:45 +0100943 pdu = 'A0D43A0508' + b2h(pin)
944 data, sw = self._scc._tp.send_apdu(pdu)
Daniel Willmann5d8cd9b2020-10-19 11:01:49 +0200945
Harald Weltec91085e2022-02-10 18:05:45 +0100946 # authenticate as ADM (enough to write file, and can set PINs)
Jan Balkec3ebd332015-01-26 12:22:55 +0100947
Harald Weltec91085e2022-02-10 18:05:45 +0100948 self._scc.verify_chv(0x05, pin)
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100949
Harald Weltec91085e2022-02-10 18:05:45 +0100950 # write EF.ICCID
951 data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100952
Harald Weltec91085e2022-02-10 18:05:45 +0100953 # select DF_GSM
954 r = self._scc.select_path(['7f20'])
Daniel Willmann5d8cd9b2020-10-19 11:01:49 +0200955
Harald Weltec91085e2022-02-10 18:05:45 +0100956 # write EF.IMSI
957 data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100958
Harald Weltec91085e2022-02-10 18:05:45 +0100959 # write EF.ACC
960 if p.get('acc') is not None:
961 data, sw = self._scc.update_binary('6f78', lpad(p['acc'], 4))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100962
Harald Weltec91085e2022-02-10 18:05:45 +0100963 # get size and write EF.HPLMN
964 r = self._scc.select_path(['6f30'])
965 size = int(r[-1][4:8], 16)
966 hplmn = enc_plmn(p['mcc'], p['mnc'])
967 self._scc.update_binary('6f30', hplmn + 'ff' * (size-3))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100968
Harald Weltec91085e2022-02-10 18:05:45 +0100969 # set COMP128 version 0 in proprietary file
970 data, sw = self._scc.update_binary('0001', '001000')
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100971
Harald Weltec91085e2022-02-10 18:05:45 +0100972 # set Ki in proprietary file
973 data, sw = self._scc.update_binary('0001', p['ki'], 3)
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100974
Harald Weltec91085e2022-02-10 18:05:45 +0100975 # select DF_TELECOM
976 r = self._scc.select_path(['3f00', '7f10'])
Daniel Willmann5d8cd9b2020-10-19 11:01:49 +0200977
Harald Weltec91085e2022-02-10 18:05:45 +0100978 # write EF.SMSP
979 if p.get('smsp'):
980 data, sw = self._scc.update_record('6f42', 1, lpad(p['smsp'], 80))
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100981
Sylvain Munaut2fc205c2013-12-23 17:22:56 +0100982
Harald Welteca673942020-06-03 15:19:40 +0200983class SysmoUSIMSJS1(UsimCard):
Harald Weltec91085e2022-02-10 18:05:45 +0100984 """
985 sysmocom sysmoUSIM-SJS1
986 """
Jan Balke3e840672015-01-26 15:36:27 +0100987
Harald Weltec91085e2022-02-10 18:05:45 +0100988 name = 'sysmoUSIM-SJS1'
Jan Balke3e840672015-01-26 15:36:27 +0100989
Harald Weltec91085e2022-02-10 18:05:45 +0100990 def __init__(self, ssc):
991 super(SysmoUSIMSJS1, self).__init__(ssc)
992 self._scc.cla_byte = "00"
993 self._scc.sel_ctrl = "0004" # request an FCP
Jan Balke3e840672015-01-26 15:36:27 +0100994
Harald Weltec91085e2022-02-10 18:05:45 +0100995 @classmethod
996 def autodetect(kls, scc):
997 try:
998 # Look for ATR
999 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"):
1000 return kls(scc)
1001 except:
1002 return None
1003 return None
Jan Balke3e840672015-01-26 15:36:27 +01001004
Harald Weltec91085e2022-02-10 18:05:45 +01001005 def verify_adm(self, key):
1006 # authenticate as ADM using default key (written on the card..)
1007 if not key:
1008 raise ValueError(
1009 "Please provide a PIN-ADM as there is no default one")
1010 (res, sw) = self._scc.verify_chv(0x0A, key)
1011 return sw
Harald Weltea6704252021-01-08 20:19:11 +01001012
Harald Weltec91085e2022-02-10 18:05:45 +01001013 def program(self, p):
1014 self.verify_adm(h2b(p['pin_adm']))
Jan Balke3e840672015-01-26 15:36:27 +01001015
Harald Weltec91085e2022-02-10 18:05:45 +01001016 # select MF
1017 r = self._scc.select_path(['3f00'])
Jan Balke3e840672015-01-26 15:36:27 +01001018
Harald Weltec91085e2022-02-10 18:05:45 +01001019 # write EF.ICCID
1020 data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
Philipp Maiere9604882017-03-21 17:24:31 +01001021
Harald Weltec91085e2022-02-10 18:05:45 +01001022 # select DF_GSM
1023 r = self._scc.select_path(['7f20'])
Jan Balke3e840672015-01-26 15:36:27 +01001024
Harald Weltec91085e2022-02-10 18:05:45 +01001025 # set Ki in proprietary file
1026 data, sw = self._scc.update_binary('00FF', p['ki'])
Jan Balke3e840672015-01-26 15:36:27 +01001027
Harald Weltec91085e2022-02-10 18:05:45 +01001028 # set OPc in proprietary file
1029 if 'opc' in p:
1030 content = "01" + p['opc']
1031 data, sw = self._scc.update_binary('00F7', content)
Jan Balke3e840672015-01-26 15:36:27 +01001032
Harald Weltec91085e2022-02-10 18:05:45 +01001033 # set Service Provider Name
1034 if p.get('name') is not None:
1035 self.update_spn(p['name'], True, True)
Supreeth Herle7947d922019-06-08 07:50:53 +02001036
Harald Weltec91085e2022-02-10 18:05:45 +01001037 if p.get('acc') is not None:
1038 self.update_acc(p['acc'])
Supreeth Herlec8796a32019-12-23 12:23:42 +01001039
Harald Weltec91085e2022-02-10 18:05:45 +01001040 # write EF.IMSI
1041 data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
Jan Balke3e840672015-01-26 15:36:27 +01001042
Harald Weltec91085e2022-02-10 18:05:45 +01001043 # EF.PLMNsel
1044 if p.get('mcc') and p.get('mnc'):
1045 sw = self.update_plmnsel(p['mcc'], p['mnc'])
1046 if sw != '9000':
1047 print("Programming PLMNsel failed with code %s" % sw)
Philipp Maier2d15ea02019-03-20 12:40:36 +01001048
Harald Weltec91085e2022-02-10 18:05:45 +01001049 # EF.PLMNwAcT
1050 if p.get('mcc') and p.get('mnc'):
1051 sw = self.update_plmn_act(p['mcc'], p['mnc'])
1052 if sw != '9000':
1053 print("Programming PLMNwAcT failed with code %s" % sw)
Philipp Maier2d15ea02019-03-20 12:40:36 +01001054
Harald Weltec91085e2022-02-10 18:05:45 +01001055 # EF.OPLMNwAcT
1056 if p.get('mcc') and p.get('mnc'):
1057 sw = self.update_oplmn_act(p['mcc'], p['mnc'])
1058 if sw != '9000':
1059 print("Programming OPLMNwAcT failed with code %s" % sw)
Philipp Maier2d15ea02019-03-20 12:40:36 +01001060
Harald Weltec91085e2022-02-10 18:05:45 +01001061 # EF.HPLMNwAcT
1062 if p.get('mcc') and p.get('mnc'):
1063 sw = self.update_hplmn_act(p['mcc'], p['mnc'])
1064 if sw != '9000':
1065 print("Programming HPLMNwAcT failed with code %s" % sw)
Supreeth Herlef442fb42020-01-21 12:47:32 +01001066
Harald Weltec91085e2022-02-10 18:05:45 +01001067 # EF.AD
1068 if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
1069 if p.get('mcc') and p.get('mnc'):
1070 mnc = p['mnc']
1071 else:
1072 mnc = None
1073 sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
1074 if sw != '9000':
1075 print("Programming AD failed with code %s" % sw)
Philipp Maier2d15ea02019-03-20 12:40:36 +01001076
Harald Weltec91085e2022-02-10 18:05:45 +01001077 # EF.SMSP
1078 if p.get('smsp'):
1079 r = self._scc.select_path(['3f00', '7f10'])
1080 data, sw = self._scc.update_record(
1081 '6f42', 1, lpad(p['smsp'], 104), force_len=True)
Jan Balke3e840672015-01-26 15:36:27 +01001082
Harald Weltec91085e2022-02-10 18:05:45 +01001083 # EF.MSISDN
1084 # TODO: Alpha Identifier (currently 'ff'O * 20)
1085 # TODO: Capability/Configuration1 Record Identifier
1086 # TODO: Extension1 Record Identifier
1087 if p.get('msisdn') is not None:
1088 msisdn = enc_msisdn(p['msisdn'])
1089 data = 'ff' * 20 + msisdn
Supreeth Herle5a541012019-12-22 08:59:16 +01001090
Harald Weltec91085e2022-02-10 18:05:45 +01001091 r = self._scc.select_path(['3f00', '7f10'])
1092 data, sw = self._scc.update_record('6F40', 1, data, force_len=True)
Supreeth Herle5a541012019-12-22 08:59:16 +01001093
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001094
herlesupreeth4a3580b2020-09-29 10:11:36 +02001095class FairwavesSIM(UsimCard):
Harald Weltec91085e2022-02-10 18:05:45 +01001096 """
1097 FairwavesSIM
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001098
Harald Weltec91085e2022-02-10 18:05:45 +01001099 The SIM card is operating according to the standard.
1100 For Ki/OP/OPC programming the following files are additionally open for writing:
1101 3F00/7F20/FF01 – OP/OPC:
1102 byte 1 = 0x01, bytes 2-17: OPC;
1103 byte 1 = 0x00, bytes 2-17: OP;
1104 3F00/7F20/FF02: Ki
1105 """
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001106
Harald Weltec91085e2022-02-10 18:05:45 +01001107 name = 'Fairwaves-SIM'
1108 # Propriatary files
1109 _EF_num = {
1110 'Ki': 'FF02',
1111 'OP/OPC': 'FF01',
1112 }
1113 _EF = {
1114 'Ki': DF['GSM']+[_EF_num['Ki']],
1115 'OP/OPC': DF['GSM']+[_EF_num['OP/OPC']],
1116 }
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001117
Harald Weltec91085e2022-02-10 18:05:45 +01001118 def __init__(self, ssc):
1119 super(FairwavesSIM, self).__init__(ssc)
1120 self._adm_chv_num = 0x11
1121 self._adm2_chv_num = 0x12
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001122
Harald Weltec91085e2022-02-10 18:05:45 +01001123 @classmethod
1124 def autodetect(kls, scc):
1125 try:
1126 # Look for ATR
1127 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"):
1128 return kls(scc)
1129 except:
1130 return None
1131 return None
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001132
Harald Weltec91085e2022-02-10 18:05:45 +01001133 def verify_adm2(self, key):
1134 '''
1135 Authenticate with ADM2 key.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001136
Harald Weltec91085e2022-02-10 18:05:45 +01001137 Fairwaves SIM cards support hierarchical key structure and ADM2 key
1138 is a key which has access to proprietary files (Ki and OP/OPC).
1139 That said, ADM key inherits permissions of ADM2 key and thus we rarely
1140 need ADM2 key per se.
1141 '''
1142 (res, sw) = self._scc.verify_chv(self._adm2_chv_num, key)
1143 return sw
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001144
Harald Weltec91085e2022-02-10 18:05:45 +01001145 def read_ki(self):
1146 """
1147 Read Ki in proprietary file.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001148
Harald Weltec91085e2022-02-10 18:05:45 +01001149 Requires ADM1 access level
1150 """
1151 return self._scc.read_binary(self._EF['Ki'])
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001152
Harald Weltec91085e2022-02-10 18:05:45 +01001153 def update_ki(self, ki):
1154 """
1155 Set Ki in proprietary file.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001156
Harald Weltec91085e2022-02-10 18:05:45 +01001157 Requires ADM1 access level
1158 """
1159 data, sw = self._scc.update_binary(self._EF['Ki'], ki)
1160 return sw
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001161
Harald Weltec91085e2022-02-10 18:05:45 +01001162 def read_op_opc(self):
1163 """
1164 Read Ki in proprietary file.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001165
Harald Weltec91085e2022-02-10 18:05:45 +01001166 Requires ADM1 access level
1167 """
1168 (ef, sw) = self._scc.read_binary(self._EF['OP/OPC'])
1169 type = 'OP' if ef[0:2] == '00' else 'OPC'
1170 return ((type, ef[2:]), sw)
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001171
Harald Weltec91085e2022-02-10 18:05:45 +01001172 def update_op(self, op):
1173 """
1174 Set OP in proprietary file.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001175
Harald Weltec91085e2022-02-10 18:05:45 +01001176 Requires ADM1 access level
1177 """
1178 content = '00' + op
1179 data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
1180 return sw
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001181
Harald Weltec91085e2022-02-10 18:05:45 +01001182 def update_opc(self, opc):
1183 """
1184 Set OPC in proprietary file.
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001185
Harald Weltec91085e2022-02-10 18:05:45 +01001186 Requires ADM1 access level
1187 """
1188 content = '01' + opc
1189 data, sw = self._scc.update_binary(self._EF['OP/OPC'], content)
1190 return sw
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001191
Harald Weltec91085e2022-02-10 18:05:45 +01001192 def program(self, p):
1193 # For some reason the card programming only works when the card
1194 # is handled as a classic SIM, even though it is an USIM, so we
1195 # reconfigure the class byte and the select control field on
1196 # the fly. When the programming is done the original values are
1197 # restored.
1198 cla_byte_orig = self._scc.cla_byte
1199 sel_ctrl_orig = self._scc.sel_ctrl
1200 self._scc.cla_byte = "a0"
1201 self._scc.sel_ctrl = "0000"
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001202
Harald Weltec91085e2022-02-10 18:05:45 +01001203 try:
1204 self._program(p)
1205 finally:
1206 # restore original cla byte and sel ctrl
1207 self._scc.cla_byte = cla_byte_orig
1208 self._scc.sel_ctrl = sel_ctrl_orig
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001209
Harald Weltec91085e2022-02-10 18:05:45 +01001210 def _program(self, p):
1211 # authenticate as ADM1
1212 if not p['pin_adm']:
1213 raise ValueError(
1214 "Please provide a PIN-ADM as there is no default one")
1215 self.verify_adm(h2b(p['pin_adm']))
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001216
Harald Weltec91085e2022-02-10 18:05:45 +01001217 # TODO: Set operator name
1218 if p.get('smsp') is not None:
1219 sw = self.update_smsp(p['smsp'])
1220 if sw != '9000':
1221 print("Programming SMSP failed with code %s" % sw)
1222 # This SIM doesn't support changing ICCID
1223 if p.get('mcc') is not None and p.get('mnc') is not None:
1224 sw = self.update_hplmn_act(p['mcc'], p['mnc'])
1225 if sw != '9000':
1226 print("Programming MCC/MNC failed with code %s" % sw)
1227 if p.get('imsi') is not None:
1228 sw = self.update_imsi(p['imsi'])
1229 if sw != '9000':
1230 print("Programming IMSI failed with code %s" % sw)
1231 if p.get('ki') is not None:
1232 sw = self.update_ki(p['ki'])
1233 if sw != '9000':
1234 print("Programming Ki failed with code %s" % sw)
1235 if p.get('opc') is not None:
1236 sw = self.update_opc(p['opc'])
1237 if sw != '9000':
1238 print("Programming OPC failed with code %s" % sw)
1239 if p.get('acc') is not None:
1240 sw = self.update_acc(p['acc'])
1241 if sw != '9000':
1242 print("Programming ACC failed with code %s" % sw)
Alexander Chemerise0d9d882018-01-10 14:18:32 +09001243
1244
Philipp Maierbb73e512021-05-05 16:14:00 +02001245class OpenCellsSim(SimCard):
Harald Weltec91085e2022-02-10 18:05:45 +01001246 """
1247 OpenCellsSim
Todd Neal9eeadfc2018-04-25 15:36:29 -05001248
Harald Weltec91085e2022-02-10 18:05:45 +01001249 """
Todd Neal9eeadfc2018-04-25 15:36:29 -05001250
Harald Weltec91085e2022-02-10 18:05:45 +01001251 name = 'OpenCells-SIM'
Todd Neal9eeadfc2018-04-25 15:36:29 -05001252
Harald Weltec91085e2022-02-10 18:05:45 +01001253 def __init__(self, ssc):
1254 super(OpenCellsSim, self).__init__(ssc)
1255 self._adm_chv_num = 0x0A
Todd Neal9eeadfc2018-04-25 15:36:29 -05001256
Harald Weltec91085e2022-02-10 18:05:45 +01001257 @classmethod
1258 def autodetect(kls, scc):
1259 try:
1260 # Look for ATR
1261 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"):
1262 return kls(scc)
1263 except:
1264 return None
1265 return None
Todd Neal9eeadfc2018-04-25 15:36:29 -05001266
Harald Weltec91085e2022-02-10 18:05:45 +01001267 def program(self, p):
1268 if not p['pin_adm']:
1269 raise ValueError(
1270 "Please provide a PIN-ADM as there is no default one")
1271 self._scc.verify_chv(0x0A, h2b(p['pin_adm']))
Todd Neal9eeadfc2018-04-25 15:36:29 -05001272
Harald Weltec91085e2022-02-10 18:05:45 +01001273 # select MF
1274 r = self._scc.select_path(['3f00'])
Todd Neal9eeadfc2018-04-25 15:36:29 -05001275
Harald Weltec91085e2022-02-10 18:05:45 +01001276 # write EF.ICCID
1277 data, sw = self._scc.update_binary('2fe2', enc_iccid(p['iccid']))
Todd Neal9eeadfc2018-04-25 15:36:29 -05001278
Harald Weltec91085e2022-02-10 18:05:45 +01001279 r = self._scc.select_path(['7ff0'])
Todd Neal9eeadfc2018-04-25 15:36:29 -05001280
Harald Weltec91085e2022-02-10 18:05:45 +01001281 # set Ki in proprietary file
1282 data, sw = self._scc.update_binary('FF02', p['ki'])
Todd Neal9eeadfc2018-04-25 15:36:29 -05001283
Harald Weltec91085e2022-02-10 18:05:45 +01001284 # set OPC in proprietary file
1285 data, sw = self._scc.update_binary('FF01', p['opc'])
Todd Neal9eeadfc2018-04-25 15:36:29 -05001286
Harald Weltec91085e2022-02-10 18:05:45 +01001287 # select DF_GSM
1288 r = self._scc.select_path(['7f20'])
Todd Neal9eeadfc2018-04-25 15:36:29 -05001289
Harald Weltec91085e2022-02-10 18:05:45 +01001290 # write EF.IMSI
1291 data, sw = self._scc.update_binary('6f07', enc_imsi(p['imsi']))
Todd Neal9eeadfc2018-04-25 15:36:29 -05001292
Todd Neal9eeadfc2018-04-25 15:36:29 -05001293
herlesupreeth4a3580b2020-09-29 10:11:36 +02001294class WavemobileSim(UsimCard):
Harald Weltec91085e2022-02-10 18:05:45 +01001295 """
1296 WavemobileSim
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001297
Harald Weltec91085e2022-02-10 18:05:45 +01001298 """
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001299
Harald Weltec91085e2022-02-10 18:05:45 +01001300 name = 'Wavemobile-SIM'
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001301
Harald Weltec91085e2022-02-10 18:05:45 +01001302 def __init__(self, ssc):
1303 super(WavemobileSim, self).__init__(ssc)
1304 self._adm_chv_num = 0x0A
1305 self._scc.cla_byte = "00"
1306 self._scc.sel_ctrl = "0004" # request an FCP
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001307
Harald Weltec91085e2022-02-10 18:05:45 +01001308 @classmethod
1309 def autodetect(kls, scc):
1310 try:
1311 # Look for ATR
1312 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"):
1313 return kls(scc)
1314 except:
1315 return None
1316 return None
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001317
Harald Weltec91085e2022-02-10 18:05:45 +01001318 def program(self, p):
1319 if not p['pin_adm']:
1320 raise ValueError(
1321 "Please provide a PIN-ADM as there is no default one")
1322 self.verify_adm(h2b(p['pin_adm']))
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001323
Harald Weltec91085e2022-02-10 18:05:45 +01001324 # EF.ICCID
1325 # TODO: Add programming of the ICCID
1326 if p.get('iccid'):
1327 print(
1328 "Warning: Programming of the ICCID is not implemented for this type of card.")
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001329
Harald Weltec91085e2022-02-10 18:05:45 +01001330 # KI (Presumably a propritary file)
1331 # TODO: Add programming of KI
1332 if p.get('ki'):
1333 print(
1334 "Warning: Programming of the KI is not implemented for this type of card.")
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001335
Harald Weltec91085e2022-02-10 18:05:45 +01001336 # OPc (Presumably a propritary file)
1337 # TODO: Add programming of OPc
1338 if p.get('opc'):
1339 print(
1340 "Warning: Programming of the OPc is not implemented for this type of card.")
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001341
Harald Weltec91085e2022-02-10 18:05:45 +01001342 # EF.SMSP
1343 if p.get('smsp'):
1344 sw = self.update_smsp(p['smsp'])
1345 if sw != '9000':
1346 print("Programming SMSP failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001347
Harald Weltec91085e2022-02-10 18:05:45 +01001348 # EF.IMSI
1349 if p.get('imsi'):
1350 sw = self.update_imsi(p['imsi'])
1351 if sw != '9000':
1352 print("Programming IMSI failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001353
Harald Weltec91085e2022-02-10 18:05:45 +01001354 # EF.ACC
1355 if p.get('acc'):
1356 sw = self.update_acc(p['acc'])
1357 if sw != '9000':
1358 print("Programming ACC failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001359
Harald Weltec91085e2022-02-10 18:05:45 +01001360 # EF.PLMNsel
1361 if p.get('mcc') and p.get('mnc'):
1362 sw = self.update_plmnsel(p['mcc'], p['mnc'])
1363 if sw != '9000':
1364 print("Programming PLMNsel failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001365
Harald Weltec91085e2022-02-10 18:05:45 +01001366 # EF.PLMNwAcT
1367 if p.get('mcc') and p.get('mnc'):
1368 sw = self.update_plmn_act(p['mcc'], p['mnc'])
1369 if sw != '9000':
1370 print("Programming PLMNwAcT failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001371
Harald Weltec91085e2022-02-10 18:05:45 +01001372 # EF.OPLMNwAcT
1373 if p.get('mcc') and p.get('mnc'):
1374 sw = self.update_oplmn_act(p['mcc'], p['mnc'])
1375 if sw != '9000':
1376 print("Programming OPLMNwAcT failed with code %s" % sw)
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001377
Harald Weltec91085e2022-02-10 18:05:45 +01001378 # EF.AD
1379 if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
1380 if p.get('mcc') and p.get('mnc'):
1381 mnc = p['mnc']
1382 else:
1383 mnc = None
1384 sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
1385 if sw != '9000':
1386 print("Programming AD failed with code %s" % sw)
Philipp Maier6e507a72019-04-01 16:33:48 +02001387
Harald Weltec91085e2022-02-10 18:05:45 +01001388 return None
Philipp Maierc8ce82a2018-07-04 17:57:20 +02001389
Todd Neal9eeadfc2018-04-25 15:36:29 -05001390
herlesupreethb0c7d122020-12-23 09:25:46 +01001391class SysmoISIMSJA2(UsimCard, IsimCard):
Harald Weltec91085e2022-02-10 18:05:45 +01001392 """
1393 sysmocom sysmoISIM-SJA2
1394 """
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001395
Harald Weltec91085e2022-02-10 18:05:45 +01001396 name = 'sysmoISIM-SJA2'
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001397
Harald Weltec91085e2022-02-10 18:05:45 +01001398 def __init__(self, ssc):
1399 super(SysmoISIMSJA2, self).__init__(ssc)
1400 self._scc.cla_byte = "00"
1401 self._scc.sel_ctrl = "0004" # request an FCP
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001402
Harald Weltec91085e2022-02-10 18:05:45 +01001403 @classmethod
1404 def autodetect(kls, scc):
1405 try:
1406 # Try card model #1
1407 atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 30 34 05 4B A9"
1408 if scc.get_atr() == toBytes(atr):
1409 return kls(scc)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001410
Harald Weltec91085e2022-02-10 18:05:45 +01001411 # Try card model #2
1412 atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 75 31 33 02 51 B2"
1413 if scc.get_atr() == toBytes(atr):
1414 return kls(scc)
Philipp Maierb3e11ea2020-03-11 12:32:44 +01001415
Harald Weltec91085e2022-02-10 18:05:45 +01001416 # Try card model #3
1417 atr = "3B 9F 96 80 1F 87 80 31 E0 73 FE 21 1B 67 4A 4C 52 75 31 04 51 D5"
1418 if scc.get_atr() == toBytes(atr):
1419 return kls(scc)
1420 except:
1421 return None
1422 return None
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001423
Harald Weltec91085e2022-02-10 18:05:45 +01001424 def verify_adm(self, key):
1425 # authenticate as ADM using default key (written on the card..)
1426 if not key:
1427 raise ValueError(
1428 "Please provide a PIN-ADM as there is no default one")
1429 (res, sw) = self._scc.verify_chv(0x0A, key)
1430 return sw
Harald Weltea6704252021-01-08 20:19:11 +01001431
Harald Weltec91085e2022-02-10 18:05:45 +01001432 def program(self, p):
1433 self.verify_adm(h2b(p['pin_adm']))
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001434
Harald Weltec91085e2022-02-10 18:05:45 +01001435 # This type of card does not allow to reprogram the ICCID.
1436 # Reprogramming the ICCID would mess up the card os software
1437 # license management, so the ICCID must be kept at its factory
1438 # setting!
1439 if p.get('iccid'):
1440 print(
1441 "Warning: Programming of the ICCID is not implemented for this type of card.")
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001442
Harald Weltec91085e2022-02-10 18:05:45 +01001443 # select DF_GSM
1444 self._scc.select_path(['7f20'])
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001445
Harald Weltec91085e2022-02-10 18:05:45 +01001446 # set Service Provider Name
1447 if p.get('name') is not None:
1448 self.update_spn(p['name'], True, True)
Robert Falkenberg54595362021-04-06 12:04:34 +02001449
Harald Weltec91085e2022-02-10 18:05:45 +01001450 # write EF.IMSI
1451 if p.get('imsi'):
1452 self._scc.update_binary('6f07', enc_imsi(p['imsi']))
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001453
Harald Weltec91085e2022-02-10 18:05:45 +01001454 # EF.PLMNsel
1455 if p.get('mcc') and p.get('mnc'):
1456 sw = self.update_plmnsel(p['mcc'], p['mnc'])
1457 if sw != '9000':
1458 print("Programming PLMNsel failed with code %s" % sw)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001459
Harald Weltec91085e2022-02-10 18:05:45 +01001460 # EF.PLMNwAcT
1461 if p.get('mcc') and p.get('mnc'):
1462 sw = self.update_plmn_act(p['mcc'], p['mnc'])
1463 if sw != '9000':
1464 print("Programming PLMNwAcT failed with code %s" % sw)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001465
Harald Weltec91085e2022-02-10 18:05:45 +01001466 # EF.OPLMNwAcT
1467 if p.get('mcc') and p.get('mnc'):
1468 sw = self.update_oplmn_act(p['mcc'], p['mnc'])
1469 if sw != '9000':
1470 print("Programming OPLMNwAcT failed with code %s" % sw)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001471
Harald Weltec91085e2022-02-10 18:05:45 +01001472 # EF.HPLMNwAcT
1473 if p.get('mcc') and p.get('mnc'):
1474 sw = self.update_hplmn_act(p['mcc'], p['mnc'])
1475 if sw != '9000':
1476 print("Programming HPLMNwAcT failed with code %s" % sw)
Harald Welte32f0d412020-05-05 17:35:57 +02001477
Harald Weltec91085e2022-02-10 18:05:45 +01001478 # EF.AD
1479 if (p.get('mcc') and p.get('mnc')) or p.get('opmode'):
1480 if p.get('mcc') and p.get('mnc'):
1481 mnc = p['mnc']
1482 else:
1483 mnc = None
1484 sw = self.update_ad(mnc=mnc, opmode=p.get('opmode'))
1485 if sw != '9000':
1486 print("Programming AD failed with code %s" % sw)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001487
Harald Weltec91085e2022-02-10 18:05:45 +01001488 # EF.SMSP
1489 if p.get('smsp'):
1490 r = self._scc.select_path(['3f00', '7f10'])
1491 data, sw = self._scc.update_record(
1492 '6f42', 1, lpad(p['smsp'], 104), force_len=True)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001493
Harald Weltec91085e2022-02-10 18:05:45 +01001494 # EF.MSISDN
1495 # TODO: Alpha Identifier (currently 'ff'O * 20)
1496 # TODO: Capability/Configuration1 Record Identifier
1497 # TODO: Extension1 Record Identifier
1498 if p.get('msisdn') is not None:
1499 msisdn = enc_msisdn(p['msisdn'])
1500 content = 'ff' * 20 + msisdn
Supreeth Herlec6019232020-03-26 10:00:45 +01001501
Harald Weltec91085e2022-02-10 18:05:45 +01001502 r = self._scc.select_path(['3f00', '7f10'])
1503 data, sw = self._scc.update_record(
1504 '6F40', 1, content, force_len=True)
Supreeth Herlec6019232020-03-26 10:00:45 +01001505
Harald Weltec91085e2022-02-10 18:05:45 +01001506 # EF.ACC
1507 if p.get('acc'):
1508 sw = self.update_acc(p['acc'])
1509 if sw != '9000':
1510 print("Programming ACC failed with code %s" % sw)
Supreeth Herlea97944b2020-03-26 10:03:25 +01001511
Harald Weltec91085e2022-02-10 18:05:45 +01001512 # Populate AIDs
1513 self.read_aids()
Supreeth Herle80164052020-03-23 12:06:29 +01001514
Harald Weltec91085e2022-02-10 18:05:45 +01001515 # update EF-SIM_AUTH_KEY (and EF-USIM_AUTH_KEY_2G, which is
1516 # hard linked to EF-USIM_AUTH_KEY)
1517 self._scc.select_path(['3f00'])
1518 self._scc.select_path(['a515'])
1519 if p.get('ki'):
1520 self._scc.update_binary('6f20', p['ki'], 1)
1521 if p.get('opc'):
1522 self._scc.update_binary('6f20', p['opc'], 17)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001523
Harald Weltec91085e2022-02-10 18:05:45 +01001524 # update EF-USIM_AUTH_KEY in ADF.ISIM
1525 data, sw = self.select_adf_by_aid(adf="isim")
1526 if sw == '9000':
1527 if p.get('ki'):
1528 self._scc.update_binary('af20', p['ki'], 1)
1529 if p.get('opc'):
1530 self._scc.update_binary('af20', p['opc'], 17)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001531
Harald Weltec91085e2022-02-10 18:05:45 +01001532 # update EF.P-CSCF in ADF.ISIM
1533 if self.file_exists(EF_ISIM_ADF_map['PCSCF']):
1534 if p.get('pcscf'):
1535 sw = self.update_pcscf(p['pcscf'])
1536 else:
1537 sw = self.update_pcscf("")
1538 if sw != '9000':
1539 print("Programming P-CSCF failed with code %s" % sw)
Supreeth Herlecf727f22020-03-24 17:32:21 +01001540
Harald Weltec91085e2022-02-10 18:05:45 +01001541 # update EF.DOMAIN in ADF.ISIM
1542 if self.file_exists(EF_ISIM_ADF_map['DOMAIN']):
1543 if p.get('ims_hdomain'):
1544 sw = self.update_domain(domain=p['ims_hdomain'])
1545 else:
1546 sw = self.update_domain()
Supreeth Herlecf727f22020-03-24 17:32:21 +01001547
Harald Weltec91085e2022-02-10 18:05:45 +01001548 if sw != '9000':
1549 print(
1550 "Programming Home Network Domain Name failed with code %s" % sw)
Supreeth Herle79f43dd2020-03-25 11:43:19 +01001551
Harald Weltec91085e2022-02-10 18:05:45 +01001552 # update EF.IMPI in ADF.ISIM
1553 # TODO: Validate IMPI input
1554 if self.file_exists(EF_ISIM_ADF_map['IMPI']):
1555 if p.get('impi'):
1556 sw = self.update_impi(p['impi'])
1557 else:
1558 sw = self.update_impi()
1559 if sw != '9000':
1560 print("Programming IMPI failed with code %s" % sw)
Supreeth Herle79f43dd2020-03-25 11:43:19 +01001561
Harald Weltec91085e2022-02-10 18:05:45 +01001562 # update EF.IMPU in ADF.ISIM
1563 # TODO: Validate IMPU input
1564 # Support multiple IMPU if there is enough space
1565 if self.file_exists(EF_ISIM_ADF_map['IMPU']):
1566 if p.get('impu'):
1567 sw = self.update_impu(p['impu'])
1568 else:
1569 sw = self.update_impu()
1570 if sw != '9000':
1571 print("Programming IMPU failed with code %s" % sw)
Supreeth Herlea5bd9682020-03-26 09:16:14 +01001572
Harald Weltec91085e2022-02-10 18:05:45 +01001573 data, sw = self.select_adf_by_aid(adf="usim")
1574 if sw == '9000':
1575 # update EF-USIM_AUTH_KEY in ADF.USIM
1576 if p.get('ki'):
1577 self._scc.update_binary('af20', p['ki'], 1)
1578 if p.get('opc'):
1579 self._scc.update_binary('af20', p['opc'], 17)
Supreeth Herlebe7007e2020-03-26 09:27:45 +01001580
Harald Weltec91085e2022-02-10 18:05:45 +01001581 # update EF.EHPLMN in ADF.USIM
1582 if self.file_exists(EF_USIM_ADF_map['EHPLMN']):
1583 if p.get('mcc') and p.get('mnc'):
1584 sw = self.update_ehplmn(p['mcc'], p['mnc'])
1585 if sw != '9000':
1586 print("Programming EHPLMN failed with code %s" % sw)
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001587
Harald Weltec91085e2022-02-10 18:05:45 +01001588 # update EF.ePDGId in ADF.USIM
1589 if self.file_exists(EF_USIM_ADF_map['ePDGId']):
1590 if p.get('epdgid'):
1591 sw = self.update_epdgid(p['epdgid'])
1592 else:
1593 sw = self.update_epdgid("")
1594 if sw != '9000':
1595 print("Programming ePDGId failed with code %s" % sw)
Supreeth Herle8e0fccd2020-03-23 12:10:56 +01001596
Harald Weltec91085e2022-02-10 18:05:45 +01001597 # update EF.ePDGSelection in ADF.USIM
1598 if self.file_exists(EF_USIM_ADF_map['ePDGSelection']):
1599 if p.get('epdgSelection'):
1600 epdg_plmn = p['epdgSelection']
1601 sw = self.update_ePDGSelection(
1602 epdg_plmn[:3], epdg_plmn[3:])
1603 else:
1604 sw = self.update_ePDGSelection("", "")
1605 if sw != '9000':
1606 print("Programming ePDGSelection failed with code %s" % sw)
Supreeth Herle8e0fccd2020-03-23 12:10:56 +01001607
Harald Weltec91085e2022-02-10 18:05:45 +01001608 # After successfully programming EF.ePDGId and EF.ePDGSelection,
1609 # Set service 106 and 107 as available in EF.UST
1610 # Disable service 95, 99, 115 if ISIM application is present
1611 if self.file_exists(EF_USIM_ADF_map['UST']):
1612 if p.get('epdgSelection') and p.get('epdgid'):
1613 sw = self.update_ust(106, 1)
1614 if sw != '9000':
1615 print("Programming UST failed with code %s" % sw)
1616 sw = self.update_ust(107, 1)
1617 if sw != '9000':
1618 print("Programming UST failed with code %s" % sw)
Supreeth Herlef964df42020-03-24 13:15:37 +01001619
Harald Weltec91085e2022-02-10 18:05:45 +01001620 sw = self.update_ust(95, 0)
1621 if sw != '9000':
1622 print("Programming UST failed with code %s" % sw)
1623 sw = self.update_ust(99, 0)
1624 if sw != '9000':
1625 print("Programming UST failed with code %s" % sw)
1626 sw = self.update_ust(115, 0)
1627 if sw != '9000':
1628 print("Programming UST failed with code %s" % sw)
Supreeth Herlef964df42020-03-24 13:15:37 +01001629
Harald Weltec91085e2022-02-10 18:05:45 +01001630 return
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001631
Philipp Maier0ad5bcf2019-12-31 17:55:47 +01001632
Todd Neal9eeadfc2018-04-25 15:36:29 -05001633# In order for autodetection ...
Harald Weltec91085e2022-02-10 18:05:45 +01001634_cards_classes = [FakeMagicSim, SuperSim, MagicSim, GrcardSim,
1635 SysmoSIMgr1, SysmoSIMgr2, SysmoUSIMgr1, SysmoUSIMSJS1,
1636 FairwavesSIM, OpenCellsSim, WavemobileSim, SysmoISIMSJA2]
1637
Alexander Chemeris8ad124a2018-01-10 14:17:55 +09001638
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001639def card_detect(ctype, scc):
Harald Weltec91085e2022-02-10 18:05:45 +01001640 # Detect type if needed
1641 card = None
1642 ctypes = dict([(kls.name, kls) for kls in _cards_classes])
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001643
Harald Weltec91085e2022-02-10 18:05:45 +01001644 if ctype == "auto":
1645 for kls in _cards_classes:
1646 card = kls.autodetect(scc)
1647 if card:
1648 print("Autodetected card type: %s" % card.name)
1649 card.reset()
1650 break
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001651
Harald Weltec91085e2022-02-10 18:05:45 +01001652 if card is None:
1653 print("Autodetection failed")
1654 return None
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001655
Harald Weltec91085e2022-02-10 18:05:45 +01001656 elif ctype in ctypes:
1657 card = ctypes[ctype](scc)
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001658
Harald Weltec91085e2022-02-10 18:05:45 +01001659 else:
1660 raise ValueError("Unknown card type: %s" % ctype)
Supreeth Herle4c306ab2020-03-18 11:38:00 +01001661
Harald Weltec91085e2022-02-10 18:05:45 +01001662 return card