blob: 0ceb88407a8c3e979124a6bc555977f4cdc917dd [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
Harald Welte722c11a2023-12-23 21:13:43 +01004(C) 2022-2023 by Harald Welte <laforge@osmocom.org>
Harald Welte34eb5042022-02-21 17:19:28 +01005
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,
Harald Welte722c11a2023-12-23 21:13:43 +010071 tls_psk=0x85, # v2.3.1 Section 11.1.8
72 aes=0x88, # v2.3.1 Section 11.1.8
73 hmac_sha1=0x90, # v2.3.1 Section 11.1.8
74 hmac_sha1_160=0x91, # v2.3.1 Section 11.1.8
Harald Welte34eb5042022-02-21 17:19:28 +010075 rsa_public_exponent_e_cleartex=0xA0,
76 rsa_modulus_n_cleartext=0xA1,
77 rsa_modulus_n=0xA2,
78 rsa_private_exponent_d=0xA3,
79 rsa_chines_remainder_p=0xA4,
80 rsa_chines_remainder_q=0xA5,
81 rsa_chines_remainder_pq=0xA6,
82 rsa_chines_remainder_dpi=0xA7,
83 rsa_chines_remainder_dqi=0xA8,
Harald Welte722c11a2023-12-23 21:13:43 +010084 ecc_public_key=0xB0, # v2.3.1 Section 11.1.8
85 ecc_private_key=0xB1, # v2.3.1 Section 11.1.8
86 ecc_field_parameter_p=0xB2, # v2.3.1 Section 11.1.8
87 ecc_field_parameter_a=0xB3, # v2.3.1 Section 11.1.8
88 ecc_field_parameter_b=0xB4, # v2.3.1 Section 11.1.8
89 ecc_field_parameter_g=0xB5, # v2.3.1 Section 11.1.8
90 ecc_field_parameter_n=0xB6, # v2.3.1 Section 11.1.8
91 ecc_field_parameter_k=0xB7, # v2.3.1 Section 11.1.8
92 ecc_key_parameters_reference=0xF0, # v2.3.1 Section 11.1.8
Harald Welte34eb5042022-02-21 17:19:28 +010093 not_available=0xff)
94
95# GlobalPlatform 2.1.1 Section 9.3.3.1
Harald Welte34eb5042022-02-21 17:19:28 +010096class KeyInformationData(BER_TLV_IE, tag=0xc0):
Harald Welte45626272023-12-23 21:02:44 +010097 _test_de_encode = [
98 ( 'c00401708010', {"key_identifier": 1, "key_version_number": 112, "key_types": [ {"length": 16, "type": "des"} ]} ),
99 ( 'c00402708010', {"key_identifier": 2, "key_version_number": 112, "key_types": [ {"length": 16, "type": "des"} ]} ),
100 ( 'c00403708010', {"key_identifier": 3, "key_version_number": 112, "key_types": [ {"length": 16, "type": "des"} ]} ),
101 ( 'c00401018010', {"key_identifier": 1, "key_version_number": 1, "key_types": [ {"length": 16, "type": "des"} ]} ),
102 ( 'c00402018010', {"key_identifier": 2, "key_version_number": 1, "key_types": [ {"length": 16, "type": "des"} ]} ),
103 ( 'c00403018010', {"key_identifier": 3, "key_version_number": 1, "key_types": [ {"length": 16, "type": "des"} ]} ),
104 ( 'c00401028010', {"key_identifier": 1, "key_version_number": 2, "key_types": [ {"length": 16, "type": "des"} ]} ),
105 ( 'c00402028010', {"key_identifier": 2, "key_version_number": 2, "key_types": [ {"length": 16, "type": "des"} ]} ),
106 ( 'c00403038010', {"key_identifier": 3, "key_version_number": 3, "key_types": [ {"length": 16, "type": "des"} ]} ),
107 ( 'c00401038010', {"key_identifier": 1, "key_version_number": 3, "key_types": [ {"length": 16, "type": "des"} ]} ),
108 ( 'c00402038010', {"key_identifier": 2, "key_version_number": 3, "key_types": [ {"length": 16, "type": "des"} ]} ),
Harald Welte722c11a2023-12-23 21:13:43 +0100109 ( 'c00402038810', {"key_identifier": 2, "key_version_number": 3, "key_types": [ {"length": 16, "type": "aes"} ]} ),
Harald Welte45626272023-12-23 21:02:44 +0100110 ]
Harald Welte2538dd72023-12-23 21:01:39 +0100111 KeyTypeLen = Struct('type'/KeyType, 'length'/Int8ub)
Harald Welte34eb5042022-02-21 17:19:28 +0100112 _construct = Struct('key_identifier'/Byte, 'key_version_number'/Byte,
Harald Welte2538dd72023-12-23 21:01:39 +0100113 'key_types'/GreedyRange(KeyTypeLen))
Harald Welte34eb5042022-02-21 17:19:28 +0100114class KeyInformation(BER_TLV_IE, tag=0xe0, nested=[KeyInformationData]):
115 pass
116
117# card data sample, returned in response to GET DATA (80ca006600):
118# 66 31
119# 73 2f
120# 06 07
121# 2a864886fc6b01
122# 60 0c
123# 06 0a
124# 2a864886fc6b02020101
125# 63 09
126# 06 07
127# 2a864886fc6b03
128# 64 0b
129# 06 09
130# 2a864886fc6b040215
131
132# GlobalPlatform 2.1.1 Table F-1
133class ObjectIdentifier(BER_TLV_IE, tag=0x06):
134 _construct = GreedyBytes
135class CardManagementTypeAndVersion(BER_TLV_IE, tag=0x60, nested=[ObjectIdentifier]):
136 pass
137class CardIdentificationScheme(BER_TLV_IE, tag=0x63, nested=[ObjectIdentifier]):
138 pass
139class SecureChannelProtocolOfISD(BER_TLV_IE, tag=0x64, nested=[ObjectIdentifier]):
140 pass
141class CardConfigurationDetails(BER_TLV_IE, tag=0x65):
142 _construct = GreedyBytes
143class CardChipDetails(BER_TLV_IE, tag=0x66):
144 _construct = GreedyBytes
145class CardRecognitionData(BER_TLV_IE, tag=0x73, nested=[ObjectIdentifier,
146 CardManagementTypeAndVersion,
147 CardIdentificationScheme,
148 SecureChannelProtocolOfISD,
149 CardConfigurationDetails,
150 CardChipDetails]):
151 pass
152class CardData(BER_TLV_IE, tag=0x66, nested=[CardRecognitionData]):
153 pass
154
155# GlobalPlatform 2.1.1 Table F-2
156class SecureChannelProtocolOfSelectedSD(BER_TLV_IE, tag=0x64, nested=[ObjectIdentifier]):
157 pass
158class SecurityDomainMgmtData(BER_TLV_IE, tag=0x73, nested=[CardManagementTypeAndVersion,
159 CardIdentificationScheme,
160 SecureChannelProtocolOfSelectedSD,
161 CardConfigurationDetails,
162 CardChipDetails]):
163 pass
164
165# GlobalPlatform 2.1.1 Section 9.1.1
166IsdLifeCycleState = Enum(Byte, op_ready=0x01, initialized=0x07, secured=0x0f,
167 card_locked = 0x7f, terminated=0xff)
168
169# GlobalPlatform 2.1.1 Section 9.9.3.1
170class ApplicationID(BER_TLV_IE, tag=0x84):
171 _construct = GreedyBytes
172
173# GlobalPlatform 2.1.1 Section 9.9.3.1
174class SecurityDomainManagementData(BER_TLV_IE, tag=0x73):
175 _construct = GreedyBytes
176
177# GlobalPlatform 2.1.1 Section 9.9.3.1
178class ApplicationProductionLifeCycleData(BER_TLV_IE, tag=0x9f6e):
179 _construct = GreedyBytes
180
181# GlobalPlatform 2.1.1 Section 9.9.3.1
182class MaximumLengthOfDataFieldInCommandMessage(BER_TLV_IE, tag=0x9f65):
183 _construct = GreedyInteger()
184
185# GlobalPlatform 2.1.1 Section 9.9.3.1
186class ProprietaryData(BER_TLV_IE, tag=0xA5, nested=[SecurityDomainManagementData,
187 ApplicationProductionLifeCycleData,
188 MaximumLengthOfDataFieldInCommandMessage]):
189 pass
190
Harald Welte268a2022023-10-22 13:12:11 +0200191# explicitly define this list and give it a name so pySim.euicc can reference it
192FciTemplateNestedList = [ApplicationID, SecurityDomainManagementData,
193 ApplicationProductionLifeCycleData,
194 MaximumLengthOfDataFieldInCommandMessage,
195 ProprietaryData]
196
Harald Welte34eb5042022-02-21 17:19:28 +0100197# GlobalPlatform 2.1.1 Section 9.9.3.1
Harald Welte268a2022023-10-22 13:12:11 +0200198class FciTemplate(BER_TLV_IE, tag=0x6f, nested=FciTemplateNestedList):
Harald Welte34eb5042022-02-21 17:19:28 +0100199 pass
200
201class IssuerIdentificationNumber(BER_TLV_IE, tag=0x42):
202 _construct = BcdAdapter(GreedyBytes)
203
204class CardImageNumber(BER_TLV_IE, tag=0x45):
205 _construct = BcdAdapter(GreedyBytes)
206
207class SequenceCounterOfDefaultKvn(BER_TLV_IE, tag=0xc1):
208 _construct = GreedyInteger()
209
210class ConfirmationCounter(BER_TLV_IE, tag=0xc2):
211 _construct = GreedyInteger()
212
213# Collection of all the data objects we can get from GET DATA
214class DataCollection(TLV_IE_Collection, nested=[IssuerIdentificationNumber,
215 CardImageNumber,
216 CardData,
217 KeyInformation,
218 SequenceCounterOfDefaultKvn,
219 ConfirmationCounter]):
220 pass
221
222def decode_select_response(resp_hex: str) -> object:
223 t = FciTemplate()
224 t.from_tlv(h2b(resp_hex))
225 d = t.to_dict()
226 return flatten_dict_lists(d['fci_template'])
227
228# Application Dedicated File of a Security Domain
229class ADF_SD(CardADF):
230 def __init__(self, aid: str, name: str, desc: str):
231 super().__init__(aid=aid, fid=None, sfid=None, name=name, desc=desc)
232 self.shell_commands += [self.AddlShellCommands()]
233
234 @staticmethod
235 def decode_select_response(res_hex: str) -> object:
236 return decode_select_response(res_hex)
237
238 @with_default_category('Application-Specific Commands')
239 class AddlShellCommands(CommandSet):
240 def __init__(self):
241 super().__init__()
242
Harald Weltefdcf3c52023-07-11 08:52:39 +0200243 get_data_parser = argparse.ArgumentParser()
244 get_data_parser.add_argument('data_object_name', type=str,
245 help='Name of the data object to be retrieved from the card')
246
247 @cmd2.with_argparser(get_data_parser)
Harald Welte34eb5042022-02-21 17:19:28 +0100248 def do_get_data(self, opts):
Harald Weltefdcf3c52023-07-11 08:52:39 +0200249 """Perform the GlobalPlatform GET DATA command in order to obtain some card-specific data."""
250 tlv_cls_name = opts.data_object_name
251 try:
252 tlv_cls = DataCollection().members_by_name[tlv_cls_name]
253 except KeyError:
254 do_names = [camel_to_snake(str(x.__name__)) for x in DataCollection.possible_nested]
255 self._cmd.poutput('Unknown data object "%s", available options: %s' % (tlv_cls_name,
256 do_names))
257 return
Harald Welte46255122023-10-21 23:40:42 +0200258 (data, sw) = self._cmd.lchan.scc.get_data(cla=0x80, tag=tlv_cls.tag)
Harald Welte34eb5042022-02-21 17:19:28 +0100259 ie = tlv_cls()
260 ie.from_tlv(h2b(data))
261 self._cmd.poutput_json(ie.to_dict())
262
263 def complete_get_data(self, text, line, begidx, endidx) -> List[str]:
Harald Welte30de9fd2023-07-09 21:25:14 +0200264 data_dict = {camel_to_snake(str(x.__name__)): x for x in DataCollection.possible_nested}
Harald Welte34eb5042022-02-21 17:19:28 +0100265 index_dict = {1: data_dict}
266 return self._cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict)
267
268# Card Application of a Security Domain
269class CardApplicationSD(CardApplication):
Harald Weltedffe7af2023-12-23 17:23:52 +0100270 __intermediate = True
Harald Welte34eb5042022-02-21 17:19:28 +0100271 def __init__(self, aid: str, name: str, desc: str):
272 super().__init__(name, adf=ADF_SD(aid, name, desc), sw=sw_table)
273
274# Card Application of Issuer Security Domain
275class CardApplicationISD(CardApplicationSD):
276 # FIXME: ISD AID is not static, but could be different. One can select the empty
277 # application using '00a4040000' and then parse the response FCI to get the ISD AID
278 def __init__(self, aid='a000000003000000'):
279 super().__init__(aid=aid, name='ADF.ISD', desc='Issuer Security Domain')
280
281#class CardProfileGlobalPlatform(CardProfile):
282# ORDER = 23
283#
284# def __init__(self, name='GlobalPlatform'):
285# super().__init__(name, desc='GlobalPlatfomr 2.1.1', cla=['00','80','84'], sw=sw_table)