laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/34852?usp=email )
Change subject: Initial support for eUICC ...................................................................... Initial support for eUICC This just adds basic support for the ISD-R application and its associated STORE DATA command which is used for the ES10x interfaces between off-card entities and the on-card ISD-R. Change-Id: Ieab37b083e25d3f36c20f6e9ed3e4bdfdd14a42a Closes: OS#5637 --- M docs/shell.rst M pySim-shell.py A pySim/euicc.py M pySim/global_platform.py 4 files changed, 565 insertions(+), 4 deletions(-) Approvals: dexter: Looks good to me, but someone else must approve laforge: Looks good to me, approved Jenkins Builder: Verified diff --git a/docs/shell.rst b/docs/shell.rst index 811ef61..31d73c0 100644 --- a/docs/shell.rst +++ b/docs/shell.rst @@ -805,6 +805,118 @@ :module: pySim.global_platform :func: ADF_SD.AddlShellCommands.get_data_parser + +eUICC ISD-R commands +-------------------- + +These commands are to perform a variety of operations against eUICC for GSMA consumer eSIM. They +implement the so-called ES10a, ES10b and ES10c interface. Basically they perform the tasks that usually would +be done by the LPAd in the UE. + +In order to use those commands, you need to go through the specified steps as documented in GSMA SGP.22: + +* open a new logical channel (and start to use it) +* select the ISD-R application + +:: + + pySIM-shell (00:MF)> open_channel 2 + pySIM-shell (00:MF)> switch_channel 2 + pySIM-shell (02:MF)> select ADF.ISD-R + { + "application_id": "a0000005591010ffffffff8900000100", + "proprietary_data": { + "maximum_length_of_data_field_in_command_message": 255 + }, + "isdr_proprietary_application_template": { + "supported_version_number": "020200" + } + } + pySIM-shell (02:ADF.ISD-R)> + +Once you are at this stage, you can issue the various eUICC related commands against the ISD-R application + + +es10x_store_data +~~~~~~~~~~~~~~~~ + +.. argparse:: + :module: pySim.euicc + :func: ADF_ISDR.AddlShellCommands.es10x_store_data_parser + +get_euicc_configured_addresses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Obtain the configured SM-DP+ and/or SM-DS addresses using the ES10a GetEuiccConfiguredAddresses() function. + +set_default_dp_address +~~~~~~~~~~~~~~~~~~~~~~ + +.. argparse:: + :module: pySim.euicc + :func: ADF_ISDR.AddlShellCommands.set_def_dp_addr_parser + +get_euicc_challenge +~~~~~~~~~~~~~~~~~~~ + +Obtain an authentication challenge from the eUICC using the ES10b GetEUICCChallenge() function. + +get_euicc_info1 +~~~~~~~~~~~~~~~ + +Obtain EUICC Information (1) from the eUICC using the ES10b GetEUICCCInfo() function. + +get_euicc_info2 +~~~~~~~~~~~~~~~ + +Obtain EUICC Information (2) from the eUICC using the ES10b GetEUICCCInfo() function. + +list_notification +~~~~~~~~~~~~~~~~~ + +Obtain the list of notifications from the eUICC using the ES10b ListNotification() function. + +remove_notification_from_list +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. argparse:: + :module: pySim.euicc + :func: ADF_ISDR.AddlShellCommands.rem_notif_parser + +enable_profile +~~~~~~~~~~~~~~ + +.. argparse:: + :module: pySim.euicc + :func: ADF_ISDR.AddlShellCommands.en_prof_parser + +disable_profile +~~~~~~~~~~~~~~~ + +.. argparse:: + :module: pySim.euicc + :func: ADF_ISDR.AddlShellCommands.dis_prof_parser + +delete_profile +~~~~~~~~~~~~~~ + +.. argparse:: + :module: pySim.euicc + :func: ADF_ISDR.AddlShellCommands.del_prof_parser + +get_eid +~~~~~~~ + +Obtain the EID of the eUICC using the ES10c GetEID() function. + +set_nickname +~~~~~~~~~~~~ + +.. argparse:: + :module: pySim.euicc + :func: ADF_ISDR.AddlShellCommands.set_nickname_parser + + cmd2 settable parameters ------------------------ diff --git a/pySim-shell.py b/pySim-shell.py index 022b276..bbb14b2 100755 --- a/pySim-shell.py +++ b/pySim-shell.py @@ -77,6 +77,7 @@ import pySim.ts_31_104 import pySim.ara_m import pySim.global_platform +import pySim.euicc from pySim.card_key_provider import CardKeyProviderCsv, card_key_provider_register, card_key_provider_get_field diff --git a/pySim/euicc.py b/pySim/euicc.py new file mode 100644 index 0000000..d9b7b70 --- /dev/null +++ b/pySim/euicc.py @@ -0,0 +1,431 @@ +# -*- coding: utf-8 -*- + +""" +Various definitions related to GSMA eSIM / eUICC + +Related Specs: GSMA SGP.22, GSMA SGP.02, etc. +""" + +# Copyright (C) 2023 Harald Welte <lafo...@osmocom.org> +# +# 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 +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from pySim.tlv import * +from pySim.construct import * +from construct import Optional as COptional +from construct import * +import argparse +from cmd2 import cmd2, CommandSet, with_default_category +from pySim.commands import SimCardCommands +from pySim.filesystem import CardADF, CardApplication +from pySim.utils import Hexstr, SwHexstr +import pySim.global_platform + +class VersionAdapter(Adapter): + """convert an EUICC Version (3-int array) to a textual representation.""" + + def _decode(self, obj, context, path): + return "%u.%u.%u" % (obj[0], obj[1], obj[2]) + + def _encode(self, obj, context, path): + return [int(x) for x in obj.split('.')] + +VersionType = VersionAdapter(Array(3, Int8ub)) + +# Application Identifiers as defined in GSMA SGP.02 Annex H +AID_ISD_R = "A0000005591010FFFFFFFF8900000100" +AID_ECASD = "A0000005591010FFFFFFFF8900000200" +AID_ISD_P_FILE = "A0000005591010FFFFFFFF8900000D00" +AID_ISD_P_MODULE = "A0000005591010FFFFFFFF8900000E00" + +sw_isdr = { + 'ISD-R': { + '6a80': 'Incorrect values in command data', + '6a82': 'Profile not found', + '6a88': 'Reference data not found', + '6985': 'Conditions of use not satisfied', + } +} + +class SupportedVersionNumber(BER_TLV_IE, tag=0x82): + _construct = GreedyBytes + +class IsdrProprietaryApplicationTemplate(BER_TLV_IE, tag=0xe0, nested=[SupportedVersionNumber]): + # FIXME: lpaeSupport - what kind of tag would it have? + pass + +# GlobalPlatform 2.1.1 Section 9.9.3.1 from pySim/global_platform.py extended with E0 +class FciTemplate(BER_TLV_IE, tag=0x6f, nested=pySim.global_platform.FciTemplateNestedList + + [IsdrProprietaryApplicationTemplate]): + pass + + +# SGP.22 Section 5.7.3: GetEuiccConfiguredAddresses +class DefaultDpAddress(BER_TLV_IE, tag=0x80): + _construct = Utf8Adapter(GreedyBytes) +class RootDsAddress(BER_TLV_IE, tag=0x81): + _construct = Utf8Adapter(GreedyBytes) +class EuiccConfiguredAddresses(BER_TLV_IE, tag=0xbf3c, nested=[DefaultDpAddress, RootDsAddress]): + pass + +# SGP.22 Section 5.7.4: SetDefaultDpAddress +class SetDefaultDpAddrRes(BER_TLV_IE, tag=0x80): + _construct = Enum(Int8ub, ok=0, undefinedError=127) +class SetDefaultDpAddress(BER_TLV_IE, tag=0xbf3f, nested=[DefaultDpAddress, SetDefaultDpAddrRes]): + pass + +# SGP.22 Section 5.7.7: GetEUICCChallenge +class EuiccChallenge(BER_TLV_IE, tag=0x80): + _construct = HexAdapter(Bytes(16)) +class GetEuiccChallenge(BER_TLV_IE, tag=0xbf2e, nested=[EuiccChallenge]): + pass + +# SGP.22 Section 5.7.8: GetEUICCInfo +class SVN(BER_TLV_IE, tag=0x82): + _construct = VersionType +class SubjectKeyIdentifier(BER_TLV_IE, tag=0x81): + _construct = HexAdapter(GreedyBytes) +class SubjectKeyIdentifierSeq(BER_TLV_IE, tag=0x04, nested=[SubjectKeyIdentifier]): + pass +class EuiccCiPkiListForVerification(BER_TLV_IE, tag=0xa9, nested=[SubjectKeyIdentifierSeq]): + pass +class EuiccCiPkiListForSigning(BER_TLV_IE, tag=0xaa, nested=[SubjectKeyIdentifierSeq]): + pass +class EuiccInfo1(BER_TLV_IE, tag=0xbf20, nested=[SVN, EuiccCiPkiListForVerification, EuiccCiPkiListForSigning]): + pass +class ProfileVersion(BER_TLV_IE, tag=0x81): + _construct = VersionType +class EuiccFirmwareVer(BER_TLV_IE, tag=0x83): + _construct = VersionType +class ExtCardResource(BER_TLV_IE, tag=0x84): + _construct = HexAdapter(GreedyBytes) +class UiccCapability(BER_TLV_IE, tag=0x85): + _construct = HexAdapter(GreedyBytes) # FIXME +class TS102241Version(BER_TLV_IE, tag=0x86): + _construct = VersionType +class GlobalPlatformVersion(BER_TLV_IE, tag=0x87): + _construct = VersionType +class RspCapability(BER_TLV_IE, tag=0x88): + _construct = HexAdapter(GreedyBytes) # FIXME +class EuiccCategory(BER_TLV_IE, tag=0x8b): + _construct = Enum(Int8ub, other=0, basicEuicc=1, mediumEuicc=2, contactlessEuicc=3) +class PpVersion(BER_TLV_IE, tag=0x04): + _construct = VersionType +class SsAcreditationNumber(BER_TLV_IE, tag=0x0c): + _construct = Utf8Adapter(GreedyBytes) + +class EuiccInfo2(BER_TLV_IE, tag=0xbf22, nested=[ProfileVersion, SVN, EuiccFirmwareVer, ExtCardResource, + UiccCapability, TS102241Version, GlobalPlatformVersion, + RspCapability, EuiccCiPkiListForVerification, + EuiccCiPkiListForSigning, EuiccCategory, PpVersion, + SsAcreditationNumber]): + pass + + +# SGP.22 Section 5.7.9: ListNotification +class ProfileMgmtOperation(BER_TLV_IE, tag=0x81): + _construct = FlagsEnum(Byte, install=1, enable=2, disable=4, delete=8) +class ListNotificationReq(BER_TLV_IE, tag=0xbf28, nested=[ProfileMgmtOperation]): + pass +class SeqNumber(BER_TLV_IE, tag=0x80): + _construct = GreedyInteger +class NotificationAddress(BER_TLV_IE, tag=0x82): + _construct = Utf8Adapter(GreedyBytes) +class Iccid(BER_TLV_IE, tag=0x5a): + _construct = HexAdapter(GreedyBytes) +class NotificationMetadata(BER_TLV_IE, tag=0xbf2f, nested=[SeqNumber, ProfileMgmtOperation, + NotificationAddress, Iccid]): + pass +class NotificationMetadataList(BER_TLV_IE, tag=0xa0, nested=[NotificationMetadata]): + pass +class ListNotificationsResultError(BER_TLV_IE, tag=0x81): + _construct = Enum(Int8ub, undefinedError=127) +class ListNotificationResp(BER_TLV_IE, tag=0xbf28, nested=[NotificationMetadataList, + ListNotificationsResultError]): + pass + +# SGP.22 Section 5.7.11: RemoveNotificationFromList +class DeleteNotificationStatus(BER_TLV_IE, tag=0x80): + _construct = Enum(Int8ub, ok=0, nothingToDelete=1, undefinedError=127) +class NotificationSentReq(BER_TLV_IE, tag=0xbf30, nested=[SeqNumber]): + pass +class NotificationSentResp(BER_TLV_IE, tag=0xbf30, nested=[DeleteNotificationStatus]): + pass + +# SGP.22 Section 5.7.12: LoadCRL: FIXME +class LoadCRL(BER_TLV_IE, tag=0xbf35, nested=[]): # FIXME + pass + +# SGP.22 Section 5.7.16:: EnableProfile +class RefreshFlag(BER_TLV_IE, tag=0x88): # FIXME + _construct = Int8ub # FIXME +class IsdpAid(BER_TLV_IE, tag=0x4f): + _construct = HexAdapter(GreedyBytes) +class EnableResult(BER_TLV_IE, tag=0x80): + _construct = Enum(Int8ub, ok=0, iccidOrAidNotFound=1, profileNotInDisabledState=2, + disallowedByPolicy=3, wrongProfileReenabling=4, catBusy=5, undefinedError=127) +class EnableProfileReq(BER_TLV_IE, tag=0xbf31, nested=[IsdpAid, Iccid, RefreshFlag]): + pass +class EnableProfileResp(BER_TLV_IE, tag=0xbf31, nested=[EnableResult]): + pass + +# SGP.22 Section 5.7.17 DisableProfile +class DisableResult(BER_TLV_IE, tag=0x80): + _construct = Enum(Int8ub, ok=0, iccidOrAidNotFound=1, profileNotInEnabledState=2, + disallowedByPolicy=3, catBusy=5, undefinedError=127) +class DisableProfileReq(BER_TLV_IE, tag=0xbf32, nested=[IsdpAid, Iccid, RefreshFlag]): + pass +class DisableProfileResp(BER_TLV_IE, tag=0xbf32, nested=[DisableResult]): + pass + +# SGP.22 Section 5.7.18: DeleteProfile +class DeleteResult(BER_TLV_IE, tag=0x80): + _construct = Enum(Int8ub, ok=0, iccidOrAidNotFound=1, profileNotInDisabledState=2, + disallowedByPolicy=3, undefinedError=127) +class DeleteProfileReq(BER_TLV_IE, tag=0xbf33, nested=[IsdpAid, Iccid]): + pass +class DeleteProfileResp(BER_TLV_IE, tag=0xbf33, nested=[DeleteResult]): + pass + +# SGP.22 Section 5.7.20 GetEID +class TagList(BER_TLV_IE, tag=0x5c): + _construct = GreedyRange(Int8ub) +class EidValue(BER_TLV_IE, tag=0x5a): + _construct = HexAdapter(GreedyBytes) +class GetEuiccData(BER_TLV_IE, tag=0xbf3e, nested=[TagList, EidValue]): + pass + +# SGP.22 Section 5.7.21: ES10c SetNickname +class ProfileNickname(BER_TLV_IE, tag=0x8f): + _construct = Utf8Adapter(GreedyBytes) +class SetNicknameReq(BER_TLV_IE, tag=0xbf29, children=[Iccid, ProfileNickname]): + pass +class SetNicknameResult(BER_TLV_IE, tag=0x80): + _construct = Enum(Int8ub, ok=0, iccidNotFound=1, undefinedError=127) +class SetNicknameResp(BER_TLV_IE, tag=0xbf29, children=[SetNicknameResult]): + pass + + +class ADF_ISDR(CardADF): + def __init__(self, aid=AID_ISD_R, name='ADF.ISD-R', fid=None, sfid=None, + desc='ISD-R (Issuer Security Domain Root) Application'): + super().__init__(aid=aid, fid=fid, sfid=sfid, name=name, desc=desc) + self.shell_commands += [self.AddlShellCommands()] + + @staticmethod + def store_data(scc: SimCardCommands, tx_do: Hexstr) -> Tuple[Hexstr, SwHexstr]: + """Perform STORE DATA according to Table 47+48 in Section 5.7.2 of SGP.22. + Only single-block store supported for now.""" + capdu = '%sE29100%02u%s' % (scc.cla4lchan('80'), len(tx_do)//2, tx_do) + return scc._tp.send_apdu_checksw(capdu) + + @staticmethod + def store_data_tlv(scc: SimCardCommands, cmd_do, resp_cls, exp_sw='9000'): + """Transceive STORE DATA APDU with the card, transparently encoding the command data from TLV + and decoding the response data tlv.""" + if cmd_do: + cmd_do_enc = cmd_do.to_tlv() + cmd_do_len = len(cmd_do_enc) + if cmd_do_len > 255: + return ValueError('DO > 255 bytes not supported yet') + else: + cmd_do_enc = b'' + (data, sw) = ADF_ISDR.store_data(scc, b2h(cmd_do_enc)) + if data: + if resp_cls: + resp_do = resp_cls() + resp_do.from_tlv(h2b(data)) + return resp_do + else: + return data + else: + return None + + def decode_select_response(self, data_hex: Hexstr) -> object: + t = FciTemplate() + t.from_tlv(h2b(data_hex)) + d = t.to_dict() + return flatten_dict_lists(d['fci_template']) + + @with_default_category('Application-Specific Commands') + class AddlShellCommands(CommandSet): + + es10x_store_data_parser = argparse.ArgumentParser() + es10x_store_data_parser.add_argument('TX_DO', help='Hexstring of encoded to-be-transmitted DO') + + @cmd2.with_argparser(es10x_store_data_parser) + def do_es10x_store_data(self, opts): + """Perform a raw STORE DATA command as defined for the ES10x eUICC interface.""" + (data, sw) = ADF_ISDR.store_data(self._cmd.lchan.scc, opts.TX_DO) + + def do_get_euicc_configured_addresses(self, opts): + """Perform an ES10a GetEuiccConfiguredAddresses function.""" + eca = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, EuiccConfiguredAddresses(), EuiccConfiguredAddresses) + d = eca.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['euicc_configured_addresses'])) + + set_def_dp_addr_parser = argparse.ArgumentParser() + set_def_dp_addr_parser.add_argument('DP_ADDRESS', help='Default SM-DP+ address as UTF-8 string') + + @cmd2.with_argparser(set_def_dp_addr_parser) + def do_set_default_dp_address(self, opts): + """Perform an ES10a SetDefaultDpAddress function.""" + sdda_cmd = SetDefaultDpAddress(children=[DefaultDpAddress(decoded=opts.DP_ADDRESS)]) + sdda = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, sdda_cmd, SetDefaultDpAddress) + d = sdda.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['set_default_dp_address'])) + + def do_get_euicc_challenge(self, opts): + """Perform an ES10b GetEUICCChallenge function.""" + gec = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, GetEuiccChallenge(), GetEuiccChallenge) + d = gec.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['get_euicc_challenge'])) + + def do_get_euicc_info1(self, opts): + """Perform an ES10b GetEUICCInfo (1) function.""" + ei1 = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, EuiccInfo1(), EuiccInfo1) + d = ei1.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['euicc_info1'])) + + def do_get_euicc_info2(self, opts): + """Perform an ES10b GetEUICCInfo (2) function.""" + ei2 = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, EuiccInfo2(), EuiccInfo2) + d = ei2.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['euicc_info2'])) + + def do_list_notification(self, opts): + """Perform an ES10b ListNotification function.""" + ln = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, ListNotificationReq(), ListNotificationResp) + d = ln.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['list_notification_resp'])) + + rem_notif_parser = argparse.ArgumentParser() + rem_notif_parser.add_argument('SEQ_NR', type=int, help='Sequence Number of the to-be-removed notification') + + @cmd2.with_argparser(rem_notif_parser) + def do_remove_notification_from_list(self, opts): + """Perform an ES10b RemoveNotificationFromList function.""" + rn_cmd = NotificationSentReq(children=[SeqNumber(decoded=opts.SEQ_NR)]) + rn = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, rn_cmd, NotificationSentResp) + d = rn.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['notification_sent_resp'])) + + en_prof_parser = argparse.ArgumentParser() + en_prof_grp = en_prof_parser.add_mutually_exclusive_group() + en_prof_grp.add_argument('--isdp-aid', help='Profile identified by its ISD-P AID') + en_prof_grp.add_argument('--iccid', help='Profile identified by its ICCID') + en_prof_parser.add_argument('--refresh-required', action='store_true', help='whether a REFRESH is required') + + @cmd2.with_argparser(en_prof_parser) + def do_enable_profile(self, opts): + """Perform an ES10c EnableProfile function.""" + ep_cmd_contents = [] + if opts.isdp_aid: + ep_cmd_contents.append(IsdpAid(decoded=opts.isdp_aid)) + if opts.iccid: + ep_cmd_contents.append(Iccid(decoded=opts.iccid)) + if opts.refresh_required: + ep_cmd_contents.append(RefreshFlag()) + ep_cmd = EnableProfileReq(children=ep_cmd_contents) + ep = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, ep_cmd, EnableProfileResp) + d = ep.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['enable_profile_resp'])) + + dis_prof_parser = argparse.ArgumentParser() + dis_prof_grp = dis_prof_parser.add_mutually_exclusive_group() + dis_prof_grp.add_argument('--isdp-aid', help='Profile identified by its ISD-P AID') + dis_prof_grp.add_argument('--iccid', help='Profile identified by its ICCID') + dis_prof_parser.add_argument('--refresh-required', action='store_true', help='whether a REFRESH is required') + + @cmd2.with_argparser(dis_prof_parser) + def do_disable_profile(self, opts): + """Perform an ES10c DisableProfile function.""" + dp_cmd_contents = [] + if opts.isdp_aid: + dp_cmd_contents.append(IsdpAid(decoded=opts.isdp_aid)) + if opts.iccid: + dp_cmd_contents.append(Iccid(decoded=opts.iccid)) + if opts.refresh_required: + dp_cmd_contents.append(RefreshFlag()) + dp_cmd = DisableProfileReq(children=dp_cmd_contents) + dp = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, dp_cmd, DisableProfileResp) + d = dp.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['disable_profile_resp'])) + + del_prof_parser = argparse.ArgumentParser() + del_prof_grp = del_prof_parser.add_mutually_exclusive_group() + del_prof_grp.add_argument('--isdp-aid', help='Profile identified by its ISD-P AID') + del_prof_grp.add_argument('--iccid', help='Profile identified by its ICCID') + + @cmd2.with_argparser(del_prof_parser) + def do_delete_profile(self, opts): + """Perform an ES10c DeleteProfile function.""" + dp_cmd_contents = [] + if opts.isdp_aid: + dp_cmd_contents.append(IsdpAid(decoded=opts.isdp_aid)) + if opts.iccid: + dp_cmd_contents.append(Iccid(decoded=opts.iccid)) + dp_cmd = DeleteProfileReq(children=dp_cmd_contents) + dp = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, dp_cmd, DeleteProfileResp) + d = dp.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['delete_profile_resp'])) + + + def do_get_eid(self, opts): + """Perform an ES10c GetEID function.""" + (data, sw) = ADF_ISDR.store_data(self._cmd.lchan.scc, 'BF3E035C015A') + ged_cmd = GetEuiccData(children=[TagList(decoded=[0x5A])]) + ged = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, ged_cmd, GetEuiccData) + d = ged.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['get_euicc_data'])) + + set_nickname_parser = argparse.ArgumentParser() + set_nickname_parser.add_argument('ICCID', help='ICCID of the profile whose nickname to set') + set_nickname_parser.add_argument('--profile-nickname', help='Nickname of the profile') + + @cmd2.with_argparser(set_nickname_parser) + def do_set_nickname(self, opts): + """Perform an ES10c SetNickname function.""" + nickname = opts.profile_nickname or '' + sn_cmd_contents = [Iccid(decoded=opts.ICCID), ProfileNickname(decoded=nickname)] + sn_cmd = SetNicknameReq(children=sn_cmd_contents) + sn = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, sn_cmd, SetNicknameResp) + d = sn.to_dict() + self._cmd.poutput_json(flatten_dict_lists(d['set_nickname_resp'])) + +class ADF_ECASD(CardADF): + def __init__(self, aid=AID_ECASD, name='ADF.ECASD', fid=None, sfid=None, + desc='ECASD (eUICC Controlling Authority Security Domain) Application'): + super().__init__(aid=aid, fid=fid, sfid=sfid, name=name, desc=desc) + self.shell_commands += [self.AddlShellCommands()] + + def decode_select_response(self, data_hex: Hexstr) -> object: + t = FciTemplate() + t.from_tlv(h2b(data_hex)) + d = t.to_dict() + return flatten_dict_lists(d['fci_template']) + + @with_default_category('Application-Specific Commands') + class AddlShellCommands(CommandSet): + pass + + + +class CardApplicationISDR(CardApplication): + def __init__(self): + super().__init__('ISD-R', adf=ADF_ISDR(), sw=sw_isdr) + +class CardApplicationECASD(CardApplication): + def __init__(self): + super().__init__('ECASD', adf=ADF_ECASD(), sw=sw_isdr) diff --git a/pySim/global_platform.py b/pySim/global_platform.py index bfa2adf..136cccc 100644 --- a/pySim/global_platform.py +++ b/pySim/global_platform.py @@ -174,11 +174,14 @@ MaximumLengthOfDataFieldInCommandMessage]): pass +# explicitly define this list and give it a name so pySim.euicc can reference it +FciTemplateNestedList = [ApplicationID, SecurityDomainManagementData, + ApplicationProductionLifeCycleData, + MaximumLengthOfDataFieldInCommandMessage, + ProprietaryData] + # GlobalPlatform 2.1.1 Section 9.9.3.1 -class FciTemplate(BER_TLV_IE, tag=0x6f, nested=[ApplicationID, SecurityDomainManagementData, - ApplicationProductionLifeCycleData, - MaximumLengthOfDataFieldInCommandMessage, - ProprietaryData]): +class FciTemplate(BER_TLV_IE, tag=0x6f, nested=FciTemplateNestedList): pass class IssuerIdentificationNumber(BER_TLV_IE, tag=0x42): -- To view, visit https://gerrit.osmocom.org/c/pysim/+/34852?usp=email To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings Gerrit-Project: pysim Gerrit-Branch: master Gerrit-Change-Id: Ieab37b083e25d3f36c20f6e9ed3e4bdfdd14a42a Gerrit-Change-Number: 34852 Gerrit-PatchSet: 10 Gerrit-Owner: laforge <lafo...@osmocom.org> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: dexter <pma...@sysmocom.de> Gerrit-Reviewer: laforge <lafo...@osmocom.org> Gerrit-Reviewer: lynxis lazus <lyn...@fe80.eu> Gerrit-CC: fixeria <vyanits...@sysmocom.de> Gerrit-MessageType: merged