From 1432af51500de2b06cfc6861416e8e29128c05fd Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 12 Feb 2024 18:02:17 +0100 Subject: [PATCH] Add terminal_capability command to send TERMINAL CAPABILITY TS 102 221 specifies a TERMINAL CAPABILITY command using which the terminal (Software + hardware talking to the card) can expose their capabilities. This is also used in the eUICC universe to let the eUICC know which features are supported. Change-Id: Iaeb8b4c34524edbb93217bf401e466399626e9b0 --- docs/shell.rst | 11 +++++ pySim/ts_102_221.py | 113 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/docs/shell.rst b/docs/shell.rst index d86f5000..487d22ed 100644 --- a/docs/shell.rst +++ b/docs/shell.rst @@ -473,7 +473,18 @@ sequence including the electrical power down. :module: pySim.ts_102_221 :func: CardProfileUICC.AddlShellCommands.resume_uicc_parser +terminal_capability +~~~~~~~~~~~~~~~~~~~ +This command allows you to perform the TERMINAL CAPABILITY command towards the card. +TS 102 221 specifies the TERMINAL CAPABILITY command using which the +terminal (Software + hardware talking to the card) can expose their +capabilities. This is also used in the eUICC universe to let the eUICC +know which features are supported. + +.. argparse:: + :module: pySim.ts_102_221 + :func: CardProfileUICC.AddlShellCommands.term_cap_parser Linear Fixed EF commands diff --git a/pySim/ts_102_221.py b/pySim/ts_102_221.py index 37b78560..1b40b099 100644 --- a/pySim/ts_102_221.py +++ b/pySim/ts_102_221.py @@ -1,7 +1,7 @@ # coding=utf-8 """Utilities / Functions related to ETSI TS 102 221, the core UICC spec. -(C) 2021 by Harald Welte +(C) 2021-2024 by Harald Welte This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -80,6 +80,10 @@ ts_102_22x_cmdset = CardCommandSet('TS 102 22x', [ CardCommand('RESIZE FILE', 0xD4, ['8X', 'CX']), ]) + +# ETSI TS 102 221 6.2.1 +SupplyVoltageClasses = FlagsEnum(Int8ub, a=0x1, b=0x2, c=0x4, d=0x8, e=0x10) + # ETSI TS 102 221 11.1.1.4.2 class FileSize(BER_TLV_IE, tag=0x80): _construct = GreedyInteger(minlen=2) @@ -131,7 +135,7 @@ class UiccCharacteristics(BER_TLV_IE, tag=0x80): # ETSI TS 102 221 11.1.1.4.6.2 class ApplicationPowerConsumption(BER_TLV_IE, tag=0x81): - _construct = Struct('voltage_class'/Int8ub, + _construct = Struct('voltage_class'/SupplyVoltageClasses, 'power_consumption_ma'/Int8ub, 'reference_freq_100k'/Int8ub) @@ -283,6 +287,33 @@ def tlv_val_interpret(inmap, indata): return val return {d[0]: newval(inmap, d[0], d[1]) for d in indata.items()} +# TS 102 221 11.1.19.2.1 +class TerminalPowerSupply(BER_TLV_IE, tag=0x80): + _construct = Struct('used_supply_voltage_class'/SupplyVoltageClasses, + 'maximum_available_power_supply'/Int8ub, + 'actual_used_freq_100k'/Int8ub) + +# TS 102 221 11.1.19.2.2 +class ExtendedLchanTerminalSupport(BER_TLV_IE, tag=0x81): + _construct = GreedyBytes + +# TS 102 221 11.1.19.2.3 +class AdditionalInterfacesSupport(BER_TLV_IE, tag=0x82): + _construct = FlagsEnum(Int8ub, uicc_clf=0x01) + +# TS 102 221 11.1.19.2.4 + SGP.32 v3.0 3.4.2 RSP Device Capabilities +class AdditionalTermCapEuicc(BER_TLV_IE, tag=0x83): + _construct = FlagsEnum(Int8ub, lui_d=0x01, lpd_d=0x02, lds_d=0x04, lui_e_scws=0x08, + metadata_update_alerting=0x10, + enterprise_capable_device=0x20, + lui_e_e4e=0x40, + lpr=0x80) + +# TS 102 221 11.1.19.2.0 +class TerminalCapability(BER_TLV_IE, tag=0xa9, nested=[TerminalPowerSupply, ExtendedLchanTerminalSupport, + AdditionalInterfacesSupport, AdditionalTermCapEuicc]): + pass + # ETSI TS 102 221 Section 9.2.7 + ISO7816-4 9.3.3/9.3.4 class _AM_DO_DF(DataObject): def __init__(self): @@ -901,3 +932,81 @@ class CardProfileUICC(CardProfile): of the card is required between SUSPEND and RESUME, and only very few non-RESUME commands are permitted between SUSPEND and RESUME. See TS 102 221 Section 11.1.22.""" self._cmd.card._scc.resume_uicc(opts.token) + + term_cap_parser = argparse.ArgumentParser() + # power group + tc_power_grp = term_cap_parser.add_argument_group('Terminal Power Supply') + tc_power_grp.add_argument('--used-supply-voltage-class', type=str, choices=['a','b','c','d','e'], + help='Actual used Supply voltage class') + tc_power_grp.add_argument('--maximum-available-power-supply', type=auto_uint8, + help='Maximum available power supply of the terminal') + tc_power_grp.add_argument('--actual-used-freq-100k', type=auto_uint8, + help='Actual used clock frequency (in units of 100kHz)') + # no separate groups for those two + tc_elc_grp = term_cap_parser.add_argument_group('Extended logical channels terminal support') + tc_elc_grp.add_argument('--extended-logical-channel', action='store_true', + help='Extended Logical Channel supported') + tc_aif_grp = term_cap_parser.add_argument_group('Additional interfaces support') + tc_aif_grp.add_argument('--uicc-clf', action='store_true', + help='Local User Interface in the Device (LUId) supported') + # eUICC group + tc_euicc_grp = term_cap_parser.add_argument_group('Additional Terminal capability indications related to eUICC') + tc_euicc_grp.add_argument('--lui-d', action='store_true', + help='Local User Interface in the Device (LUId) supported') + tc_euicc_grp.add_argument('--lpd-d', action='store_true', + help='Local Profile Download in the Device (LPDd) supported') + tc_euicc_grp.add_argument('--lds-d', action='store_true', + help='Local Discovery Service in the Device (LPDd) supported') + tc_euicc_grp.add_argument('--lui-e-scws', action='store_true', + help='LUIe based on SCWS supported') + tc_euicc_grp.add_argument('--metadata-update-alerting', action='store_true', + help='Metadata update alerting supported') + tc_euicc_grp.add_argument('--enterprise-capable-device', action='store_true', + help='Enterprise Capable Device') + tc_euicc_grp.add_argument('--lui-e-e4e', action='store_true', + help='LUIe using E4E (ENVELOPE tag E4) supported') + tc_euicc_grp.add_argument('--lpr', action='store_true', + help='LPR (LPA Proxy) supported') + + @cmd2.with_argparser(term_cap_parser) + def do_terminal_capability(self, opts): + """Perform the TERMINAL CAPABILITY function. Used to inform the UICC about terminal capability.""" + ps_flags = {} + addl_if_flags = {} + euicc_flags = {} + + opts_dict = vars(opts) + + power_items = ['used_supply_voltage_class', 'maximum_available_power_supply', 'actual_used_freq_100k'] + if any(opts_dict[x] for x in power_items): + if not all(opts_dict[x] for x in power_items): + raise argparse.ArgumentTypeError('If any of the Terminal Power Supply group options are used, all must be specified') + + for k, v in opts_dict.items(): + if k in AdditionalInterfacesSupport._construct.flags.keys(): + addl_if_flags[k] = v + elif k in AdditionalTermCapEuicc._construct.flags.keys(): + euicc_flags[k] = v + elif k in [f.name for f in TerminalPowerSupply._construct.subcons]: + if k == 'used_supply_voltage_class' and v: + v = {v: True} + ps_flags[k] = v + + child_list = [] + if any(x for x in ps_flags.values()): + child_list.append(TerminalPowerSupply(decoded=ps_flags)) + + if opts.extended_logical_channel: + child_list.append(ExtendedLchanTerminalSupport()) + if any(x for x in addl_if_flags.values()): + child_list.append(AdditionalInterfacesSupport(decoded=addl_if_flags)) + if any(x for x in euicc_flags.values()): + child_list.append(AdditionalTermCapEuicc(decoded=euicc_flags)) + + print(child_list) + tc = TerminalCapability(children=child_list) + self.terminal_capability(b2h(tc.to_tlv())) + + def terminal_capability(self, data:Hexstr): + cmd_hex = "80AA0000%02x%s" % (len(data)//2, data) + _rsp_hex, _sw = self._cmd.lchan.scc.send_apdu_checksw(cmd_hex)