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