blob: bb49fe5ebedf501486bda525a24726fdcfa2e7b2 [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
185
186
187def tlv_key_replace(inmap, indata):
188 def newkey(inmap, key):
189 if key in inmap:
190 return inmap[key]
191 else:
192 return key
193 return {newkey(inmap, d[0]): d[1] for d in indata.items()}
194
195def tlv_val_interpret(inmap, indata):
196 def newval(inmap, key, val):
197 if key in inmap:
198 return inmap[key](val)
199 else:
200 return val
201 return {d[0]: newval(inmap, d[0], d[1]) for d in indata.items()}
202
Harald Welte4ae228a2021-05-02 21:29:04 +0200203# ETSI TS 102 221 Section 9.2.7 + ISO7816-4 9.3.3/9.3.4
204
205class _AM_DO_DF(DataObject):
206 def __init__(self):
207 super().__init__('access_mode', 'Access Mode', tag=0x80)
208
209 def from_bytes(self, do:bytes):
210 res = []
211 if len(do) != 1:
212 raise ValueError("We only support single-byte AMF inside AM-DO")
213 amf = do[0]
214 # tables 17..29 and 41..44 of 7816-4
215 if amf & 0x80 == 0:
216 if amf & 0x40:
217 res.append('delete_file')
218 if amf & 0x20:
219 res.append('terminate_df')
220 if amf & 0x10:
221 res.append('activate_file')
222 if amf & 0x08:
223 res.append('deactivate_file')
224 if amf & 0x04:
225 res.append('create_file_df')
226 if amf & 0x02:
227 res.append('create_file_ef')
228 if amf & 0x01:
229 res.append('delete_file_child')
230 self.decoded = res
231
232 def to_bytes(self):
233 val = 0
234 if 'delete_file' in self.decoded:
235 val |= 0x40
236 if 'terminate_df' in self.decoded:
237 val |= 0x20
238 if 'activate_file' in self.decoded:
239 val |= 0x10
240 if 'deactivate_file' in self.decoded:
241 val |= 0x08
242 if 'create_file_df' in self.decoded:
243 val |= 0x04
244 if 'create_file_ef' in self.decoded:
245 val |= 0x02
246 if 'delete_file_child' in self.decoded:
247 val |= 0x01
248 return val.to_bytes(1, 'big')
249
250
251class _AM_DO_EF(DataObject):
252 """ISO7816-4 9.3.2 Table 18 + 9.3.3.1 Table 31"""
253 def __init__(self):
254 super().__init__('access_mode', 'Access Mode', tag=0x80)
255
256 def from_bytes(self, do:bytes):
257 res = []
258 if len(do) != 1:
259 raise ValueError("We only support single-byte AMF inside AM-DO")
260 amf = do[0]
261 # tables 17..29 and 41..44 of 7816-4
262 if amf & 0x80 == 0:
263 if amf & 0x40:
264 res.append('delete_file')
265 if amf & 0x20:
266 res.append('terminate_ef')
267 if amf & 0x10:
268 res.append('activate_file_or_record')
269 if amf & 0x08:
270 res.append('deactivate_file_or_record')
271 if amf & 0x04:
272 res.append('write_append')
273 if amf & 0x02:
274 res.append('update_erase')
275 if amf & 0x01:
276 res.append('read_search_compare')
277 self.decoded = res
278
279 def to_bytes(self):
280 val = 0
281 if 'delete_file' in self.decoded:
282 val |= 0x40
283 if 'terminate_ef' in self.decoded:
284 val |= 0x20
285 if 'activate_file_or_record' in self.decoded:
286 val |= 0x10
287 if 'deactivate_file_or_record' in self.decoded:
288 val |= 0x08
289 if 'write_append' in self.decoded:
290 val |= 0x04
291 if 'update_erase' in self.decoded:
292 val |= 0x02
293 if 'read_search_compare' in self.decoded:
294 val |= 0x01
295 return val.to_bytes(1, 'big')
296
297class _AM_DO_CHDR(DataObject):
298 """Command Header Access Mode DO according to ISO 7816-4 Table 32."""
299 def __init__(self, tag):
300 super().__init__('command_header', 'Command Header Description', tag=tag)
301
302 def from_bytes(self, do:bytes):
303 res = {}
304 i = 0
305 if self.tag & 0x08:
306 res['CLA'] = do[i]
307 i += 1
308 if self.tag & 0x04:
309 res['INS'] = do[i]
310 i += 1
311 if self.tag & 0x02:
312 res['P1'] = do[i]
313 i += 1
314 if self.tag & 0x01:
315 res['P2'] = do[i]
316 i += 1
317 self.decoded = res
318
319 def _compute_tag(self):
320 """Override to encode the tag, as it depends on the value."""
321 tag = 0x80
322 if 'CLA' in self.decoded:
323 tag |= 0x08
324 if 'INS' in self.decoded:
325 tag |= 0x04
326 if 'P1' in self.decoded:
327 tag |= 0x02
328 if 'P2' in self.decoded:
329 tag |= 0x01
330 return tag
331
332 def to_bytes(self):
333 res = bytearray()
334 if 'CLA' in self.decoded:
335 res.append(self.decoded['CLA'])
336 if 'INS' in self.decoded:
337 res.append(self.decoded['INS'])
338 if 'P1' in self.decoded:
339 res.append(self.decoded['P1'])
340 if 'P2' in self.decoded:
341 res.append(self.decoded['P2'])
342 return res
343
344AM_DO_CHDR = DataObjectChoice('am_do_chdr', members=[
345 _AM_DO_CHDR(0x81), _AM_DO_CHDR(0x82), _AM_DO_CHDR(0x83), _AM_DO_CHDR(0x84),
346 _AM_DO_CHDR(0x85), _AM_DO_CHDR(0x86), _AM_DO_CHDR(0x87), _AM_DO_CHDR(0x88),
347 _AM_DO_CHDR(0x89), _AM_DO_CHDR(0x8a), _AM_DO_CHDR(0x8b), _AM_DO_CHDR(0x8c),
348 _AM_DO_CHDR(0x8d), _AM_DO_CHDR(0x8e), _AM_DO_CHDR(0x8f)])
349
350AM_DO_DF = AM_DO_CHDR | _AM_DO_DF()
351AM_DO_EF = AM_DO_CHDR | _AM_DO_EF()
352
353
354# TS 102 221 Section 9.5.1 / Table 9.3
355pin_names = bidict({
356 0x01: 'PIN1',
357 0x02: 'PIN2',
358 0x03: 'PIN3',
359 0x04: 'PIN4',
360 0x05: 'PIN5',
361 0x06: 'PIN6',
362 0x07: 'PIN7',
363 0x08: 'PIN8',
364 0x0a: 'ADM1',
365 0x0b: 'ADM2',
366 0x0c: 'ADM3',
367 0x0d: 'ADM4',
368 0x0e: 'ADM5',
369
370 0x11: 'UNIVERSAL_PIN',
371 0x81: '2PIN1',
372 0x82: '2PIN2',
373 0x83: '2PIN3',
374 0x84: '2PIN4',
375 0x85: '2PIN5',
376 0x86: '2PIN6',
377 0x87: '2PIN7',
378 0x88: '2PIN8',
379 0x8a: 'ADM6',
380 0x8b: 'ADM7',
381 0x8c: 'ADM8',
382 0x8d: 'ADM9',
383 0x8e: 'ADM10',
384 })
385
386class CRT_DO(DataObject):
387 """Control Reference Template as per TS 102 221 9.5.1"""
388 def __init__(self):
389 super().__init__('control_reference_template', 'Control Reference Template', tag=0xA4)
390
391 def from_bytes(self, do: bytes):
392 """Decode a Control Reference Template DO."""
393 if len(do) != 6:
394 raise ValueError('Unsupported CRT DO length: %s', do)
395 if do[0] != 0x83 or do[1] != 0x01:
396 raise ValueError('Unsupported Key Ref Tag or Len in CRT DO %s', do)
397 if do[3:] != b'\x95\x01\x08':
398 raise ValueError('Unsupported Usage Qualifier Tag or Len in CRT DO %s', do)
399 self.encoded = do[0:6]
400 self.decoded = pin_names[do[2]]
401 return do[6:]
402
403 def to_bytes(self):
404 pin = pin_names.inverse[self.decoded]
405 return b'\x83\x01' + pin.to_bytes(1, 'big') + b'\x95\x01\x08'
406
407# ISO7816-4 9.3.3 Table 33
408class SecCondByte_DO(DataObject):
409 def __init__(self, tag=0x9d):
410 super().__init__('security_condition_byte', tag=tag)
411
412 def from_bytes(self, binary:bytes):
413 if len(binary) != 1:
414 raise ValueError
415 inb = binary[0]
416 if inb == 0:
417 cond = 'always'
418 if inb == 0xff:
419 cond = 'never'
420 res = []
421 if inb & 0x80:
422 cond = 'and'
423 else:
424 cond = 'or'
425 if inb & 0x40:
426 res.append('secure_messaging')
427 if inb & 0x20:
428 res.append('external_auth')
429 if inb & 0x10:
430 res.append('user_auth')
431 rd = {'mode': cond }
432 if len(res):
433 rd['conditions'] = res
434 self.decoded = rd
435
436 def to_bytes(self):
437 mode = self.decoded['mode']
438 if mode == 'always':
439 res = 0
440 elif mode == 'never':
441 res = 0xff
442 else:
443 res = 0
444 if mode == 'and':
445 res |= 0x80
446 elif mode == 'or':
447 pass
448 else:
449 raise ValueError('Unknown mode %s' % mode)
450 for c in self.decoded['conditions']:
451 if c == 'secure_messaging':
452 res |= 0x40
453 elif c == 'external_auth':
454 res |= 0x20
455 elif c == 'user_auth':
456 res |= 0x10
457 else:
458 raise ValueError('Unknown condition %s' % c)
459 return res.to_bytes(1, 'big')
460
461Always_DO = TL0_DataObject('always', 'Always', 0x90)
462Never_DO = TL0_DataObject('never', 'Never', 0x97)
463SC_DO = DataObjectChoice('security_condition', 'Security Condition',
464 members=[Always_DO, Never_DO, SecCondByte_DO(), SecCondByte_DO(0x9e), CRT_DO()])
465
Harald Welteb2edd142021-01-08 23:29:35 +0100466
467# ETSI TS 102 221 Section 11.1.1.3
468def decode_select_response(resp_hex):
469 fixup_fcp_proprietary_tlv_map(FCP_Proprietary_TLV_MAP)
470 resp_hex = resp_hex.upper()
471 # outer layer
472 fcp_base_tlv = TLV(['62'])
473 fcp_base = fcp_base_tlv.parse(resp_hex)
474 # actual FCP
475 fcp_tlv = TLV(FCP_TLV_MAP)
476 fcp = fcp_tlv.parse(fcp_base['62'])
477 # further decode the proprietary information
478 if fcp['A5']:
479 prop_tlv = TLV(FCP_Proprietary_TLV_MAP)
480 prop = prop_tlv.parse(fcp['A5'])
481 fcp['A5'] = tlv_val_interpret(FCP_prorietary_interpreter_map, prop)
482 fcp['A5'] = tlv_key_replace(FCP_Proprietary_TLV_MAP, fcp['A5'])
483 # finally make sure we get human-readable keys in the output dict
484 r = tlv_val_interpret(FCP_interpreter_map, fcp)
485 return tlv_key_replace(FCP_TLV_MAP, r)
486
487
488# TS 102 221 Section 13.1
489class EF_DIR(LinFixedEF):
490 def __init__(self, fid='2f00', sfid=0x1e, name='EF.DIR', desc='Application Directory'):
491 super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len={5,54})
492
493 def _decode_record_hex(self, raw_hex_data):
494 raw_hex_data = raw_hex_data.upper()
495 atempl_base_tlv = TLV(['61'])
496 atempl_base = atempl_base_tlv.parse(raw_hex_data)
497 atempl_TLV_MAP = {'4F': 'aid_value', 50:'label'}
498 atempl_tlv = TLV(atempl_TLV_MAP)
499 atempl = atempl_tlv.parse(atempl_base['61'])
500 # FIXME: "All other Dos are according to ISO/IEC 7816-4"
501 return tlv_key_replace(atempl_TLV_MAP, atempl)
502
503# TS 102 221 Section 13.2
504class EF_ICCID(TransparentEF):
505 def __init__(self, fid='2fe2', sfid=0x02, name='EF.ICCID', desc='ICC Identification'):
506 super().__init__(fid, sfid=sfid, name=name, desc=desc, size={10,10})
507
508 def _decode_hex(self, raw_hex):
509 return {'iccid': dec_iccid(raw_hex)}
510
511 def _encode_hex(self, abstract):
512 return enc_iccid(abstract['iccid'])
513
514# TS 102 221 Section 13.3
515class EF_PL(TransRecEF):
516 def __init__(self, fid='2f05', sfid=0x05, name='EF.PL', desc='Preferred Languages'):
517 super().__init__(fid, sfid=sfid, name=name, desc=desc, rec_len=2, size={2,None})
518
519# TS 102 221 Section 13.4
520class EF_ARR(LinFixedEF):
521 def __init__(self, fid='2f06', sfid=0x06, name='EF.ARR', desc='Access Rule Reference'):
522 super().__init__(fid, sfid=sfid, name=name, desc=desc)
Harald Welte4ae228a2021-05-02 21:29:04 +0200523 # add those commands to the general commands of a TransparentEF
524 self.shell_commands += [self.AddlShellCommands()]
525
526 @staticmethod
527 def flatten(inp:list):
528 """Flatten the somewhat deep/complex/nested data returned from decoder."""
529 def sc_abbreviate(sc):
530 if 'always' in sc:
531 return 'always'
532 elif 'never' in sc:
533 return 'never'
534 elif 'control_reference_template' in sc:
535 return sc['control_reference_template']
536 else:
537 return sc
538
539 by_mode = {}
540 for t in inp:
541 am = t[0]
542 sc = t[1]
543 sc_abbr = sc_abbreviate(sc)
544 if 'access_mode' in am:
545 for m in am['access_mode']:
546 by_mode[m] = sc_abbr
547 elif 'command_header' in am:
548 ins = am['command_header']['INS']
549 if 'CLA' in am['command_header']:
550 cla = am['command_header']['CLA']
551 else:
552 cla = None
553 cmd = ts_102_22x_cmdset.lookup(ins, cla)
554 if cmd:
555 name = cmd.name.lower().replace(' ','_')
556 by_mode[name] = sc_abbr
557 else:
558 raise ValueError
559 else:
560 raise ValueError
561 return by_mode
562
563 def _decode_record_bin(self, raw_bin_data):
564 # we can only guess if we should decode for EF or DF here :(
565 arr_seq = DataObjectSequence('arr', sequence = [AM_DO_EF, SC_DO])
566 dec = arr_seq.decode_multi(raw_bin_data)
567 # we cannot pass the result through flatten() here, as we don't have a related
568 # 'un-flattening' decoder, and hence would be unable to encode :(
569 return dec[0]
570
571 @with_default_category('File-Specific Commands')
572 class AddlShellCommands(CommandSet):
573 def __init__(self):
574 super().__init__()
575
576 @cmd2.with_argparser(LinFixedEF.ShellCommands.read_rec_dec_parser)
577 def do_read_arr_record(self, opts):
578 """Read one EF.ARR record in flattened, human-friendly form."""
579 (data, sw) = self._cmd.rs.read_record_dec(opts.record_nr)
580 data = self._cmd.rs.selected_file.flatten(data)
581 self._cmd.poutput_json(data, opts.oneline)
582
583 @cmd2.with_argparser(LinFixedEF.ShellCommands.read_recs_dec_parser)
584 def do_read_arr_records(self, opts):
585 """Read + decode all EF.ARR records in flattened, human-friendly form."""
586 num_of_rec = self._cmd.rs.selected_file_fcp['file_descriptor']['num_of_rec']
587 # collect all results in list so they are rendered as JSON list when printing
588 data_list = []
589 for recnr in range(1, 1 + num_of_rec):
590 (data, sw) = self._cmd.rs.read_record_dec(recnr)
591 data = self._cmd.rs.selected_file.flatten(data)
592 data_list.append(data)
593 self._cmd.poutput_json(data_list, opts.oneline)
594
Harald Welteb2edd142021-01-08 23:29:35 +0100595
596# TS 102 221 Section 13.6
597class EF_UMPC(TransparentEF):
598 def __init__(self, fid='2f08', sfid=0x08, name='EF.UMPC', desc='UICC Maximum Power Consumption'):
599 super().__init__(fid, sfid=sfid, name=name, desc=desc, size={5,5})
600
601
602
603class CardProfileUICC(CardProfile):
604 def __init__(self):
605 files = [
606 EF_DIR(),
607 EF_ICCID(),
608 EF_PL(),
609 EF_ARR(),
610 # FIXME: DF.CD
611 EF_UMPC(),
612 ]
613 sw = {
614 'Normal': {
615 '9000': 'Normal ending of the command',
616 '91xx': 'Normal ending of the command, with extra information from the proactive UICC containing a command for the terminal',
617 '92xx': 'Normal ending of the command, with extra information concerning an ongoing data transfer session',
618 },
619 'Postponed processing': {
620 '9300': 'SIM Application Toolkit is busy. Command cannot be executed at present, further normal commands are allowed',
621 },
622 'Warnings': {
623 '6200': 'No information given, state of non-volatile memory unchanged',
624 '6281': 'Part of returned data may be corrupted',
625 '6282': 'End of file/record reached before reading Le bytes or unsuccessful search',
626 '6283': 'Selected file invalidated',
627 '6284': 'Selected file in termination state',
628 '62f1': 'More data available',
629 '62f2': 'More data available and proactive command pending',
630 '62f3': 'Response data available',
631 '63f1': 'More data expected',
632 '63f2': 'More data expected and proactive command pending',
633 '63cx': 'Command successful but after using an internal update retry routine X times',
634 },
635 'Execution errors': {
636 '6400': 'No information given, state of non-volatile memory unchanged',
637 '6500': 'No information given, state of non-volatile memory changed',
638 '6581': 'Memory problem',
639 },
640 'Checking errors': {
641 '6700': 'Wrong length',
642 '67xx': 'The interpretation of this status word is command dependent',
643 '6b00': 'Wrong parameter(s) P1-P2',
644 '6d00': 'Instruction code not supported or invalid',
645 '6e00': 'Class not supported',
646 '6f00': 'Technical problem, no precise diagnosis',
647 '6fxx': 'The interpretation of this status word is command dependent',
648 },
649 'Functions in CLA not supported': {
650 '6800': 'No information given',
651 '6881': 'Logical channel not supported',
652 '6882': 'Secure messaging not supported',
653 },
654 'Command not allowed': {
655 '6900': 'No information given',
656 '6981': 'Command incompatible with file structure',
657 '6982': 'Security status not satisfied',
658 '6983': 'Authentication/PIN method blocked',
659 '6984': 'Referenced data invalidated',
660 '6985': 'Conditions of use not satisfied',
661 '6986': 'Command not allowed (no EF selected)',
662 '6989': 'Command not allowed - secure channel - security not satisfied',
663 },
664 'Wrong parameters': {
665 '6a80': 'Incorrect parameters in the data field',
666 '6a81': 'Function not supported',
667 '6a82': 'File not found',
668 '6a83': 'Record not found',
669 '6a84': 'Not enough memory space',
670 '6a86': 'Incorrect parameters P1 to P2',
671 '6a87': 'Lc inconsistent with P1 to P2',
672 '6a88': 'Referenced data not found',
673 },
674 'Application errors': {
675 '9850': 'INCREASE cannot be performed, max value reached',
676 '9862': 'Authentication error, application specific',
677 '9863': 'Security session or association expired',
678 '9864': 'Minimum UICC suspension time is too long',
679 },
680 }
681
Philipp Maierdd2091a2021-03-31 17:28:06 +0200682 super().__init__('UICC', desc='ETSI TS 102 221', files_in_mf=files, sw=sw)