blob: 19b1c57feb82b783663eb92583352040bee9a15a [file] [log] [blame]
Harald Welte268a2022023-10-22 13:12:11 +02001# -*- coding: utf-8 -*-
2
3"""
4Various definitions related to GSMA eSIM / eUICC
5
6Related Specs: GSMA SGP.22, GSMA SGP.02, etc.
7"""
8
9# Copyright (C) 2023 Harald Welte <laforge@osmocom.org>
10#
11# This program is free software: you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation, either version 2 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program. If not, see <http://www.gnu.org/licenses/>.
23
24from pySim.tlv import *
25from pySim.construct import *
26from construct import Optional as COptional
27from construct import *
28import argparse
29from cmd2 import cmd2, CommandSet, with_default_category
30from pySim.commands import SimCardCommands
31from pySim.filesystem import CardADF, CardApplication
32from pySim.utils import Hexstr, SwHexstr
33import pySim.global_platform
34
35class VersionAdapter(Adapter):
36 """convert an EUICC Version (3-int array) to a textual representation."""
37
38 def _decode(self, obj, context, path):
39 return "%u.%u.%u" % (obj[0], obj[1], obj[2])
40
41 def _encode(self, obj, context, path):
42 return [int(x) for x in obj.split('.')]
43
44VersionType = VersionAdapter(Array(3, Int8ub))
45
46# Application Identifiers as defined in GSMA SGP.02 Annex H
47AID_ISD_R = "A0000005591010FFFFFFFF8900000100"
48AID_ECASD = "A0000005591010FFFFFFFF8900000200"
49AID_ISD_P_FILE = "A0000005591010FFFFFFFF8900000D00"
50AID_ISD_P_MODULE = "A0000005591010FFFFFFFF8900000E00"
51
52sw_isdr = {
53 'ISD-R': {
54 '6a80': 'Incorrect values in command data',
55 '6a82': 'Profile not found',
56 '6a88': 'Reference data not found',
57 '6985': 'Conditions of use not satisfied',
58 }
59}
60
61class SupportedVersionNumber(BER_TLV_IE, tag=0x82):
62 _construct = GreedyBytes
63
64class IsdrProprietaryApplicationTemplate(BER_TLV_IE, tag=0xe0, nested=[SupportedVersionNumber]):
65 # FIXME: lpaeSupport - what kind of tag would it have?
66 pass
67
68# GlobalPlatform 2.1.1 Section 9.9.3.1 from pySim/global_platform.py extended with E0
69class FciTemplate(BER_TLV_IE, tag=0x6f, nested=pySim.global_platform.FciTemplateNestedList +
70 [IsdrProprietaryApplicationTemplate]):
71 pass
72
73
74# SGP.22 Section 5.7.3: GetEuiccConfiguredAddresses
75class DefaultDpAddress(BER_TLV_IE, tag=0x80):
76 _construct = Utf8Adapter(GreedyBytes)
77class RootDsAddress(BER_TLV_IE, tag=0x81):
78 _construct = Utf8Adapter(GreedyBytes)
79class EuiccConfiguredAddresses(BER_TLV_IE, tag=0xbf3c, nested=[DefaultDpAddress, RootDsAddress]):
80 pass
81
82# SGP.22 Section 5.7.4: SetDefaultDpAddress
83class SetDefaultDpAddrRes(BER_TLV_IE, tag=0x80):
84 _construct = Enum(Int8ub, ok=0, undefinedError=127)
85class SetDefaultDpAddress(BER_TLV_IE, tag=0xbf3f, nested=[DefaultDpAddress, SetDefaultDpAddrRes]):
86 pass
87
88# SGP.22 Section 5.7.7: GetEUICCChallenge
89class EuiccChallenge(BER_TLV_IE, tag=0x80):
90 _construct = HexAdapter(Bytes(16))
91class GetEuiccChallenge(BER_TLV_IE, tag=0xbf2e, nested=[EuiccChallenge]):
92 pass
93
94# SGP.22 Section 5.7.8: GetEUICCInfo
95class SVN(BER_TLV_IE, tag=0x82):
96 _construct = VersionType
97class SubjectKeyIdentifier(BER_TLV_IE, tag=0x81):
98 _construct = HexAdapter(GreedyBytes)
99class SubjectKeyIdentifierSeq(BER_TLV_IE, tag=0x04, nested=[SubjectKeyIdentifier]):
100 pass
101class EuiccCiPkiListForVerification(BER_TLV_IE, tag=0xa9, nested=[SubjectKeyIdentifierSeq]):
102 pass
103class EuiccCiPkiListForSigning(BER_TLV_IE, tag=0xaa, nested=[SubjectKeyIdentifierSeq]):
104 pass
105class EuiccInfo1(BER_TLV_IE, tag=0xbf20, nested=[SVN, EuiccCiPkiListForVerification, EuiccCiPkiListForSigning]):
106 pass
107class ProfileVersion(BER_TLV_IE, tag=0x81):
108 _construct = VersionType
109class EuiccFirmwareVer(BER_TLV_IE, tag=0x83):
110 _construct = VersionType
111class ExtCardResource(BER_TLV_IE, tag=0x84):
112 _construct = HexAdapter(GreedyBytes)
113class UiccCapability(BER_TLV_IE, tag=0x85):
114 _construct = HexAdapter(GreedyBytes) # FIXME
115class TS102241Version(BER_TLV_IE, tag=0x86):
116 _construct = VersionType
117class GlobalPlatformVersion(BER_TLV_IE, tag=0x87):
118 _construct = VersionType
119class RspCapability(BER_TLV_IE, tag=0x88):
120 _construct = HexAdapter(GreedyBytes) # FIXME
121class EuiccCategory(BER_TLV_IE, tag=0x8b):
122 _construct = Enum(Int8ub, other=0, basicEuicc=1, mediumEuicc=2, contactlessEuicc=3)
123class PpVersion(BER_TLV_IE, tag=0x04):
124 _construct = VersionType
125class SsAcreditationNumber(BER_TLV_IE, tag=0x0c):
126 _construct = Utf8Adapter(GreedyBytes)
Harald Weltecbc0bdf2023-10-24 12:11:04 +0200127class IpaMode(BER_TLV_IE, tag=0x90): # see SGP.32 v1.0
128 _construct = Enum(Int8ub, ipad=0, ipea=1)
129class IotVersion(BER_TLV_IE, tag=0x80): # see SGP.32 v1.0
130 _construct = VersionType
131class IotVersionSeq(BER_TLV_IE, tag=0xa0, nested=[IotVersion]): # see SGP.32 v1.0
132 pass
133class IotSpecificInfo(BER_TLV_IE, tag=0x94, nested=[IotVersionSeq]): # see SGP.32 v1.0
134 pass
Harald Welte268a2022023-10-22 13:12:11 +0200135class EuiccInfo2(BER_TLV_IE, tag=0xbf22, nested=[ProfileVersion, SVN, EuiccFirmwareVer, ExtCardResource,
136 UiccCapability, TS102241Version, GlobalPlatformVersion,
137 RspCapability, EuiccCiPkiListForVerification,
138 EuiccCiPkiListForSigning, EuiccCategory, PpVersion,
Harald Weltecbc0bdf2023-10-24 12:11:04 +0200139 SsAcreditationNumber, IpaMode, IotSpecificInfo]):
Harald Welte268a2022023-10-22 13:12:11 +0200140 pass
141
Harald Welte268a2022023-10-22 13:12:11 +0200142# SGP.22 Section 5.7.9: ListNotification
143class ProfileMgmtOperation(BER_TLV_IE, tag=0x81):
144 _construct = FlagsEnum(Byte, install=1, enable=2, disable=4, delete=8)
145class ListNotificationReq(BER_TLV_IE, tag=0xbf28, nested=[ProfileMgmtOperation]):
146 pass
147class SeqNumber(BER_TLV_IE, tag=0x80):
148 _construct = GreedyInteger
149class NotificationAddress(BER_TLV_IE, tag=0x82):
150 _construct = Utf8Adapter(GreedyBytes)
151class Iccid(BER_TLV_IE, tag=0x5a):
Harald Welte2b6dedd2023-12-07 14:21:19 +0100152 _construct = BcdAdapter(GreedyBytes)
Harald Welte268a2022023-10-22 13:12:11 +0200153class NotificationMetadata(BER_TLV_IE, tag=0xbf2f, nested=[SeqNumber, ProfileMgmtOperation,
154 NotificationAddress, Iccid]):
155 pass
156class NotificationMetadataList(BER_TLV_IE, tag=0xa0, nested=[NotificationMetadata]):
157 pass
158class ListNotificationsResultError(BER_TLV_IE, tag=0x81):
159 _construct = Enum(Int8ub, undefinedError=127)
160class ListNotificationResp(BER_TLV_IE, tag=0xbf28, nested=[NotificationMetadataList,
161 ListNotificationsResultError]):
162 pass
163
164# SGP.22 Section 5.7.11: RemoveNotificationFromList
165class DeleteNotificationStatus(BER_TLV_IE, tag=0x80):
166 _construct = Enum(Int8ub, ok=0, nothingToDelete=1, undefinedError=127)
167class NotificationSentReq(BER_TLV_IE, tag=0xbf30, nested=[SeqNumber]):
168 pass
169class NotificationSentResp(BER_TLV_IE, tag=0xbf30, nested=[DeleteNotificationStatus]):
170 pass
171
172# SGP.22 Section 5.7.12: LoadCRL: FIXME
173class LoadCRL(BER_TLV_IE, tag=0xbf35, nested=[]): # FIXME
174 pass
175
Harald Welte884eb552023-10-24 11:03:50 +0200176# SGP.22 Section 5.7.15: GetProfilesInfo
177class TagList(BER_TLV_IE, tag=0x5c):
178 _construct = GreedyRange(Int8ub) # FIXME: tags could be multi-byte
179class ProfileInfoListReq(BER_TLV_IE, tag=0xbf2d, nested=[TagList]): # FIXME: SearchCriteria
180 pass
181class IsdpAid(BER_TLV_IE, tag=0x4f):
182 _construct = HexAdapter(GreedyBytes)
183class ProfileState(BER_TLV_IE, tag=0x9f70):
184 _construct = Enum(Int8ub, disabled=0, enabled=1)
185class ProfileNickname(BER_TLV_IE, tag=0x90):
186 _construct = Utf8Adapter(GreedyBytes)
187class ServiceProviderName(BER_TLV_IE, tag=0x91):
188 _construct = Utf8Adapter(GreedyBytes)
189class ProfileName(BER_TLV_IE, tag=0x92):
190 _construct = Utf8Adapter(GreedyBytes)
191class IconType(BER_TLV_IE, tag=0x93):
192 _construct = Enum(Int8ub, jpg=0, png=1)
193class Icon(BER_TLV_IE, tag=0x94):
194 _construct = GreedyBytes
195class ProfileClass(BER_TLV_IE, tag=0x95):
196 _construct = Enum(Int8ub, test=0, provisioning=1, operational=2)
197class ProfileInfo(BER_TLV_IE, tag=0xe3, nested=[Iccid, IsdpAid, ProfileState, ProfileNickname,
198 ServiceProviderName, ProfileName, IconType, Icon,
199 ProfileClass]): # FIXME: more IEs
200 pass
201class ProfileInfoSeq(BER_TLV_IE, tag=0xa0, nested=[ProfileInfo]):
202 pass
203class ProfileInfoListError(BER_TLV_IE, tag=0x81):
204 _construct = Enum(Int8ub, incorrectInputValues=1, undefinedError=2)
205class ProfileInfoListResp(BER_TLV_IE, tag=0xbf2d, nested=[ProfileInfoSeq, ProfileInfoListError]):
206 pass
207
Harald Welte268a2022023-10-22 13:12:11 +0200208# SGP.22 Section 5.7.16:: EnableProfile
Harald Welte008cdf42023-12-07 00:03:47 +0100209class RefreshFlag(BER_TLV_IE, tag=0x81): # FIXME
Harald Welte268a2022023-10-22 13:12:11 +0200210 _construct = Int8ub # FIXME
Harald Welte268a2022023-10-22 13:12:11 +0200211class EnableResult(BER_TLV_IE, tag=0x80):
212 _construct = Enum(Int8ub, ok=0, iccidOrAidNotFound=1, profileNotInDisabledState=2,
213 disallowedByPolicy=3, wrongProfileReenabling=4, catBusy=5, undefinedError=127)
Harald Welte008cdf42023-12-07 00:03:47 +0100214class ProfileIdentifier(BER_TLV_IE, tag=0xa0, nested=[IsdpAid, Iccid]):
215 pass
216class EnableProfileReq(BER_TLV_IE, tag=0xbf31, nested=[ProfileIdentifier, RefreshFlag]):
Harald Welte268a2022023-10-22 13:12:11 +0200217 pass
218class EnableProfileResp(BER_TLV_IE, tag=0xbf31, nested=[EnableResult]):
219 pass
220
221# SGP.22 Section 5.7.17 DisableProfile
222class DisableResult(BER_TLV_IE, tag=0x80):
223 _construct = Enum(Int8ub, ok=0, iccidOrAidNotFound=1, profileNotInEnabledState=2,
224 disallowedByPolicy=3, catBusy=5, undefinedError=127)
Harald Welte008cdf42023-12-07 00:03:47 +0100225class DisableProfileReq(BER_TLV_IE, tag=0xbf32, nested=[ProfileIdentifier, RefreshFlag]):
Harald Welte268a2022023-10-22 13:12:11 +0200226 pass
227class DisableProfileResp(BER_TLV_IE, tag=0xbf32, nested=[DisableResult]):
228 pass
229
230# SGP.22 Section 5.7.18: DeleteProfile
231class DeleteResult(BER_TLV_IE, tag=0x80):
232 _construct = Enum(Int8ub, ok=0, iccidOrAidNotFound=1, profileNotInDisabledState=2,
233 disallowedByPolicy=3, undefinedError=127)
Harald Welte008cdf42023-12-07 00:03:47 +0100234class DeleteProfileReq(BER_TLV_IE, tag=0xbf33, nested=[ProfileIdentifier]):
Harald Welte268a2022023-10-22 13:12:11 +0200235 pass
236class DeleteProfileResp(BER_TLV_IE, tag=0xbf33, nested=[DeleteResult]):
237 pass
238
239# SGP.22 Section 5.7.20 GetEID
Harald Welte268a2022023-10-22 13:12:11 +0200240class EidValue(BER_TLV_IE, tag=0x5a):
241 _construct = HexAdapter(GreedyBytes)
242class GetEuiccData(BER_TLV_IE, tag=0xbf3e, nested=[TagList, EidValue]):
243 pass
244
245# SGP.22 Section 5.7.21: ES10c SetNickname
Harald Welte884eb552023-10-24 11:03:50 +0200246class SnrProfileNickname(BER_TLV_IE, tag=0x8f):
Harald Welte268a2022023-10-22 13:12:11 +0200247 _construct = Utf8Adapter(GreedyBytes)
Harald Welteb582c3c2023-12-07 23:29:11 +0100248class SetNicknameReq(BER_TLV_IE, tag=0xbf29, nested=[Iccid, SnrProfileNickname]):
Harald Welte268a2022023-10-22 13:12:11 +0200249 pass
250class SetNicknameResult(BER_TLV_IE, tag=0x80):
251 _construct = Enum(Int8ub, ok=0, iccidNotFound=1, undefinedError=127)
Harald Welteb582c3c2023-12-07 23:29:11 +0100252class SetNicknameResp(BER_TLV_IE, tag=0xbf29, nested=[SetNicknameResult]):
Harald Welte268a2022023-10-22 13:12:11 +0200253 pass
254
Harald Weltecbc0bdf2023-10-24 12:11:04 +0200255# SGP.32 Section 5.9.10: ES10b: GetCerts
256class GetCertsReq(BER_TLV_IE, tag=0xbf56):
257 pass
258class EumCertificate(BER_TLV_IE, tag=0xa5):
259 _construct = GreedyBytes
260class EuiccCertificate(BER_TLV_IE, tag=0xa6):
261 _construct = GreedyBytes
262class GetCertsError(BER_TLV_IE, tag=0x80):
263 _construct = Enum(Int8ub, invalidCiPKId=1, undefinedError=127)
264class GetCertsResp(BER_TLV_IE, tag=0xbf56, nested=[EumCertificate, EuiccCertificate, GetCertsError]):
265 pass
266
267# SGP.32 Section 5.9.18: ES10b: GetEimConfigurationData
268class EimId(BER_TLV_IE, tag=0x80):
269 _construct = Utf8Adapter(GreedyBytes)
270class EimFqdn(BER_TLV_IE, tag=0x81):
271 _construct = Utf8Adapter(GreedyBytes)
272class EimIdType(BER_TLV_IE, tag=0x82):
273 _construct = Enum(Int8ub, eimIdTypeOid=1, eimIdTypeFqdn=2, eimIdTypeProprietary=3)
274class CounterValue(BER_TLV_IE, tag=0x83):
275 _construct = GreedyInteger
276class AssociationToken(BER_TLV_IE, tag=0x84):
277 _construct = GreedyInteger
278class EimSupportedProtocol(BER_TLV_IE, tag=0x87):
279 _construct = Enum(Int8ub, eimRetrieveHttps=0, eimRetrieveCoaps=1, eimInjectHttps=2, eimInjectCoaps=3,
280 eimProprietary=4)
281# FIXME: eimPublicKeyData, trustedPublicKeyDataTls, euiccCiPKId
282class EimConfigurationData(BER_TLV_IE, tag=0x80, nested=[EimId, EimFqdn, EimIdType, CounterValue,
283 AssociationToken, EimSupportedProtocol]):
284 pass
285class EimConfigurationDataSeq(BER_TLV_IE, tag=0xa0, nested=[EimConfigurationData]):
286 pass
287class GetEimConfigurationData(BER_TLV_IE, tag=0xbf55, nested=[EimConfigurationDataSeq]):
288 pass
Harald Welte268a2022023-10-22 13:12:11 +0200289
290class ADF_ISDR(CardADF):
291 def __init__(self, aid=AID_ISD_R, name='ADF.ISD-R', fid=None, sfid=None,
292 desc='ISD-R (Issuer Security Domain Root) Application'):
293 super().__init__(aid=aid, fid=fid, sfid=sfid, name=name, desc=desc)
294 self.shell_commands += [self.AddlShellCommands()]
295
296 @staticmethod
297 def store_data(scc: SimCardCommands, tx_do: Hexstr) -> Tuple[Hexstr, SwHexstr]:
298 """Perform STORE DATA according to Table 47+48 in Section 5.7.2 of SGP.22.
299 Only single-block store supported for now."""
Harald Weltec20d4422023-12-07 22:46:40 +0100300 capdu = '%sE29100%02x%s' % (scc.cla4lchan('80'), len(tx_do)//2, tx_do)
Harald Welte268a2022023-10-22 13:12:11 +0200301 return scc._tp.send_apdu_checksw(capdu)
302
303 @staticmethod
304 def store_data_tlv(scc: SimCardCommands, cmd_do, resp_cls, exp_sw='9000'):
305 """Transceive STORE DATA APDU with the card, transparently encoding the command data from TLV
306 and decoding the response data tlv."""
307 if cmd_do:
308 cmd_do_enc = cmd_do.to_tlv()
309 cmd_do_len = len(cmd_do_enc)
310 if cmd_do_len > 255:
311 return ValueError('DO > 255 bytes not supported yet')
312 else:
313 cmd_do_enc = b''
314 (data, sw) = ADF_ISDR.store_data(scc, b2h(cmd_do_enc))
315 if data:
316 if resp_cls:
317 resp_do = resp_cls()
318 resp_do.from_tlv(h2b(data))
319 return resp_do
320 else:
321 return data
322 else:
323 return None
324
325 def decode_select_response(self, data_hex: Hexstr) -> object:
326 t = FciTemplate()
327 t.from_tlv(h2b(data_hex))
328 d = t.to_dict()
329 return flatten_dict_lists(d['fci_template'])
330
331 @with_default_category('Application-Specific Commands')
332 class AddlShellCommands(CommandSet):
333
334 es10x_store_data_parser = argparse.ArgumentParser()
335 es10x_store_data_parser.add_argument('TX_DO', help='Hexstring of encoded to-be-transmitted DO')
336
337 @cmd2.with_argparser(es10x_store_data_parser)
338 def do_es10x_store_data(self, opts):
339 """Perform a raw STORE DATA command as defined for the ES10x eUICC interface."""
340 (data, sw) = ADF_ISDR.store_data(self._cmd.lchan.scc, opts.TX_DO)
341
342 def do_get_euicc_configured_addresses(self, opts):
343 """Perform an ES10a GetEuiccConfiguredAddresses function."""
344 eca = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, EuiccConfiguredAddresses(), EuiccConfiguredAddresses)
345 d = eca.to_dict()
346 self._cmd.poutput_json(flatten_dict_lists(d['euicc_configured_addresses']))
347
348 set_def_dp_addr_parser = argparse.ArgumentParser()
349 set_def_dp_addr_parser.add_argument('DP_ADDRESS', help='Default SM-DP+ address as UTF-8 string')
350
351 @cmd2.with_argparser(set_def_dp_addr_parser)
352 def do_set_default_dp_address(self, opts):
353 """Perform an ES10a SetDefaultDpAddress function."""
354 sdda_cmd = SetDefaultDpAddress(children=[DefaultDpAddress(decoded=opts.DP_ADDRESS)])
355 sdda = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, sdda_cmd, SetDefaultDpAddress)
356 d = sdda.to_dict()
357 self._cmd.poutput_json(flatten_dict_lists(d['set_default_dp_address']))
358
359 def do_get_euicc_challenge(self, opts):
360 """Perform an ES10b GetEUICCChallenge function."""
361 gec = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, GetEuiccChallenge(), GetEuiccChallenge)
362 d = gec.to_dict()
363 self._cmd.poutput_json(flatten_dict_lists(d['get_euicc_challenge']))
364
365 def do_get_euicc_info1(self, opts):
366 """Perform an ES10b GetEUICCInfo (1) function."""
367 ei1 = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, EuiccInfo1(), EuiccInfo1)
368 d = ei1.to_dict()
369 self._cmd.poutput_json(flatten_dict_lists(d['euicc_info1']))
370
371 def do_get_euicc_info2(self, opts):
372 """Perform an ES10b GetEUICCInfo (2) function."""
373 ei2 = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, EuiccInfo2(), EuiccInfo2)
374 d = ei2.to_dict()
375 self._cmd.poutput_json(flatten_dict_lists(d['euicc_info2']))
376
377 def do_list_notification(self, opts):
378 """Perform an ES10b ListNotification function."""
379 ln = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, ListNotificationReq(), ListNotificationResp)
380 d = ln.to_dict()
381 self._cmd.poutput_json(flatten_dict_lists(d['list_notification_resp']))
382
383 rem_notif_parser = argparse.ArgumentParser()
384 rem_notif_parser.add_argument('SEQ_NR', type=int, help='Sequence Number of the to-be-removed notification')
385
386 @cmd2.with_argparser(rem_notif_parser)
387 def do_remove_notification_from_list(self, opts):
388 """Perform an ES10b RemoveNotificationFromList function."""
389 rn_cmd = NotificationSentReq(children=[SeqNumber(decoded=opts.SEQ_NR)])
390 rn = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, rn_cmd, NotificationSentResp)
391 d = rn.to_dict()
392 self._cmd.poutput_json(flatten_dict_lists(d['notification_sent_resp']))
393
Harald Welte884eb552023-10-24 11:03:50 +0200394 def do_get_profiles_info(self, opts):
395 """Perform an ES10c GetProfilesInfo function."""
396 pi = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, ProfileInfoListReq(), ProfileInfoListResp)
397 d = pi.to_dict()
398 self._cmd.poutput_json(flatten_dict_lists(d['profile_info_list_resp']))
399
Harald Welte268a2022023-10-22 13:12:11 +0200400 en_prof_parser = argparse.ArgumentParser()
401 en_prof_grp = en_prof_parser.add_mutually_exclusive_group()
402 en_prof_grp.add_argument('--isdp-aid', help='Profile identified by its ISD-P AID')
403 en_prof_grp.add_argument('--iccid', help='Profile identified by its ICCID')
404 en_prof_parser.add_argument('--refresh-required', action='store_true', help='whether a REFRESH is required')
405
406 @cmd2.with_argparser(en_prof_parser)
407 def do_enable_profile(self, opts):
408 """Perform an ES10c EnableProfile function."""
Harald Welte268a2022023-10-22 13:12:11 +0200409 if opts.isdp_aid:
Harald Welte008cdf42023-12-07 00:03:47 +0100410 p_id = ProfileIdentifier(children=[IsdpAid(decoded=opts.isdp_aid)])
Harald Welte268a2022023-10-22 13:12:11 +0200411 if opts.iccid:
Harald Welte008cdf42023-12-07 00:03:47 +0100412 p_id = ProfileIdentifier(children=[Iccid(decoded=opts.iccid)])
413 ep_cmd_contents = [p_id, RefreshFlag(decoded=opts.refresh_required)]
Harald Welte268a2022023-10-22 13:12:11 +0200414 ep_cmd = EnableProfileReq(children=ep_cmd_contents)
415 ep = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, ep_cmd, EnableProfileResp)
416 d = ep.to_dict()
417 self._cmd.poutput_json(flatten_dict_lists(d['enable_profile_resp']))
418
419 dis_prof_parser = argparse.ArgumentParser()
420 dis_prof_grp = dis_prof_parser.add_mutually_exclusive_group()
421 dis_prof_grp.add_argument('--isdp-aid', help='Profile identified by its ISD-P AID')
422 dis_prof_grp.add_argument('--iccid', help='Profile identified by its ICCID')
423 dis_prof_parser.add_argument('--refresh-required', action='store_true', help='whether a REFRESH is required')
424
425 @cmd2.with_argparser(dis_prof_parser)
426 def do_disable_profile(self, opts):
427 """Perform an ES10c DisableProfile function."""
Harald Welte268a2022023-10-22 13:12:11 +0200428 if opts.isdp_aid:
Harald Welte008cdf42023-12-07 00:03:47 +0100429 p_id = ProfileIdentifier(children=[IsdpAid(decoded=opts.isdp_aid)])
Harald Welte268a2022023-10-22 13:12:11 +0200430 if opts.iccid:
Harald Welte008cdf42023-12-07 00:03:47 +0100431 p_id = ProfileIdentifier(children=[Iccid(decoded=opts.iccid)])
432 dp_cmd_contents = [p_id, RefreshFlag(decoded=opts.refresh_required)]
Harald Welte268a2022023-10-22 13:12:11 +0200433 dp_cmd = DisableProfileReq(children=dp_cmd_contents)
434 dp = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, dp_cmd, DisableProfileResp)
435 d = dp.to_dict()
436 self._cmd.poutput_json(flatten_dict_lists(d['disable_profile_resp']))
437
438 del_prof_parser = argparse.ArgumentParser()
439 del_prof_grp = del_prof_parser.add_mutually_exclusive_group()
440 del_prof_grp.add_argument('--isdp-aid', help='Profile identified by its ISD-P AID')
441 del_prof_grp.add_argument('--iccid', help='Profile identified by its ICCID')
442
443 @cmd2.with_argparser(del_prof_parser)
444 def do_delete_profile(self, opts):
445 """Perform an ES10c DeleteProfile function."""
Harald Welte268a2022023-10-22 13:12:11 +0200446 if opts.isdp_aid:
Harald Welte008cdf42023-12-07 00:03:47 +0100447 p_id = ProfileIdentifier(children=[IsdpAid(decoded=opts.isdp_aid)])
Harald Welte268a2022023-10-22 13:12:11 +0200448 if opts.iccid:
Harald Welte008cdf42023-12-07 00:03:47 +0100449 p_id = ProfileIdentifier(children=[Iccid(decoded=opts.iccid)])
450 dp_cmd_contents = [p_id]
Harald Welte268a2022023-10-22 13:12:11 +0200451 dp_cmd = DeleteProfileReq(children=dp_cmd_contents)
452 dp = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, dp_cmd, DeleteProfileResp)
453 d = dp.to_dict()
454 self._cmd.poutput_json(flatten_dict_lists(d['delete_profile_resp']))
455
456
457 def do_get_eid(self, opts):
458 """Perform an ES10c GetEID function."""
459 (data, sw) = ADF_ISDR.store_data(self._cmd.lchan.scc, 'BF3E035C015A')
460 ged_cmd = GetEuiccData(children=[TagList(decoded=[0x5A])])
461 ged = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, ged_cmd, GetEuiccData)
462 d = ged.to_dict()
463 self._cmd.poutput_json(flatten_dict_lists(d['get_euicc_data']))
464
465 set_nickname_parser = argparse.ArgumentParser()
466 set_nickname_parser.add_argument('ICCID', help='ICCID of the profile whose nickname to set')
467 set_nickname_parser.add_argument('--profile-nickname', help='Nickname of the profile')
468
469 @cmd2.with_argparser(set_nickname_parser)
470 def do_set_nickname(self, opts):
471 """Perform an ES10c SetNickname function."""
472 nickname = opts.profile_nickname or ''
473 sn_cmd_contents = [Iccid(decoded=opts.ICCID), ProfileNickname(decoded=nickname)]
474 sn_cmd = SetNicknameReq(children=sn_cmd_contents)
475 sn = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, sn_cmd, SetNicknameResp)
476 d = sn.to_dict()
477 self._cmd.poutput_json(flatten_dict_lists(d['set_nickname_resp']))
478
Harald Weltecbc0bdf2023-10-24 12:11:04 +0200479 def do_get_certs(self, opts):
480 """Perform an ES10c GetCerts() function on an IoT eUICC."""
481 gc = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, GetCertsReq(), GetCertsResp)
482 d = gc.to_dict()
483 self._cmd.poutput_json(flatten_dict_lists(d['get_certficiates_resp']))
484
485 def do_get_eim_configuration_data(self, opts):
486 """Perform an ES10b GetEimConfigurationData function on an Iot eUICC."""
487 gec = ADF_ISDR.store_data_tlv(self._cmd.lchan.scc, GetEimConfigurationData(),
488 GetEimConfigurationData)
489 d = gec.to_dict()
490 self._cmd.poutput_json(flatten_dict_lists(d['get_eim_configuration_data']))
491
492
Harald Welte268a2022023-10-22 13:12:11 +0200493class ADF_ECASD(CardADF):
494 def __init__(self, aid=AID_ECASD, name='ADF.ECASD', fid=None, sfid=None,
495 desc='ECASD (eUICC Controlling Authority Security Domain) Application'):
496 super().__init__(aid=aid, fid=fid, sfid=sfid, name=name, desc=desc)
497 self.shell_commands += [self.AddlShellCommands()]
498
499 def decode_select_response(self, data_hex: Hexstr) -> object:
500 t = FciTemplate()
501 t.from_tlv(h2b(data_hex))
502 d = t.to_dict()
503 return flatten_dict_lists(d['fci_template'])
504
505 @with_default_category('Application-Specific Commands')
506 class AddlShellCommands(CommandSet):
507 pass
508
509
510
511class CardApplicationISDR(CardApplication):
512 def __init__(self):
513 super().__init__('ISD-R', adf=ADF_ISDR(), sw=sw_isdr)
514
515class CardApplicationECASD(CardApplication):
516 def __init__(self):
517 super().__init__('ECASD', adf=ADF_ECASD(), sw=sw_isdr)