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