mirror of https://gerrit.osmocom.org/pysim
529 lines
25 KiB
Python
529 lines
25 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Various definitions related to GSMA eSIM / eUICC
|
|
|
|
Related Specs: GSMA SGP.22, GSMA SGP.02, etc.
|
|
"""
|
|
|
|
# Copyright (C) 2023 Harald Welte <laforge@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/>.
|
|
|
|
import argparse
|
|
|
|
from construct import Array, Struct, FlagsEnum, GreedyRange
|
|
from cmd2 import cmd2, CommandSet, with_default_category
|
|
|
|
from pySim.tlv import *
|
|
from pySim.construct import *
|
|
from pySim.commands import SimCardCommands
|
|
from pySim.utils import Hexstr, SwHexstr, SwMatchstr
|
|
import pySim.global_platform
|
|
|
|
def compute_eid_checksum(eid) -> str:
|
|
"""Compute and add/replace check digits of an EID value according to GSMA SGP.29 Section 10."""
|
|
if isinstance(eid, str):
|
|
if len(eid) == 30:
|
|
# first pad by 2 digits
|
|
eid += "00"
|
|
elif len(eid) == 32:
|
|
# zero the last two digits
|
|
eid = eid[:-2] + "00"
|
|
else:
|
|
raise ValueError("and EID must be 30 or 32 digits")
|
|
eid_int = int(eid)
|
|
elif isinstance(eid, int):
|
|
eid_int = eid
|
|
if eid_int % 100:
|
|
# zero the last two digits
|
|
eid_int -= eid_int % 100
|
|
# Using the resulting 32 digits as a decimal integer, compute the remainder of that number on division by
|
|
# 97, Subtract the remainder from 98, and use the decimal result for the two check digits, if the result
|
|
# is one digit long, its value SHALL be prefixed by one digit of 0.
|
|
csum = 98 - (eid_int % 97)
|
|
eid_int += csum
|
|
return str(eid_int)
|
|
|
|
def verify_eid_checksum(eid) -> bool:
|
|
"""Verify the check digits of an EID value according to GSMA SGP.29 Section 10."""
|
|
# Using the 32 digits as a decimal integer, compute the remainder of that number on division by 97. If the
|
|
# remainder of the division is 1, the verification is successful; otherwise the EID is invalid.
|
|
return int(eid) % 97 == 1
|
|
|
|
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"
|
|
|
|
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=0x04):
|
|
_construct = HexAdapter(GreedyBytes)
|
|
class EuiccCiPkiListForVerification(BER_TLV_IE, tag=0xa9, nested=[SubjectKeyIdentifier]):
|
|
pass
|
|
class EuiccCiPkiListForSigning(BER_TLV_IE, tag=0xaa, nested=[SubjectKeyIdentifier]):
|
|
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 IpaMode(BER_TLV_IE, tag=0x90): # see SGP.32 v1.0
|
|
_construct = Enum(Int8ub, ipad=0, ipea=1)
|
|
class IotVersion(BER_TLV_IE, tag=0x80): # see SGP.32 v1.0
|
|
_construct = VersionType
|
|
class IotVersionSeq(BER_TLV_IE, tag=0xa0, nested=[IotVersion]): # see SGP.32 v1.0
|
|
pass
|
|
class IotSpecificInfo(BER_TLV_IE, tag=0x94, nested=[IotVersionSeq]): # see SGP.32 v1.0
|
|
pass
|
|
class EuiccInfo2(BER_TLV_IE, tag=0xbf22, nested=[ProfileVersion, SVN, EuiccFirmwareVer, ExtCardResource,
|
|
UiccCapability, TS102241Version, GlobalPlatformVersion,
|
|
RspCapability, EuiccCiPkiListForVerification,
|
|
EuiccCiPkiListForSigning, EuiccCategory, PpVersion,
|
|
SsAcreditationNumber, IpaMode, IotSpecificInfo]):
|
|
pass
|
|
|
|
# SGP.22 Section 5.7.9: ListNotification
|
|
class ProfileMgmtOperation(BER_TLV_IE, tag=0x81):
|
|
# we have to ignore the first byte which tells us how many padding bits are used in the last octet
|
|
_construct = Struct(Byte, "pmo"/FlagsEnum(Byte, install=0x80, enable=0x40, disable=0x20, delete=0x10))
|
|
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=0x0c):
|
|
_construct = Utf8Adapter(GreedyBytes)
|
|
class Iccid(BER_TLV_IE, tag=0x5a):
|
|
_construct = BcdAdapter(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.15: GetProfilesInfo
|
|
class TagList(BER_TLV_IE, tag=0x5c):
|
|
_construct = GreedyRange(Int8ub) # FIXME: tags could be multi-byte
|
|
class ProfileInfoListReq(BER_TLV_IE, tag=0xbf2d, nested=[TagList]): # FIXME: SearchCriteria
|
|
pass
|
|
class IsdpAid(BER_TLV_IE, tag=0x4f):
|
|
_construct = HexAdapter(GreedyBytes)
|
|
class ProfileState(BER_TLV_IE, tag=0x9f70):
|
|
_construct = Enum(Int8ub, disabled=0, enabled=1)
|
|
class ProfileNickname(BER_TLV_IE, tag=0x90):
|
|
_construct = Utf8Adapter(GreedyBytes)
|
|
class ServiceProviderName(BER_TLV_IE, tag=0x91):
|
|
_construct = Utf8Adapter(GreedyBytes)
|
|
class ProfileName(BER_TLV_IE, tag=0x92):
|
|
_construct = Utf8Adapter(GreedyBytes)
|
|
class IconType(BER_TLV_IE, tag=0x93):
|
|
_construct = Enum(Int8ub, jpg=0, png=1)
|
|
class Icon(BER_TLV_IE, tag=0x94):
|
|
_construct = GreedyBytes
|
|
class ProfileClass(BER_TLV_IE, tag=0x95):
|
|
_construct = Enum(Int8ub, test=0, provisioning=1, operational=2)
|
|
class ProfileInfo(BER_TLV_IE, tag=0xe3, nested=[Iccid, IsdpAid, ProfileState, ProfileNickname,
|
|
ServiceProviderName, ProfileName, IconType, Icon,
|
|
ProfileClass]): # FIXME: more IEs
|
|
pass
|
|
class ProfileInfoSeq(BER_TLV_IE, tag=0xa0, nested=[ProfileInfo]):
|
|
pass
|
|
class ProfileInfoListError(BER_TLV_IE, tag=0x81):
|
|
_construct = Enum(Int8ub, incorrectInputValues=1, undefinedError=2)
|
|
class ProfileInfoListResp(BER_TLV_IE, tag=0xbf2d, nested=[ProfileInfoSeq, ProfileInfoListError]):
|
|
pass
|
|
|
|
# SGP.22 Section 5.7.16:: EnableProfile
|
|
class RefreshFlag(BER_TLV_IE, tag=0x81): # FIXME
|
|
_construct = Int8ub # FIXME
|
|
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 ProfileIdentifier(BER_TLV_IE, tag=0xa0, nested=[IsdpAid, Iccid]):
|
|
pass
|
|
class EnableProfileReq(BER_TLV_IE, tag=0xbf31, nested=[ProfileIdentifier, 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=[ProfileIdentifier, 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 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 SnrProfileNickname(BER_TLV_IE, tag=0x8f):
|
|
_construct = Utf8Adapter(GreedyBytes)
|
|
class SetNicknameReq(BER_TLV_IE, tag=0xbf29, nested=[Iccid, SnrProfileNickname]):
|
|
pass
|
|
class SetNicknameResult(BER_TLV_IE, tag=0x80):
|
|
_construct = Enum(Int8ub, ok=0, iccidNotFound=1, undefinedError=127)
|
|
class SetNicknameResp(BER_TLV_IE, tag=0xbf29, nested=[SetNicknameResult]):
|
|
pass
|
|
|
|
# SGP.32 Section 5.9.10: ES10b: GetCerts
|
|
class GetCertsReq(BER_TLV_IE, tag=0xbf56):
|
|
pass
|
|
class EumCertificate(BER_TLV_IE, tag=0xa5):
|
|
_construct = GreedyBytes
|
|
class EuiccCertificate(BER_TLV_IE, tag=0xa6):
|
|
_construct = GreedyBytes
|
|
class GetCertsError(BER_TLV_IE, tag=0x80):
|
|
_construct = Enum(Int8ub, invalidCiPKId=1, undefinedError=127)
|
|
class GetCertsResp(BER_TLV_IE, tag=0xbf56, nested=[EumCertificate, EuiccCertificate, GetCertsError]):
|
|
pass
|
|
|
|
# SGP.32 Section 5.9.18: ES10b: GetEimConfigurationData
|
|
class EimId(BER_TLV_IE, tag=0x80):
|
|
_construct = Utf8Adapter(GreedyBytes)
|
|
class EimFqdn(BER_TLV_IE, tag=0x81):
|
|
_construct = Utf8Adapter(GreedyBytes)
|
|
class EimIdType(BER_TLV_IE, tag=0x82):
|
|
_construct = Enum(Int8ub, eimIdTypeOid=1, eimIdTypeFqdn=2, eimIdTypeProprietary=3)
|
|
class CounterValue(BER_TLV_IE, tag=0x83):
|
|
_construct = GreedyInteger
|
|
class AssociationToken(BER_TLV_IE, tag=0x84):
|
|
_construct = GreedyInteger
|
|
class EimSupportedProtocol(BER_TLV_IE, tag=0x87):
|
|
_construct = Enum(Int8ub, eimRetrieveHttps=0, eimRetrieveCoaps=1, eimInjectHttps=2, eimInjectCoaps=3,
|
|
eimProprietary=4)
|
|
# FIXME: eimPublicKeyData, trustedPublicKeyDataTls, euiccCiPKId
|
|
class EimConfigurationData(BER_TLV_IE, tag=0x80, nested=[EimId, EimFqdn, EimIdType, CounterValue,
|
|
AssociationToken, EimSupportedProtocol]):
|
|
pass
|
|
class EimConfigurationDataSeq(BER_TLV_IE, tag=0xa0, nested=[EimConfigurationData]):
|
|
pass
|
|
class GetEimConfigurationData(BER_TLV_IE, tag=0xbf55, nested=[EimConfigurationDataSeq]):
|
|
pass
|
|
|
|
class CardApplicationISDR(pySim.global_platform.CardApplicationSD):
|
|
def __init__(self):
|
|
super().__init__(name='ADF.ISD-R', aid=AID_ISD_R,
|
|
desc='ISD-R (Issuer Security Domain Root) Application')
|
|
self.adf.decode_select_response = self.decode_select_response
|
|
self.adf.shell_commands += [self.AddlShellCommands()]
|
|
|
|
@staticmethod
|
|
def store_data(scc: SimCardCommands, tx_do: Hexstr, exp_sw: SwMatchstr ="9000") -> 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%02x%s' % (scc.cla4lchan('80'), len(tx_do)//2, tx_do)
|
|
return scc.send_apdu_checksw(capdu, exp_sw)
|
|
|
|
@staticmethod
|
|
def store_data_tlv(scc: SimCardCommands, cmd_do, resp_cls, exp_sw: SwMatchstr = '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) = CardApplicationISDR.store_data(scc, b2h(cmd_do_enc), exp_sw=exp_sw)
|
|
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) = CardApplicationISDR.store_data(self._cmd.lchan.scc, opts.TX_DO)
|
|
|
|
def do_get_euicc_configured_addresses(self, _opts):
|
|
"""Perform an ES10a GetEuiccConfiguredAddresses function."""
|
|
eca = CardApplicationISDR.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 = CardApplicationISDR.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 = CardApplicationISDR.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 = CardApplicationISDR.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 = CardApplicationISDR.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 = CardApplicationISDR.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 = CardApplicationISDR.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']))
|
|
|
|
def do_get_profiles_info(self, _opts):
|
|
"""Perform an ES10c GetProfilesInfo function."""
|
|
pi = CardApplicationISDR.store_data_tlv(self._cmd.lchan.scc, ProfileInfoListReq(), ProfileInfoListResp)
|
|
d = pi.to_dict()
|
|
self._cmd.poutput_json(flatten_dict_lists(d['profile_info_list_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."""
|
|
if opts.isdp_aid:
|
|
p_id = ProfileIdentifier(children=[IsdpAid(decoded=opts.isdp_aid)])
|
|
if opts.iccid:
|
|
p_id = ProfileIdentifier(children=[Iccid(decoded=opts.iccid)])
|
|
ep_cmd_contents = [p_id, RefreshFlag(decoded=opts.refresh_required)]
|
|
ep_cmd = EnableProfileReq(children=ep_cmd_contents)
|
|
ep = CardApplicationISDR.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."""
|
|
if opts.isdp_aid:
|
|
p_id = ProfileIdentifier(children=[IsdpAid(decoded=opts.isdp_aid)])
|
|
if opts.iccid:
|
|
p_id = ProfileIdentifier(children=[Iccid(decoded=opts.iccid)])
|
|
dp_cmd_contents = [p_id, RefreshFlag(decoded=opts.refresh_required)]
|
|
dp_cmd = DisableProfileReq(children=dp_cmd_contents)
|
|
dp = CardApplicationISDR.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."""
|
|
if opts.isdp_aid:
|
|
p_id = IsdpAid(decoded=opts.isdp_aid)
|
|
if opts.iccid:
|
|
p_id = Iccid(decoded=opts.iccid)
|
|
dp_cmd_contents = [p_id]
|
|
dp_cmd = DeleteProfileReq(children=dp_cmd_contents)
|
|
dp = CardApplicationISDR.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) = CardApplicationISDR.store_data(self._cmd.lchan.scc, 'BF3E035C015A')
|
|
ged_cmd = GetEuiccData(children=[TagList(decoded=[0x5A])])
|
|
ged = CardApplicationISDR.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 = CardApplicationISDR.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']))
|
|
|
|
def do_get_certs(self, _opts):
|
|
"""Perform an ES10c GetCerts() function on an IoT eUICC."""
|
|
gc = CardApplicationISDR.store_data_tlv(self._cmd.lchan.scc, GetCertsReq(), GetCertsResp)
|
|
d = gc.to_dict()
|
|
self._cmd.poutput_json(flatten_dict_lists(d['get_certficiates_resp']))
|
|
|
|
def do_get_eim_configuration_data(self, _opts):
|
|
"""Perform an ES10b GetEimConfigurationData function on an Iot eUICC."""
|
|
gec = CardApplicationISDR.store_data_tlv(self._cmd.lchan.scc, GetEimConfigurationData(),
|
|
GetEimConfigurationData)
|
|
d = gec.to_dict()
|
|
self._cmd.poutput_json(flatten_dict_lists(d['get_eim_configuration_data']))
|
|
|
|
class CardApplicationECASD(pySim.global_platform.CardApplicationSD):
|
|
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'])
|
|
|
|
def __init__(self):
|
|
super().__init__(name='ADF.ECASD', aid=AID_ECASD,
|
|
desc='ECASD (eUICC Controlling Authority Security Domain) Application')
|
|
self.adf.decode_select_response = self.decode_select_response
|
|
self.adf.shell_commands += [self.AddlShellCommands()]
|
|
|
|
@with_default_category('Application-Specific Commands')
|
|
class AddlShellCommands(CommandSet):
|
|
pass
|