Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | |
| 3 | """ |
| 4 | Various definitions related to GSMA eSIM / eUICC |
| 5 | |
| 6 | Related 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 | |
| 24 | from pySim.tlv import * |
| 25 | from pySim.construct import * |
| 26 | from construct import Optional as COptional |
| 27 | from construct import * |
| 28 | import argparse |
| 29 | from cmd2 import cmd2, CommandSet, with_default_category |
| 30 | from pySim.commands import SimCardCommands |
| 31 | from pySim.filesystem import CardADF, CardApplication |
| 32 | from pySim.utils import Hexstr, SwHexstr |
| 33 | import pySim.global_platform |
| 34 | |
| 35 | class 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 | |
| 44 | VersionType = VersionAdapter(Array(3, Int8ub)) |
| 45 | |
| 46 | # Application Identifiers as defined in GSMA SGP.02 Annex H |
| 47 | AID_ISD_R = "A0000005591010FFFFFFFF8900000100" |
| 48 | AID_ECASD = "A0000005591010FFFFFFFF8900000200" |
| 49 | AID_ISD_P_FILE = "A0000005591010FFFFFFFF8900000D00" |
| 50 | AID_ISD_P_MODULE = "A0000005591010FFFFFFFF8900000E00" |
| 51 | |
| 52 | sw_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 | |
| 61 | class SupportedVersionNumber(BER_TLV_IE, tag=0x82): |
| 62 | _construct = GreedyBytes |
| 63 | |
| 64 | class 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 |
| 69 | class 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 |
| 75 | class DefaultDpAddress(BER_TLV_IE, tag=0x80): |
| 76 | _construct = Utf8Adapter(GreedyBytes) |
| 77 | class RootDsAddress(BER_TLV_IE, tag=0x81): |
| 78 | _construct = Utf8Adapter(GreedyBytes) |
| 79 | class EuiccConfiguredAddresses(BER_TLV_IE, tag=0xbf3c, nested=[DefaultDpAddress, RootDsAddress]): |
| 80 | pass |
| 81 | |
| 82 | # SGP.22 Section 5.7.4: SetDefaultDpAddress |
| 83 | class SetDefaultDpAddrRes(BER_TLV_IE, tag=0x80): |
| 84 | _construct = Enum(Int8ub, ok=0, undefinedError=127) |
| 85 | class SetDefaultDpAddress(BER_TLV_IE, tag=0xbf3f, nested=[DefaultDpAddress, SetDefaultDpAddrRes]): |
| 86 | pass |
| 87 | |
| 88 | # SGP.22 Section 5.7.7: GetEUICCChallenge |
| 89 | class EuiccChallenge(BER_TLV_IE, tag=0x80): |
| 90 | _construct = HexAdapter(Bytes(16)) |
| 91 | class GetEuiccChallenge(BER_TLV_IE, tag=0xbf2e, nested=[EuiccChallenge]): |
| 92 | pass |
| 93 | |
| 94 | # SGP.22 Section 5.7.8: GetEUICCInfo |
| 95 | class SVN(BER_TLV_IE, tag=0x82): |
| 96 | _construct = VersionType |
| 97 | class SubjectKeyIdentifier(BER_TLV_IE, tag=0x81): |
| 98 | _construct = HexAdapter(GreedyBytes) |
| 99 | class SubjectKeyIdentifierSeq(BER_TLV_IE, tag=0x04, nested=[SubjectKeyIdentifier]): |
| 100 | pass |
| 101 | class EuiccCiPkiListForVerification(BER_TLV_IE, tag=0xa9, nested=[SubjectKeyIdentifierSeq]): |
| 102 | pass |
| 103 | class EuiccCiPkiListForSigning(BER_TLV_IE, tag=0xaa, nested=[SubjectKeyIdentifierSeq]): |
| 104 | pass |
| 105 | class EuiccInfo1(BER_TLV_IE, tag=0xbf20, nested=[SVN, EuiccCiPkiListForVerification, EuiccCiPkiListForSigning]): |
| 106 | pass |
| 107 | class ProfileVersion(BER_TLV_IE, tag=0x81): |
| 108 | _construct = VersionType |
| 109 | class EuiccFirmwareVer(BER_TLV_IE, tag=0x83): |
| 110 | _construct = VersionType |
| 111 | class ExtCardResource(BER_TLV_IE, tag=0x84): |
| 112 | _construct = HexAdapter(GreedyBytes) |
| 113 | class UiccCapability(BER_TLV_IE, tag=0x85): |
| 114 | _construct = HexAdapter(GreedyBytes) # FIXME |
| 115 | class TS102241Version(BER_TLV_IE, tag=0x86): |
| 116 | _construct = VersionType |
| 117 | class GlobalPlatformVersion(BER_TLV_IE, tag=0x87): |
| 118 | _construct = VersionType |
| 119 | class RspCapability(BER_TLV_IE, tag=0x88): |
| 120 | _construct = HexAdapter(GreedyBytes) # FIXME |
| 121 | class EuiccCategory(BER_TLV_IE, tag=0x8b): |
| 122 | _construct = Enum(Int8ub, other=0, basicEuicc=1, mediumEuicc=2, contactlessEuicc=3) |
| 123 | class PpVersion(BER_TLV_IE, tag=0x04): |
| 124 | _construct = VersionType |
| 125 | class SsAcreditationNumber(BER_TLV_IE, tag=0x0c): |
| 126 | _construct = Utf8Adapter(GreedyBytes) |
Harald Welte | cbc0bdf | 2023-10-24 12:11:04 +0200 | [diff] [blame] | 127 | class IpaMode(BER_TLV_IE, tag=0x90): # see SGP.32 v1.0 |
| 128 | _construct = Enum(Int8ub, ipad=0, ipea=1) |
| 129 | class IotVersion(BER_TLV_IE, tag=0x80): # see SGP.32 v1.0 |
| 130 | _construct = VersionType |
| 131 | class IotVersionSeq(BER_TLV_IE, tag=0xa0, nested=[IotVersion]): # see SGP.32 v1.0 |
| 132 | pass |
| 133 | class IotSpecificInfo(BER_TLV_IE, tag=0x94, nested=[IotVersionSeq]): # see SGP.32 v1.0 |
| 134 | pass |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 135 | class EuiccInfo2(BER_TLV_IE, tag=0xbf22, nested=[ProfileVersion, SVN, EuiccFirmwareVer, ExtCardResource, |
| 136 | UiccCapability, TS102241Version, GlobalPlatformVersion, |
| 137 | RspCapability, EuiccCiPkiListForVerification, |
| 138 | EuiccCiPkiListForSigning, EuiccCategory, PpVersion, |
Harald Welte | cbc0bdf | 2023-10-24 12:11:04 +0200 | [diff] [blame] | 139 | SsAcreditationNumber, IpaMode, IotSpecificInfo]): |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 140 | pass |
| 141 | |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 142 | # SGP.22 Section 5.7.9: ListNotification |
| 143 | class ProfileMgmtOperation(BER_TLV_IE, tag=0x81): |
| 144 | _construct = FlagsEnum(Byte, install=1, enable=2, disable=4, delete=8) |
| 145 | class ListNotificationReq(BER_TLV_IE, tag=0xbf28, nested=[ProfileMgmtOperation]): |
| 146 | pass |
| 147 | class SeqNumber(BER_TLV_IE, tag=0x80): |
| 148 | _construct = GreedyInteger |
| 149 | class NotificationAddress(BER_TLV_IE, tag=0x82): |
| 150 | _construct = Utf8Adapter(GreedyBytes) |
| 151 | class Iccid(BER_TLV_IE, tag=0x5a): |
Harald Welte | 2b6dedd | 2023-12-07 14:21:19 +0100 | [diff] [blame] | 152 | _construct = BcdAdapter(GreedyBytes) |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 153 | class NotificationMetadata(BER_TLV_IE, tag=0xbf2f, nested=[SeqNumber, ProfileMgmtOperation, |
| 154 | NotificationAddress, Iccid]): |
| 155 | pass |
| 156 | class NotificationMetadataList(BER_TLV_IE, tag=0xa0, nested=[NotificationMetadata]): |
| 157 | pass |
| 158 | class ListNotificationsResultError(BER_TLV_IE, tag=0x81): |
| 159 | _construct = Enum(Int8ub, undefinedError=127) |
| 160 | class ListNotificationResp(BER_TLV_IE, tag=0xbf28, nested=[NotificationMetadataList, |
| 161 | ListNotificationsResultError]): |
| 162 | pass |
| 163 | |
| 164 | # SGP.22 Section 5.7.11: RemoveNotificationFromList |
| 165 | class DeleteNotificationStatus(BER_TLV_IE, tag=0x80): |
| 166 | _construct = Enum(Int8ub, ok=0, nothingToDelete=1, undefinedError=127) |
| 167 | class NotificationSentReq(BER_TLV_IE, tag=0xbf30, nested=[SeqNumber]): |
| 168 | pass |
| 169 | class NotificationSentResp(BER_TLV_IE, tag=0xbf30, nested=[DeleteNotificationStatus]): |
| 170 | pass |
| 171 | |
| 172 | # SGP.22 Section 5.7.12: LoadCRL: FIXME |
| 173 | class LoadCRL(BER_TLV_IE, tag=0xbf35, nested=[]): # FIXME |
| 174 | pass |
| 175 | |
Harald Welte | 884eb55 | 2023-10-24 11:03:50 +0200 | [diff] [blame] | 176 | # SGP.22 Section 5.7.15: GetProfilesInfo |
| 177 | class TagList(BER_TLV_IE, tag=0x5c): |
| 178 | _construct = GreedyRange(Int8ub) # FIXME: tags could be multi-byte |
| 179 | class ProfileInfoListReq(BER_TLV_IE, tag=0xbf2d, nested=[TagList]): # FIXME: SearchCriteria |
| 180 | pass |
| 181 | class IsdpAid(BER_TLV_IE, tag=0x4f): |
| 182 | _construct = HexAdapter(GreedyBytes) |
| 183 | class ProfileState(BER_TLV_IE, tag=0x9f70): |
| 184 | _construct = Enum(Int8ub, disabled=0, enabled=1) |
| 185 | class ProfileNickname(BER_TLV_IE, tag=0x90): |
| 186 | _construct = Utf8Adapter(GreedyBytes) |
| 187 | class ServiceProviderName(BER_TLV_IE, tag=0x91): |
| 188 | _construct = Utf8Adapter(GreedyBytes) |
| 189 | class ProfileName(BER_TLV_IE, tag=0x92): |
| 190 | _construct = Utf8Adapter(GreedyBytes) |
| 191 | class IconType(BER_TLV_IE, tag=0x93): |
| 192 | _construct = Enum(Int8ub, jpg=0, png=1) |
| 193 | class Icon(BER_TLV_IE, tag=0x94): |
| 194 | _construct = GreedyBytes |
| 195 | class ProfileClass(BER_TLV_IE, tag=0x95): |
| 196 | _construct = Enum(Int8ub, test=0, provisioning=1, operational=2) |
| 197 | class ProfileInfo(BER_TLV_IE, tag=0xe3, nested=[Iccid, IsdpAid, ProfileState, ProfileNickname, |
| 198 | ServiceProviderName, ProfileName, IconType, Icon, |
| 199 | ProfileClass]): # FIXME: more IEs |
| 200 | pass |
| 201 | class ProfileInfoSeq(BER_TLV_IE, tag=0xa0, nested=[ProfileInfo]): |
| 202 | pass |
| 203 | class ProfileInfoListError(BER_TLV_IE, tag=0x81): |
| 204 | _construct = Enum(Int8ub, incorrectInputValues=1, undefinedError=2) |
| 205 | class ProfileInfoListResp(BER_TLV_IE, tag=0xbf2d, nested=[ProfileInfoSeq, ProfileInfoListError]): |
| 206 | pass |
| 207 | |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 208 | # SGP.22 Section 5.7.16:: EnableProfile |
Harald Welte | 008cdf4 | 2023-12-07 00:03:47 +0100 | [diff] [blame] | 209 | class RefreshFlag(BER_TLV_IE, tag=0x81): # FIXME |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 210 | _construct = Int8ub # FIXME |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 211 | class 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 Welte | 008cdf4 | 2023-12-07 00:03:47 +0100 | [diff] [blame] | 214 | class ProfileIdentifier(BER_TLV_IE, tag=0xa0, nested=[IsdpAid, Iccid]): |
| 215 | pass |
| 216 | class EnableProfileReq(BER_TLV_IE, tag=0xbf31, nested=[ProfileIdentifier, RefreshFlag]): |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 217 | pass |
| 218 | class EnableProfileResp(BER_TLV_IE, tag=0xbf31, nested=[EnableResult]): |
| 219 | pass |
| 220 | |
| 221 | # SGP.22 Section 5.7.17 DisableProfile |
| 222 | class DisableResult(BER_TLV_IE, tag=0x80): |
| 223 | _construct = Enum(Int8ub, ok=0, iccidOrAidNotFound=1, profileNotInEnabledState=2, |
| 224 | disallowedByPolicy=3, catBusy=5, undefinedError=127) |
Harald Welte | 008cdf4 | 2023-12-07 00:03:47 +0100 | [diff] [blame] | 225 | class DisableProfileReq(BER_TLV_IE, tag=0xbf32, nested=[ProfileIdentifier, RefreshFlag]): |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 226 | pass |
| 227 | class DisableProfileResp(BER_TLV_IE, tag=0xbf32, nested=[DisableResult]): |
| 228 | pass |
| 229 | |
| 230 | # SGP.22 Section 5.7.18: DeleteProfile |
| 231 | class DeleteResult(BER_TLV_IE, tag=0x80): |
| 232 | _construct = Enum(Int8ub, ok=0, iccidOrAidNotFound=1, profileNotInDisabledState=2, |
| 233 | disallowedByPolicy=3, undefinedError=127) |
Harald Welte | 008cdf4 | 2023-12-07 00:03:47 +0100 | [diff] [blame] | 234 | class DeleteProfileReq(BER_TLV_IE, tag=0xbf33, nested=[ProfileIdentifier]): |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 235 | pass |
| 236 | class DeleteProfileResp(BER_TLV_IE, tag=0xbf33, nested=[DeleteResult]): |
| 237 | pass |
| 238 | |
| 239 | # SGP.22 Section 5.7.20 GetEID |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 240 | class EidValue(BER_TLV_IE, tag=0x5a): |
| 241 | _construct = HexAdapter(GreedyBytes) |
| 242 | class GetEuiccData(BER_TLV_IE, tag=0xbf3e, nested=[TagList, EidValue]): |
| 243 | pass |
| 244 | |
| 245 | # SGP.22 Section 5.7.21: ES10c SetNickname |
Harald Welte | 884eb55 | 2023-10-24 11:03:50 +0200 | [diff] [blame] | 246 | class SnrProfileNickname(BER_TLV_IE, tag=0x8f): |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 247 | _construct = Utf8Adapter(GreedyBytes) |
Harald Welte | b582c3c | 2023-12-07 23:29:11 +0100 | [diff] [blame] | 248 | class SetNicknameReq(BER_TLV_IE, tag=0xbf29, nested=[Iccid, SnrProfileNickname]): |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 249 | pass |
| 250 | class SetNicknameResult(BER_TLV_IE, tag=0x80): |
| 251 | _construct = Enum(Int8ub, ok=0, iccidNotFound=1, undefinedError=127) |
Harald Welte | b582c3c | 2023-12-07 23:29:11 +0100 | [diff] [blame] | 252 | class SetNicknameResp(BER_TLV_IE, tag=0xbf29, nested=[SetNicknameResult]): |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 253 | pass |
| 254 | |
Harald Welte | cbc0bdf | 2023-10-24 12:11:04 +0200 | [diff] [blame] | 255 | # SGP.32 Section 5.9.10: ES10b: GetCerts |
| 256 | class GetCertsReq(BER_TLV_IE, tag=0xbf56): |
| 257 | pass |
| 258 | class EumCertificate(BER_TLV_IE, tag=0xa5): |
| 259 | _construct = GreedyBytes |
| 260 | class EuiccCertificate(BER_TLV_IE, tag=0xa6): |
| 261 | _construct = GreedyBytes |
| 262 | class GetCertsError(BER_TLV_IE, tag=0x80): |
| 263 | _construct = Enum(Int8ub, invalidCiPKId=1, undefinedError=127) |
| 264 | class GetCertsResp(BER_TLV_IE, tag=0xbf56, nested=[EumCertificate, EuiccCertificate, GetCertsError]): |
| 265 | pass |
| 266 | |
| 267 | # SGP.32 Section 5.9.18: ES10b: GetEimConfigurationData |
| 268 | class EimId(BER_TLV_IE, tag=0x80): |
| 269 | _construct = Utf8Adapter(GreedyBytes) |
| 270 | class EimFqdn(BER_TLV_IE, tag=0x81): |
| 271 | _construct = Utf8Adapter(GreedyBytes) |
| 272 | class EimIdType(BER_TLV_IE, tag=0x82): |
| 273 | _construct = Enum(Int8ub, eimIdTypeOid=1, eimIdTypeFqdn=2, eimIdTypeProprietary=3) |
| 274 | class CounterValue(BER_TLV_IE, tag=0x83): |
| 275 | _construct = GreedyInteger |
| 276 | class AssociationToken(BER_TLV_IE, tag=0x84): |
| 277 | _construct = GreedyInteger |
| 278 | class 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 |
| 282 | class EimConfigurationData(BER_TLV_IE, tag=0x80, nested=[EimId, EimFqdn, EimIdType, CounterValue, |
| 283 | AssociationToken, EimSupportedProtocol]): |
| 284 | pass |
| 285 | class EimConfigurationDataSeq(BER_TLV_IE, tag=0xa0, nested=[EimConfigurationData]): |
| 286 | pass |
| 287 | class GetEimConfigurationData(BER_TLV_IE, tag=0xbf55, nested=[EimConfigurationDataSeq]): |
| 288 | pass |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 289 | |
| 290 | class 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 Welte | c20d442 | 2023-12-07 22:46:40 +0100 | [diff] [blame] | 300 | capdu = '%sE29100%02x%s' % (scc.cla4lchan('80'), len(tx_do)//2, tx_do) |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 301 | 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 Welte | 884eb55 | 2023-10-24 11:03:50 +0200 | [diff] [blame] | 394 | 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 Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 400 | 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 Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 409 | if opts.isdp_aid: |
Harald Welte | 008cdf4 | 2023-12-07 00:03:47 +0100 | [diff] [blame] | 410 | p_id = ProfileIdentifier(children=[IsdpAid(decoded=opts.isdp_aid)]) |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 411 | if opts.iccid: |
Harald Welte | 008cdf4 | 2023-12-07 00:03:47 +0100 | [diff] [blame] | 412 | p_id = ProfileIdentifier(children=[Iccid(decoded=opts.iccid)]) |
| 413 | ep_cmd_contents = [p_id, RefreshFlag(decoded=opts.refresh_required)] |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 414 | 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 Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 428 | if opts.isdp_aid: |
Harald Welte | 008cdf4 | 2023-12-07 00:03:47 +0100 | [diff] [blame] | 429 | p_id = ProfileIdentifier(children=[IsdpAid(decoded=opts.isdp_aid)]) |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 430 | if opts.iccid: |
Harald Welte | 008cdf4 | 2023-12-07 00:03:47 +0100 | [diff] [blame] | 431 | p_id = ProfileIdentifier(children=[Iccid(decoded=opts.iccid)]) |
| 432 | dp_cmd_contents = [p_id, RefreshFlag(decoded=opts.refresh_required)] |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 433 | 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 Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 446 | if opts.isdp_aid: |
Harald Welte | 008cdf4 | 2023-12-07 00:03:47 +0100 | [diff] [blame] | 447 | p_id = ProfileIdentifier(children=[IsdpAid(decoded=opts.isdp_aid)]) |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 448 | if opts.iccid: |
Harald Welte | 008cdf4 | 2023-12-07 00:03:47 +0100 | [diff] [blame] | 449 | p_id = ProfileIdentifier(children=[Iccid(decoded=opts.iccid)]) |
| 450 | dp_cmd_contents = [p_id] |
Harald Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 451 | 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 Welte | cbc0bdf | 2023-10-24 12:11:04 +0200 | [diff] [blame] | 479 | 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 Welte | 268a202 | 2023-10-22 13:12:11 +0200 | [diff] [blame] | 493 | class 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 | |
| 511 | class CardApplicationISDR(CardApplication): |
| 512 | def __init__(self): |
| 513 | super().__init__('ISD-R', adf=ADF_ISDR(), sw=sw_isdr) |
| 514 | |
| 515 | class CardApplicationECASD(CardApplication): |
| 516 | def __init__(self): |
| 517 | super().__init__('ECASD', adf=ADF_ECASD(), sw=sw_isdr) |