blob: 8daf927d755703ddf192774461de9a32cb2c8e3e [file] [log] [blame]
Harald Welte34eb5042022-02-21 17:19:28 +01001# coding=utf-8
2"""Partial Support for GlobalPLatform Card Spec (currently 2.1.1)
3
4(C) 2022 by Harald Welte <laforge@osmocom.org>
5
6This program is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 of the License, or
9(at your option) any later version.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program. If not, see <http://www.gnu.org/licenses/>.
18"""
19
20from typing import Optional, List, Dict, Tuple
21from construct import Optional as COptional
22from construct import *
23from bidict import bidict
24from pySim.construct import *
25from pySim.utils import *
26from pySim.filesystem import *
27from pySim.tlv import *
28from pySim.profile import CardProfile
29
30sw_table = {
31 'Warnings': {
32 '6200': 'Logical Channel already closed',
33 '6283': 'Card Life Cycle State is CARD_LOCKED',
34 '6310': 'More data available',
35 },
36 'Execution errors': {
37 '6400': 'No specific diagnosis',
38 '6581': 'Memory failure',
39 },
40 'Checking errors': {
41 '6700': 'Wrong length in Lc',
42 },
43 'Functions in CLA not supported': {
44 '6881': 'Logical channel not supported or active',
45 '6882': 'Secure messaging not supported',
46 },
47 'Command not allowed': {
48 '6982': 'Security Status not satisfied',
49 '6985': 'Conditions of use not satisfied',
50 },
51 'Wrong parameters': {
52 '6a80': 'Incorrect values in command data',
53 '6a81': 'Function not supported e.g. card Life Cycle State is CARD_LOCKED',
54 '6a82': 'Application not found',
55 '6a84': 'Not enough memory space',
56 '6a86': 'Incorrect P1 P2',
57 '6a88': 'Referenced data not found',
58 },
59 'GlobalPlatform': {
60 '6d00': 'Invalid instruction',
61 '6e00': 'Invalid class',
62 },
63 'Application errors': {
64 '9484': 'Algorithm not supported',
65 '9485': 'Invalid key check value',
66 },
67}
68
69# GlobalPlatform 2.1.1 Section 9.1.6
70KeyType = Enum(Byte, des=0x80,
71 rsa_public_exponent_e_cleartex=0xA0,
72 rsa_modulus_n_cleartext=0xA1,
73 rsa_modulus_n=0xA2,
74 rsa_private_exponent_d=0xA3,
75 rsa_chines_remainder_p=0xA4,
76 rsa_chines_remainder_q=0xA5,
77 rsa_chines_remainder_pq=0xA6,
78 rsa_chines_remainder_dpi=0xA7,
79 rsa_chines_remainder_dqi=0xA8,
80 not_available=0xff)
81
82# GlobalPlatform 2.1.1 Section 9.3.3.1
Harald Welte34eb5042022-02-21 17:19:28 +010083class KeyInformationData(BER_TLV_IE, tag=0xc0):
Harald Welte45626272023-12-23 21:02:44 +010084 _test_de_encode = [
85 ( 'c00401708010', {"key_identifier": 1, "key_version_number": 112, "key_types": [ {"length": 16, "type": "des"} ]} ),
86 ( 'c00402708010', {"key_identifier": 2, "key_version_number": 112, "key_types": [ {"length": 16, "type": "des"} ]} ),
87 ( 'c00403708010', {"key_identifier": 3, "key_version_number": 112, "key_types": [ {"length": 16, "type": "des"} ]} ),
88 ( 'c00401018010', {"key_identifier": 1, "key_version_number": 1, "key_types": [ {"length": 16, "type": "des"} ]} ),
89 ( 'c00402018010', {"key_identifier": 2, "key_version_number": 1, "key_types": [ {"length": 16, "type": "des"} ]} ),
90 ( 'c00403018010', {"key_identifier": 3, "key_version_number": 1, "key_types": [ {"length": 16, "type": "des"} ]} ),
91 ( 'c00401028010', {"key_identifier": 1, "key_version_number": 2, "key_types": [ {"length": 16, "type": "des"} ]} ),
92 ( 'c00402028010', {"key_identifier": 2, "key_version_number": 2, "key_types": [ {"length": 16, "type": "des"} ]} ),
93 ( 'c00403038010', {"key_identifier": 3, "key_version_number": 3, "key_types": [ {"length": 16, "type": "des"} ]} ),
94 ( 'c00401038010', {"key_identifier": 1, "key_version_number": 3, "key_types": [ {"length": 16, "type": "des"} ]} ),
95 ( 'c00402038010', {"key_identifier": 2, "key_version_number": 3, "key_types": [ {"length": 16, "type": "des"} ]} ),
96 ]
Harald Welte2538dd72023-12-23 21:01:39 +010097 KeyTypeLen = Struct('type'/KeyType, 'length'/Int8ub)
Harald Welte34eb5042022-02-21 17:19:28 +010098 _construct = Struct('key_identifier'/Byte, 'key_version_number'/Byte,
Harald Welte2538dd72023-12-23 21:01:39 +010099 'key_types'/GreedyRange(KeyTypeLen))
Harald Welte34eb5042022-02-21 17:19:28 +0100100class KeyInformation(BER_TLV_IE, tag=0xe0, nested=[KeyInformationData]):
101 pass
102
103# card data sample, returned in response to GET DATA (80ca006600):
104# 66 31
105# 73 2f
106# 06 07
107# 2a864886fc6b01
108# 60 0c
109# 06 0a
110# 2a864886fc6b02020101
111# 63 09
112# 06 07
113# 2a864886fc6b03
114# 64 0b
115# 06 09
116# 2a864886fc6b040215
117
118# GlobalPlatform 2.1.1 Table F-1
119class ObjectIdentifier(BER_TLV_IE, tag=0x06):
120 _construct = GreedyBytes
121class CardManagementTypeAndVersion(BER_TLV_IE, tag=0x60, nested=[ObjectIdentifier]):
122 pass
123class CardIdentificationScheme(BER_TLV_IE, tag=0x63, nested=[ObjectIdentifier]):
124 pass
125class SecureChannelProtocolOfISD(BER_TLV_IE, tag=0x64, nested=[ObjectIdentifier]):
126 pass
127class CardConfigurationDetails(BER_TLV_IE, tag=0x65):
128 _construct = GreedyBytes
129class CardChipDetails(BER_TLV_IE, tag=0x66):
130 _construct = GreedyBytes
131class CardRecognitionData(BER_TLV_IE, tag=0x73, nested=[ObjectIdentifier,
132 CardManagementTypeAndVersion,
133 CardIdentificationScheme,
134 SecureChannelProtocolOfISD,
135 CardConfigurationDetails,
136 CardChipDetails]):
137 pass
138class CardData(BER_TLV_IE, tag=0x66, nested=[CardRecognitionData]):
139 pass
140
141# GlobalPlatform 2.1.1 Table F-2
142class SecureChannelProtocolOfSelectedSD(BER_TLV_IE, tag=0x64, nested=[ObjectIdentifier]):
143 pass
144class SecurityDomainMgmtData(BER_TLV_IE, tag=0x73, nested=[CardManagementTypeAndVersion,
145 CardIdentificationScheme,
146 SecureChannelProtocolOfSelectedSD,
147 CardConfigurationDetails,
148 CardChipDetails]):
149 pass
150
151# GlobalPlatform 2.1.1 Section 9.1.1
152IsdLifeCycleState = Enum(Byte, op_ready=0x01, initialized=0x07, secured=0x0f,
153 card_locked = 0x7f, terminated=0xff)
154
155# GlobalPlatform 2.1.1 Section 9.9.3.1
156class ApplicationID(BER_TLV_IE, tag=0x84):
157 _construct = GreedyBytes
158
159# GlobalPlatform 2.1.1 Section 9.9.3.1
160class SecurityDomainManagementData(BER_TLV_IE, tag=0x73):
161 _construct = GreedyBytes
162
163# GlobalPlatform 2.1.1 Section 9.9.3.1
164class ApplicationProductionLifeCycleData(BER_TLV_IE, tag=0x9f6e):
165 _construct = GreedyBytes
166
167# GlobalPlatform 2.1.1 Section 9.9.3.1
168class MaximumLengthOfDataFieldInCommandMessage(BER_TLV_IE, tag=0x9f65):
169 _construct = GreedyInteger()
170
171# GlobalPlatform 2.1.1 Section 9.9.3.1
172class ProprietaryData(BER_TLV_IE, tag=0xA5, nested=[SecurityDomainManagementData,
173 ApplicationProductionLifeCycleData,
174 MaximumLengthOfDataFieldInCommandMessage]):
175 pass
176
Harald Welte268a2022023-10-22 13:12:11 +0200177# explicitly define this list and give it a name so pySim.euicc can reference it
178FciTemplateNestedList = [ApplicationID, SecurityDomainManagementData,
179 ApplicationProductionLifeCycleData,
180 MaximumLengthOfDataFieldInCommandMessage,
181 ProprietaryData]
182
Harald Welte34eb5042022-02-21 17:19:28 +0100183# GlobalPlatform 2.1.1 Section 9.9.3.1
Harald Welte268a2022023-10-22 13:12:11 +0200184class FciTemplate(BER_TLV_IE, tag=0x6f, nested=FciTemplateNestedList):
Harald Welte34eb5042022-02-21 17:19:28 +0100185 pass
186
187class IssuerIdentificationNumber(BER_TLV_IE, tag=0x42):
188 _construct = BcdAdapter(GreedyBytes)
189
190class CardImageNumber(BER_TLV_IE, tag=0x45):
191 _construct = BcdAdapter(GreedyBytes)
192
193class SequenceCounterOfDefaultKvn(BER_TLV_IE, tag=0xc1):
194 _construct = GreedyInteger()
195
196class ConfirmationCounter(BER_TLV_IE, tag=0xc2):
197 _construct = GreedyInteger()
198
199# Collection of all the data objects we can get from GET DATA
200class DataCollection(TLV_IE_Collection, nested=[IssuerIdentificationNumber,
201 CardImageNumber,
202 CardData,
203 KeyInformation,
204 SequenceCounterOfDefaultKvn,
205 ConfirmationCounter]):
206 pass
207
208def decode_select_response(resp_hex: str) -> object:
209 t = FciTemplate()
210 t.from_tlv(h2b(resp_hex))
211 d = t.to_dict()
212 return flatten_dict_lists(d['fci_template'])
213
214# Application Dedicated File of a Security Domain
215class ADF_SD(CardADF):
216 def __init__(self, aid: str, name: str, desc: str):
217 super().__init__(aid=aid, fid=None, sfid=None, name=name, desc=desc)
218 self.shell_commands += [self.AddlShellCommands()]
219
220 @staticmethod
221 def decode_select_response(res_hex: str) -> object:
222 return decode_select_response(res_hex)
223
224 @with_default_category('Application-Specific Commands')
225 class AddlShellCommands(CommandSet):
226 def __init__(self):
227 super().__init__()
228
Harald Weltefdcf3c52023-07-11 08:52:39 +0200229 get_data_parser = argparse.ArgumentParser()
230 get_data_parser.add_argument('data_object_name', type=str,
231 help='Name of the data object to be retrieved from the card')
232
233 @cmd2.with_argparser(get_data_parser)
Harald Welte34eb5042022-02-21 17:19:28 +0100234 def do_get_data(self, opts):
Harald Weltefdcf3c52023-07-11 08:52:39 +0200235 """Perform the GlobalPlatform GET DATA command in order to obtain some card-specific data."""
236 tlv_cls_name = opts.data_object_name
237 try:
238 tlv_cls = DataCollection().members_by_name[tlv_cls_name]
239 except KeyError:
240 do_names = [camel_to_snake(str(x.__name__)) for x in DataCollection.possible_nested]
241 self._cmd.poutput('Unknown data object "%s", available options: %s' % (tlv_cls_name,
242 do_names))
243 return
Harald Welte46255122023-10-21 23:40:42 +0200244 (data, sw) = self._cmd.lchan.scc.get_data(cla=0x80, tag=tlv_cls.tag)
Harald Welte34eb5042022-02-21 17:19:28 +0100245 ie = tlv_cls()
246 ie.from_tlv(h2b(data))
247 self._cmd.poutput_json(ie.to_dict())
248
249 def complete_get_data(self, text, line, begidx, endidx) -> List[str]:
Harald Welte30de9fd2023-07-09 21:25:14 +0200250 data_dict = {camel_to_snake(str(x.__name__)): x for x in DataCollection.possible_nested}
Harald Welte34eb5042022-02-21 17:19:28 +0100251 index_dict = {1: data_dict}
252 return self._cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict)
253
254# Card Application of a Security Domain
255class CardApplicationSD(CardApplication):
256 def __init__(self, aid: str, name: str, desc: str):
257 super().__init__(name, adf=ADF_SD(aid, name, desc), sw=sw_table)
258
259# Card Application of Issuer Security Domain
260class CardApplicationISD(CardApplicationSD):
261 # FIXME: ISD AID is not static, but could be different. One can select the empty
262 # application using '00a4040000' and then parse the response FCI to get the ISD AID
263 def __init__(self, aid='a000000003000000'):
264 super().__init__(aid=aid, name='ADF.ISD', desc='Issuer Security Domain')
265
266#class CardProfileGlobalPlatform(CardProfile):
267# ORDER = 23
268#
269# def __init__(self, name='GlobalPlatform'):
270# super().__init__(name, desc='GlobalPlatfomr 2.1.1', cla=['00','80','84'], sw=sw_table)