blob: 38a69de6f3fce0ad18687406fc22c347f5067878 [file] [log] [blame]
Harald Welteb2edd142021-01-08 23:29:35 +01001# coding=utf-8
2"""Utilities / Functions related to ETSI TS 102 221, the core UICC spec.
3
4(C) 2021 by Harald Welte <laforge@osmocom.org>
5
6This program is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 of the License, or
9(at your option) any later version.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program. If not, see <http://www.gnu.org/licenses/>.
18"""
19
20from pytlv.TLV import *
21from struct import pack, unpack
22from pySim.utils import *
23from pySim.filesystem import *
Harald Welte4ae228a2021-05-02 21:29:04 +020024from bidict import bidict
25
26ts_102_22x_cmdset = CardCommandSet('TS 102 22x', [
27 # TS 102 221 Section 10.1.2 Table 10.5 "Coding of Instruction Byte"
28 CardCommand('SELECT', 0xA4, ['0X', '4X', '6X']),
29 CardCommand('STATUS', 0xF2, ['8X', 'CX', 'EX']),
30 CardCommand('READ BINARY', 0xB0, ['0X', '4X', '6X']),
31 CardCommand('UPDATE BINARY', 0xD6, ['0X', '4X', '6X']),
32 CardCommand('READ RECORD', 0xB2, ['0X', '4X', '6X']),
33 CardCommand('UPDATE RECORD', 0xDC, ['0X', '4X', '6X']),
34 CardCommand('SEARCH RECORD', 0xA2, ['0X', '4X', '6X']),
35 CardCommand('INCREASE', 0x32, ['8X', 'CX', 'EX']),
36 CardCommand('RETRIEVE DATA', 0xCB, ['8X', 'CX', 'EX']),
37 CardCommand('SET DATA', 0xDB, ['8X', 'CX', 'EX']),
38 CardCommand('VERIFY PIN', 0x20, ['0X', '4X', '6X']),
39 CardCommand('CHANGE PIN', 0x24, ['0X', '4X', '6X']),
40 CardCommand('DISABLE PIN', 0x26, ['0X', '4X', '6X']),
41 CardCommand('ENABLE PIN', 0x28, ['0X', '4X', '6X']),
42 CardCommand('UNBLOCK PIN', 0x2C, ['0X', '4X', '6X']),
43 CardCommand('DEACTIVATE FILE', 0x04, ['0X', '4X', '6X']),
44 CardCommand('ACTIVATE FILE', 0x44, ['0X', '4X', '6X']),
45 CardCommand('AUTHENTICATE', 0x88, ['0X', '4X', '6X']),
46 CardCommand('AUTHENTICATE', 0x89, ['0X', '4X', '6X']),
47 CardCommand('GET CHALLENGE', 0x84, ['0X', '4X', '6X']),
48 CardCommand('TERMINAL CAPABILITY', 0xAA, ['8X', 'CX', 'EX']),
49 CardCommand('TERMINAL PROFILE', 0x10, ['80']),
50 CardCommand('ENVELOPE', 0xC2, ['80']),
51 CardCommand('FETCH', 0x12, ['80']),
52 CardCommand('TERMINAL RESPONSE', 0x14, ['80']),
53 CardCommand('MANAGE CHANNEL', 0x70, ['0X', '4X', '6X']),
54 CardCommand('MANAGE SECURE CHANNEL', 0x73, ['0X', '4X', '6X']),
55 CardCommand('TRANSACT DATA', 0x75, ['0X', '4X', '6X']),
56 CardCommand('SUSPEND UICC', 0x76, ['80']),
57 CardCommand('GET IDENTITY', 0x78, ['8X', 'CX', 'EX']),
58 CardCommand('EXCHANGE CAPABILITIES', 0x7A, ['80']),
59 CardCommand('GET RESPONSE', 0xC0, ['0X', '4X', '6X']),
60 # TS 102 222 Section 6.1 Table 1 "Coding of the commands"
61 CardCommand('CREATE FILE', 0xE0, ['0X', '4X']),
62 CardCommand('DELETE FILE', 0xE4, ['0X', '4X']),
63 CardCommand('DEACTIVATE FILE', 0x04, ['0X', '4X']),
64 CardCommand('ACTIVATE FILE', 0x44, ['0X', '4X']),
65 CardCommand('TERMINATE DF', 0xE6, ['0X', '4X']),
66 CardCommand('TERMINATE EF', 0xE8, ['0X', '4X']),
67 CardCommand('TERMINATE CARD USAGE', 0xFE, ['0X', '4X']),
68 CardCommand('RESIZE FILE', 0xD4, ['8X', 'CX']),
69 ])
Harald Welteb2edd142021-01-08 23:29:35 +010070
71
72FCP_TLV_MAP = {
73 '82': 'file_descriptor',
74 '83': 'file_identifier',
75 '84': 'df_name',
76 'A5': 'proprietary_info',
77 '8A': 'life_cycle_status_int',
78 '8B': 'security_attrib_ref_expanded',
79 '8C': 'security_attrib_compact',
80 'AB': 'security_attrib_espanded',
81 'C6': 'pin_status_template_do',
82 '80': 'file_size',
83 '81': 'total_file_size',
84 '88': 'short_file_id',
85 }
86
87# ETSI TS 102 221 11.1.1.4.6
88FCP_Proprietary_TLV_MAP = {
89 '80': 'uicc_characteristics',
90 '81': 'application_power_consumption',
91 '82': 'minimum_app_clock_freq',
92 '83': 'available_memory',
93 '84': 'file_details',
94 '85': 'reserved_file_size',
95 '86': 'maximum_file_size',
96 '87': 'suported_system_commands',
97 '88': 'specific_uicc_env_cond',
98 '89': 'p2p_cat_secured_apdu',
99 # Additional private TLV objects (bits b7 and b8 of the first byte of the tag set to '1')
100 }
101
102# ETSI TS 102 221 11.1.1.4.3
103def interpret_file_descriptor(in_hex):
104 in_bin = h2b(in_hex)
105 out = {}
106 ft_dict = {
107 0: 'working_ef',
108 1: 'internal_ef',
109 7: 'df'
110 }
111 fs_dict = {
112 0: 'no_info_given',
113 1: 'transparent',
114 2: 'linear_fixed',
115 6: 'cyclic',
Harald Welte917d98c2021-04-21 11:51:25 +0200116 0x39: 'ber_tlv',
Harald Welteb2edd142021-01-08 23:29:35 +0100117 }
118 fdb = in_bin[0]
119 ftype = (fdb >> 3) & 7
Harald Welte917d98c2021-04-21 11:51:25 +0200120 if fdb & 0xbf == 0x39:
121 fstruct = 0x39
122 else:
123 fstruct = fdb & 7
Harald Welteb2edd142021-01-08 23:29:35 +0100124 out['shareable'] = True if fdb & 0x40 else False
125 out['file_type'] = ft_dict[ftype] if ftype in ft_dict else ftype
126 out['structure'] = fs_dict[fstruct] if fstruct in fs_dict else fstruct
127 if len(in_bin) >= 5:
128 out['record_len'] = int.from_bytes(in_bin[2:4], 'big')
129 out['num_of_rec'] = int.from_bytes(in_bin[4:5], 'big')
130 return out
131
132# ETSI TS 102 221 11.1.1.4.9
133def interpret_life_cycle_sts_int(in_hex):
134 lcsi = int(in_hex, 16)
135 if lcsi == 0x00:
136 return 'no_information'
137 elif lcsi == 0x01:
138 return 'creation'
139 elif lcsi == 0x03:
140 return 'initialization'
141 elif lcsi & 0x05 == 0x05:
142 return 'operational_activated'
143 elif lcsi & 0x05 == 0x04:
144 return 'operational_deactivated'
145 elif lcsi & 0xc0 == 0xc0:
146 return 'termination'
147 else:
148 return in_hex
149
150# ETSI TS 102 221 11.1.1.4.10
151FCP_Pin_Status_TLV_MAP = {
152 '90': 'ps_do',
153 '95': 'usage_qualifier',
154 '83': 'key_reference',
155 }
156
157def interpret_ps_templ_do(in_hex):
158 # cannot use the 'TLV' parser due to repeating tags
159 #psdo_tlv = TLV(FCP_Pin_Status_TLV_MAP)
160 #return psdo_tlv.parse(in_hex)
161 return in_hex
162
163# 'interpreter' functions for each tag
164FCP_interpreter_map = {
165 '80': lambda x: int(x, 16),
166 '82': interpret_file_descriptor,
167 '8A': interpret_life_cycle_sts_int,
168 'C6': interpret_ps_templ_do,
169 }
170
171FCP_prorietary_interpreter_map = {
172 '83': lambda x: int(x, 16),
173 }
174
175# pytlv unfortunately doesn't have a setting using which we can make it
176# accept unknown tags. It also doesn't raise a specific exception type but
177# just the generic ValueError, so we cannot ignore those either. Instead,
178# we insert a dict entry for every possible proprietary tag permitted
179def fixup_fcp_proprietary_tlv_map(tlv_map):
180 if 'D0' in tlv_map:
181 return
Philipp Maierc98ef8a2021-04-07 10:51:22 +0200182 for i in range(0xc0, 0xff):
Harald Welteb2edd142021-01-08 23:29:35 +0100183 i_hex = i2h([i]).upper()
184 tlv_map[i_hex] = 'proprietary_' + i_hex
Robert Falkenberg90f74972021-05-06 09:57:37 +0200185 # Other non-standard TLV objects found on some cards
186 tlv_map['9B'] = 'target_ef' # for sysmoUSIM-SJS1
Harald Welteb2edd142021-01-08 23:29:35 +0100187
188
189def tlv_key_replace(inmap, indata):
190 def newkey(inmap, key):
191 if key in inmap:
192 return inmap[key]
193 else:
194 return key
195 return {newkey(inmap, d[0]): d[1] for d in indata.items()}
196
197def tlv_val_interpret(inmap, indata):
198 def newval(inmap, key, val):
199 if key in inmap:
200 return inmap[key](val)
201 else:
202 return val
203 return {d[0]: newval(inmap, d[0], d[1]) for d in indata.items()}
204
Harald Welte4ae228a2021-05-02 21:29:04 +0200205# ETSI TS 102 221 Section 9.2.7 + ISO7816-4 9.3.3/9.3.4
206
207class _AM_DO_DF(DataObject):
208 def __init__(self):
209 super().__init__('access_mode', 'Access Mode', tag=0x80)
210
211 def from_bytes(self, do:bytes):
212 res = []
213 if len(do) != 1:
214 raise ValueError("We only support single-byte AMF inside AM-DO")
215 amf = do[0]
216 # tables 17..29 and 41..44 of 7816-4
217 if amf & 0x80 == 0:
218 if amf & 0x40:
219 res.append('delete_file')
220 if amf & 0x20:
221 res.append('terminate_df')
222 if amf & 0x10:
223 res.append('activate_file')
224 if amf & 0x08:
225 res.append('deactivate_file')
226 if amf & 0x04:
227 res.append('create_file_df')
228 if amf & 0x02:
229 res.append('create_file_ef')
230 if amf & 0x01:
231 res.append('delete_file_child')
232 self.decoded = res
233
234 def to_bytes(self):
235 val = 0
236 if 'delete_file' in self.decoded:
237 val |= 0x40
238 if 'terminate_df' in self.decoded:
239 val |= 0x20
240 if 'activate_file' in self.decoded:
241 val |= 0x10
242 if 'deactivate_file' in self.decoded:
243 val |= 0x08
244 if 'create_file_df' in self.decoded:
245 val |= 0x04
246 if 'create_file_ef' in self.decoded:
247 val |= 0x02
248 if 'delete_file_child' in self.decoded:
249 val |= 0x01
250 return val.to_bytes(1, 'big')
251
252
253class _AM_DO_EF(DataObject):
254 """ISO7816-4 9.3.2 Table 18 + 9.3.3.1 Table 31"""
255 def __init__(self):
256 super().__init__('access_mode', 'Access Mode', tag=0x80)
257
258 def from_bytes(self, do:bytes):
259 res = []
260 if len(do) != 1:
261 raise ValueError("We only support single-byte AMF inside AM-DO")
262 amf = do[0]
263 # tables 17..29 and 41..44 of 7816-4
264 if amf & 0x80 == 0:
265 if amf & 0x40:
266 res.append('delete_file')
267 if amf & 0x20:
268 res.append('terminate_ef')
269 if amf & 0x10:
270 res.append('activate_file_or_record')
271 if amf & 0x08:
272 res.append('deactivate_file_or_record')
273 if amf & 0x04:
274 res.append('write_append')
275 if amf & 0x02:
276 res.append('update_erase')
277 if amf & 0x01:
278 res.append('read_search_compare')
279 self.decoded = res
280
281 def to_bytes(self):
282 val = 0
283 if 'delete_file' in self.decoded:
284 val |= 0x40
285 if 'terminate_ef' in self.decoded:
286 val |= 0x20
287 if 'activate_file_or_record' in self.decoded:
288 val |= 0x10
289 if 'deactivate_file_or_record' in self.decoded:
290 val |= 0x08
291 if 'write_append' in self.decoded:
292 val |= 0x04
293 if 'update_erase' in self.decoded:
294 val |= 0x02
295 if 'read_search_compare' in self.decoded:
296 val |= 0x01
297 return val.to_bytes(1, 'big')
298
299class _AM_DO_CHDR(DataObject):
300 """Command Header Access Mode DO according to ISO 7816-4 Table 32."""
301 def __init__(self, tag):
302 super().__init__('command_header', 'Command Header Description', tag=tag)
303
304 def from_bytes(self, do:bytes):
305 res = {}
306 i = 0
307 if self.tag & 0x08:
308 res['CLA'] = do[i]
309 i += 1
310 if self.tag & 0x04:
311 res['INS'] = do[i]
312 i += 1
313 if self.tag & 0x02:
314 res['P1'] = do[i]
315 i += 1
316 if self.tag & 0x01:
317 res['P2'] = do[i]
318 i += 1
319 self.decoded = res
320
321 def _compute_tag(self):
322 """Override to encode the tag, as it depends on the value."""
323 tag = 0x80
324 if 'CLA' in self.decoded:
325 tag |= 0x08
326 if 'INS' in self.decoded:
327 tag |= 0x04
328 if 'P1' in self.decoded:
329 tag |= 0x02
330 if 'P2' in self.decoded:
331 tag |= 0x01
332 return tag
333
334 def to_bytes(self):
335 res = bytearray()
336 if 'CLA' in self.decoded:
337 res.append(self.decoded['CLA'])
338 if 'INS' in self.decoded:
339 res.append(self.decoded['INS'])
340 if 'P1' in self.decoded:
341 res.append(self.decoded['P1'])
342 if 'P2' in self.decoded:
343 res.append(self.decoded['P2'])
344 return res
345
346AM_DO_CHDR = DataObjectChoice('am_do_chdr', members=[
347 _AM_DO_CHDR(0x81), _AM_DO_CHDR(0x82), _AM_DO_CHDR(0x83), _AM_DO_CHDR(0x84),
348 _AM_DO_CHDR(0x85), _AM_DO_CHDR(0x86), _AM_DO_CHDR(0x87), _AM_DO_CHDR(0x88),
349 _AM_DO_CHDR(0x89), _AM_DO_CHDR(0x8a), _AM_DO_CHDR(0x8b), _AM_DO_CHDR(0x8c),
350 _AM_DO_CHDR(0x8d), _AM_DO_CHDR(0x8e), _AM_DO_CHDR(0x8f)])
351
352AM_DO_DF = AM_DO_CHDR | _AM_DO_DF()
353AM_DO_EF = AM_DO_CHDR | _AM_DO_EF()
354
355
356# TS 102 221 Section 9.5.1 / Table 9.3
357pin_names = bidict({
358 0x01: 'PIN1',
359 0x02: 'PIN2',
360 0x03: 'PIN3',
361 0x04: 'PIN4',
362 0x05: 'PIN5',
363 0x06: 'PIN6',
364 0x07: 'PIN7',
365 0x08: 'PIN8',
366 0x0a: 'ADM1',
367 0x0b: 'ADM2',
368 0x0c: 'ADM3',
369 0x0d: 'ADM4',
370 0x0e: 'ADM5',
371
372 0x11: 'UNIVERSAL_PIN',
373 0x81: '2PIN1',
374 0x82: '2PIN2',
375 0x83: '2PIN3',
376 0x84: '2PIN4',
377 0x85: '2PIN5',
378 0x86: '2PIN6',
379 0x87: '2PIN7',
380 0x88: '2PIN8',
381 0x8a: 'ADM6',
382 0x8b: 'ADM7',
383 0x8c: 'ADM8',
384 0x8d: 'ADM9',
385 0x8e: 'ADM10',
386 })
387
388class CRT_DO(DataObject):
389 """Control Reference Template as per TS 102 221 9.5.1"""
390 def __init__(self):
391 super().__init__('control_reference_template', 'Control Reference Template', tag=0xA4)
392
393 def from_bytes(self, do: bytes):
394 """Decode a Control Reference Template DO."""
395 if len(do) != 6:
396 raise ValueError('Unsupported CRT DO length: %s', do)
397 if do[0] != 0x83 or do[1] != 0x01:
398 raise ValueError('Unsupported Key Ref Tag or Len in CRT DO %s', do)
399 if do[3:] != b'\x95\x01\x08':
400 raise ValueError('Unsupported Usage Qualifier Tag or Len in CRT DO %s', do)
401 self.encoded = do[0:6]
402 self.decoded = pin_names[do[2]]
403 return do[6:]
404
405 def to_bytes(self):
406 pin = pin_names.inverse[self.decoded]
407 return b'\x83\x01' + pin.to_bytes(1, 'big') + b'\x95\x01\x08'
408
409# ISO7816-4 9.3.3 Table 33
410class SecCondByte_DO(DataObject):
411 def __init__(self, tag=0x9d):
412 super().__init__('security_condition_byte', tag=tag)
413
414 def from_bytes(self, binary:bytes):
415 if len(binary) != 1:
416 raise ValueError
417 inb = binary[0]
418 if inb == 0:
419 cond = 'always'
420 if inb == 0xff:
421 cond = 'never'
422 res = []
423 if inb & 0x80:
424 cond = 'and'
425 else:
426 cond = 'or'
427 if inb & 0x40:
428 res.append('secure_messaging')
429 if inb & 0x20:
430 res.append('external_auth')
431 if inb & 0x10:
432 res.append('user_auth')
433 rd = {'mode': cond }
434 if len(res):
435 rd['conditions'] = res
436 self.decoded = rd
437
438 def to_bytes(self):
439 mode = self.decoded['mode']
440 if mode == 'always':
441 res = 0
442 elif mode == 'never':
443 res = 0xff
444 else:
445 res = 0
446 if mode == 'and':
447 res |= 0x80
448 elif mode == 'or':
449 pass
450 else:
451 raise ValueError('Unknown mode %s' % mode)
452 for c in self.decoded['conditions']:
453 if c == 'secure_messaging':
454 res |= 0x40
455 elif c == 'external_auth':
456 res |= 0x20
457 elif c == 'user_auth':
458 res |= 0x10
459 else:
460 raise ValueError('Unknown condition %s' % c)
461 return res.to_bytes(1, 'big')
462
463Always_DO = TL0_DataObject('always', 'Always', 0x90)
464Never_DO = TL0_DataObject('never', 'Never', 0x97)
465SC_DO = DataObjectChoice('security_condition', 'Security Condition',
466 members=[Always_DO, Never_DO, SecCondByte_DO(), SecCondByte_DO(0x9e), CRT_DO()])
467
Harald Welteb2edd142021-01-08 23:29:35 +0100468
469# ETSI TS 102 221 Section 11.1.1.3
470def decode_select_response(resp_hex):
471 fixup_fcp_proprietary_tlv_map(FCP_Proprietary_TLV_MAP)
472 resp_hex = resp_hex.upper()
473 # outer layer
474 fcp_base_tlv = TLV(['62'])
475 fcp_base = fcp_base_tlv.parse(resp_hex)
476 # actual FCP
477 fcp_tlv = TLV(FCP_TLV_MAP)
478 fcp = fcp_tlv.parse(fcp_base['62'])
479 # further decode the proprietary information
480 if fcp['A5']:
481 prop_tlv = TLV(FCP_Proprietary_TLV_MAP)
482 prop = prop_tlv.parse(fcp['A5'])
483 fcp['A5'] = tlv_val_interpret(FCP_prorietary_interpreter_map, prop)
484 fcp['A5'] = tlv_key_replace(FCP_Proprietary_TLV_MAP, fcp['A5'])
485 # finally make sure we get human-readable keys in the output dict
486 r = tlv_val_interpret(FCP_interpreter_map, fcp)
487 return tlv_key_replace(FCP_TLV_MAP, r)
488
489
490# TS 102 221 Section 13.1
491class EF_DIR(LinFixedEF):
492 def __init__(self, fid='2f00', sfid=0x1e, name='EF.DIR', desc='Application Directory'):
493 super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={5,54})
494
495 def _decode_record_hex(self, raw_hex_data):
496 raw_hex_data = raw_hex_data.upper()
497 atempl_base_tlv = TLV(['61'])
498 atempl_base = atempl_base_tlv.parse(raw_hex_data)
499 atempl_TLV_MAP = {'4F': 'aid_value', 50:'label'}
500 atempl_tlv = TLV(atempl_TLV_MAP)
501 atempl = atempl_tlv.parse(atempl_base['61'])
502 # FIXME: "All other Dos are according to ISO/IEC 7816-4"
503 return tlv_key_replace(atempl_TLV_MAP, atempl)
504
505# TS 102 221 Section 13.2
506class EF_ICCID(TransparentEF):
507 def __init__(self, fid='2fe2', sfid=0x02, name='EF.ICCID', desc='ICC Identification'):
508 super().__init__(fid, sfid=sfid, name=name, desc=desc, size={10,10})
509
510 def _decode_hex(self, raw_hex):
511 return {'iccid': dec_iccid(raw_hex)}
512
513 def _encode_hex(self, abstract):
514 return enc_iccid(abstract['iccid'])
515
516# TS 102 221 Section 13.3
517class EF_PL(TransRecEF):
518 def __init__(self, fid='2f05', sfid=0x05, name='EF.PL', desc='Preferred Languages'):
519 super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=2, size={2,None})
520
521# TS 102 221 Section 13.4
522class EF_ARR(LinFixedEF):
523 def __init__(self, fid='2f06', sfid=0x06, name='EF.ARR', desc='Access Rule Reference'):
524 super().__init__(fid, sfid=sfid, name=name, desc=desc)
Harald Welte4ae228a2021-05-02 21:29:04 +0200525 # add those commands to the general commands of a TransparentEF
526 self.shell_commands += [self.AddlShellCommands()]
527
528 @staticmethod
529 def flatten(inp:list):
530 """Flatten the somewhat deep/complex/nested data returned from decoder."""
531 def sc_abbreviate(sc):
532 if 'always' in sc:
533 return 'always'
534 elif 'never' in sc:
535 return 'never'
536 elif 'control_reference_template' in sc:
537 return sc['control_reference_template']
538 else:
539 return sc
540
541 by_mode = {}
542 for t in inp:
543 am = t[0]
544 sc = t[1]
545 sc_abbr = sc_abbreviate(sc)
546 if 'access_mode' in am:
547 for m in am['access_mode']:
548 by_mode[m] = sc_abbr
549 elif 'command_header' in am:
550 ins = am['command_header']['INS']
551 if 'CLA' in am['command_header']:
552 cla = am['command_header']['CLA']
553 else:
554 cla = None
555 cmd = ts_102_22x_cmdset.lookup(ins, cla)
556 if cmd:
557 name = cmd.name.lower().replace(' ','_')
558 by_mode[name] = sc_abbr
559 else:
560 raise ValueError
561 else:
562 raise ValueError
563 return by_mode
564
565 def _decode_record_bin(self, raw_bin_data):
566 # we can only guess if we should decode for EF or DF here :(
567 arr_seq = DataObjectSequence('arr', sequence = [AM_DO_EF, SC_DO])
568 dec = arr_seq.decode_multi(raw_bin_data)
569 # we cannot pass the result through flatten() here, as we don't have a related
570 # 'un-flattening' decoder, and hence would be unable to encode :(
571 return dec[0]
572
573 @with_default_category('File-Specific Commands')
574 class AddlShellCommands(CommandSet):
575 def __init__(self):
576 super().__init__()
577
578 @cmd2.with_argparser(LinFixedEF.ShellCommands.read_rec_dec_parser)
579 def do_read_arr_record(self, opts):
580 """Read one EF.ARR record in flattened, human-friendly form."""
581 (data, sw) = self._cmd.rs.read_record_dec(opts.record_nr)
582 data = self._cmd.rs.selected_file.flatten(data)
583 self._cmd.poutput_json(data, opts.oneline)
584
585 @cmd2.with_argparser(LinFixedEF.ShellCommands.read_recs_dec_parser)
586 def do_read_arr_records(self, opts):
587 """Read + decode all EF.ARR records in flattened, human-friendly form."""
588 num_of_rec = self._cmd.rs.selected_file_fcp['file_descriptor']['num_of_rec']
589 # collect all results in list so they are rendered as JSON list when printing
590 data_list = []
591 for recnr in range(1, 1 + num_of_rec):
592 (data, sw) = self._cmd.rs.read_record_dec(recnr)
593 data = self._cmd.rs.selected_file.flatten(data)
594 data_list.append(data)
595 self._cmd.poutput_json(data_list, opts.oneline)
596
Harald Welteb2edd142021-01-08 23:29:35 +0100597
598# TS 102 221 Section 13.6
599class EF_UMPC(TransparentEF):
600 def __init__(self, fid='2f08', sfid=0x08, name='EF.UMPC', desc='UICC Maximum Power Consumption'):
601 super().__init__(fid, sfid=sfid, name=name, desc=desc, size={5,5})
602
603
604
605class CardProfileUICC(CardProfile):
606 def __init__(self):
607 files = [
608 EF_DIR(),
609 EF_ICCID(),
610 EF_PL(),
611 EF_ARR(),
612 # FIXME: DF.CD
613 EF_UMPC(),
614 ]
615 sw = {
616 'Normal': {
617 '9000': 'Normal ending of the command',
618 '91xx': 'Normal ending of the command, with extra information from the proactive UICC containing a command for the terminal',
619 '92xx': 'Normal ending of the command, with extra information concerning an ongoing data transfer session',
620 },
621 'Postponed processing': {
622 '9300': 'SIM Application Toolkit is busy. Command cannot be executed at present, further normal commands are allowed',
623 },
624 'Warnings': {
625 '6200': 'No information given, state of non-volatile memory unchanged',
626 '6281': 'Part of returned data may be corrupted',
627 '6282': 'End of file/record reached before reading Le bytes or unsuccessful search',
628 '6283': 'Selected file invalidated',
629 '6284': 'Selected file in termination state',
630 '62f1': 'More data available',
631 '62f2': 'More data available and proactive command pending',
632 '62f3': 'Response data available',
633 '63f1': 'More data expected',
634 '63f2': 'More data expected and proactive command pending',
635 '63cx': 'Command successful but after using an internal update retry routine X times',
636 },
637 'Execution errors': {
638 '6400': 'No information given, state of non-volatile memory unchanged',
639 '6500': 'No information given, state of non-volatile memory changed',
640 '6581': 'Memory problem',
641 },
642 'Checking errors': {
643 '6700': 'Wrong length',
644 '67xx': 'The interpretation of this status word is command dependent',
645 '6b00': 'Wrong parameter(s) P1-P2',
646 '6d00': 'Instruction code not supported or invalid',
647 '6e00': 'Class not supported',
648 '6f00': 'Technical problem, no precise diagnosis',
649 '6fxx': 'The interpretation of this status word is command dependent',
650 },
651 'Functions in CLA not supported': {
652 '6800': 'No information given',
653 '6881': 'Logical channel not supported',
654 '6882': 'Secure messaging not supported',
655 },
656 'Command not allowed': {
657 '6900': 'No information given',
658 '6981': 'Command incompatible with file structure',
659 '6982': 'Security status not satisfied',
660 '6983': 'Authentication/PIN method blocked',
661 '6984': 'Referenced data invalidated',
662 '6985': 'Conditions of use not satisfied',
663 '6986': 'Command not allowed (no EF selected)',
664 '6989': 'Command not allowed - secure channel - security not satisfied',
665 },
666 'Wrong parameters': {
667 '6a80': 'Incorrect parameters in the data field',
668 '6a81': 'Function not supported',
669 '6a82': 'File not found',
670 '6a83': 'Record not found',
671 '6a84': 'Not enough memory space',
672 '6a86': 'Incorrect parameters P1 to P2',
673 '6a87': 'Lc inconsistent with P1 to P2',
674 '6a88': 'Referenced data not found',
675 },
676 'Application errors': {
677 '9850': 'INCREASE cannot be performed, max value reached',
678 '9862': 'Authentication error, application specific',
679 '9863': 'Security session or association expired',
680 '9864': 'Minimum UICC suspension time is too long',
681 },
682 }
683
Philipp Maierdd2091a2021-03-31 17:28:06 +0200684 super().__init__('UICC', desc='ETSI TS 102 221', files_in_mf=files, sw=sw)