blob: b4ac74713dfdd5429eaa522851576af451511593 [file] [log] [blame]
Harald Welte95ce6b12021-10-20 18:40:54 +02001# -*- coding: utf-8 -*-
2
3# without this, pylint will fail when inner classes are used
4# within the 'nested' kwarg of our TlvMeta metaclass on python 3.7 :(
5# pylint: disable=undefined-variable
6
7"""
8Support for the Secure Element Access Control, specifically the ARA-M inside an UICC.
9"""
10
11#
12# Copyright (C) 2021 Harald Welte <laforge@osmocom.org>
13#
14# This program is free software: you can redistribute it and/or modify
15# it under the terms of the GNU General Public License as published by
16# the Free Software Foundation, either version 2 of the License, or
17# (at your option) any later version.
18#
19# This program is distributed in the hope that it will be useful,
20# but WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22# GNU General Public License for more details.
23#
24# You should have received a copy of the GNU General Public License
25# along with this program. If not, see <http://www.gnu.org/licenses/>.
26#
27
28
29from construct import *
30from construct import Optional as COptional
31from pySim.construct import *
32from pySim.filesystem import *
33from pySim.tlv import *
34
35# various BER-TLV encoded Data Objects (DOs)
36
Harald Weltec91085e2022-02-10 18:05:45 +010037
Harald Welte95ce6b12021-10-20 18:40:54 +020038class AidRefDO(BER_TLV_IE, tag=0x4f):
39 # SEID v1.1 Table 6-3
40 _construct = HexAdapter(GreedyBytes)
41
Harald Weltec91085e2022-02-10 18:05:45 +010042
Harald Welte95ce6b12021-10-20 18:40:54 +020043class AidRefEmptyDO(BER_TLV_IE, tag=0xc0):
44 # SEID v1.1 Table 6-3
45 pass
46
Harald Weltec91085e2022-02-10 18:05:45 +010047
Harald Welte95ce6b12021-10-20 18:40:54 +020048class DevAppIdRefDO(BER_TLV_IE, tag=0xc1):
49 # SEID v1.1 Table 6-4
50 _construct = HexAdapter(GreedyBytes)
51
Harald Weltec91085e2022-02-10 18:05:45 +010052
Harald Welte95ce6b12021-10-20 18:40:54 +020053class PkgRefDO(BER_TLV_IE, tag=0xca):
54 # Android UICC Carrier Privileges specific extension, see https://source.android.com/devices/tech/config/uicc
55 _construct = Struct('package_name_string'/GreedyString("ascii"))
56
Harald Weltec91085e2022-02-10 18:05:45 +010057
58class RefDO(BER_TLV_IE, tag=0xe1, nested=[AidRefDO, AidRefEmptyDO, DevAppIdRefDO, PkgRefDO]):
Harald Welte95ce6b12021-10-20 18:40:54 +020059 # SEID v1.1 Table 6-5
60 pass
61
Harald Weltec91085e2022-02-10 18:05:45 +010062
Harald Welte95ce6b12021-10-20 18:40:54 +020063class ApduArDO(BER_TLV_IE, tag=0xd0):
64 # SEID v1.1 Table 6-8
Harald Weltec91085e2022-02-10 18:05:45 +010065 def _from_bytes(self, do: bytes):
Harald Welte95ce6b12021-10-20 18:40:54 +020066 if len(do) == 1:
67 if do[0] == 0x00:
68 self.decoded = {'generic_access_rule': 'never'}
69 return self.decoded
70 elif do[0] == 0x01:
71 self.decoded = {'generic_access_rule': 'always'}
72 return self.decoded
73 else:
74 return ValueError('Invalid 1-byte generic APDU access rule')
75 else:
76 if len(do) % 8:
77 return ValueError('Invalid non-modulo-8 length of APDU filter: %d' % len(do))
78 self.decoded['apdu_filter'] = []
79 offset = 0
80 while offset < len(do):
81 self.decoded['apdu_filter'] += {'header': b2h(do[offset:offset+4]),
82 'mask': b2h(do[offset+4:offset+8])}
83 self.decoded = res
84 return res
Harald Weltec91085e2022-02-10 18:05:45 +010085
Harald Welte95ce6b12021-10-20 18:40:54 +020086 def _to_bytes(self):
87 if 'generic_access_rule' in self.decoded:
88 if self.decoded['generic_access_rule'] == 'never':
89 return b'\x00'
90 elif self.decoded['generic_access_rule'] == 'always':
91 return b'\x01'
92 else:
93 return ValueError('Invalid 1-byte generic APDU access rule')
94 else:
95 if not 'apdu_filter' in self.decoded:
96 return ValueError('Invalid APDU AR DO')
97 filters = self.decoded['apdu_filter']
98 res = b''
99 for f in filters:
100 if not 'header' in f or not 'mask' in f:
101 return ValueError('APDU filter must contain header and mask')
102 header_b = h2b(f['header'])
103 mask_b = h2b(f['mask'])
104 if len(header_b) != 4 or len(mask_b) != 4:
105 return ValueError('APDU filter header and mask must each be 4 bytes')
106 res += header_b + mask_b
107 return res
108
Harald Weltec91085e2022-02-10 18:05:45 +0100109
Harald Welte95ce6b12021-10-20 18:40:54 +0200110class NfcArDO(BER_TLV_IE, tag=0xd1):
111 # SEID v1.1 Table 6-9
Harald Weltec91085e2022-02-10 18:05:45 +0100112 _construct = Struct('nfc_event_access_rule' /
113 Enum(Int8ub, never=0, always=1))
114
Harald Welte95ce6b12021-10-20 18:40:54 +0200115
116class PermArDO(BER_TLV_IE, tag=0xdb):
117 # Android UICC Carrier Privileges specific extension, see https://source.android.com/devices/tech/config/uicc
118 _construct = Struct('permissions'/HexAdapter(Bytes(8)))
119
Harald Weltec91085e2022-02-10 18:05:45 +0100120
Harald Welte95ce6b12021-10-20 18:40:54 +0200121class ArDO(BER_TLV_IE, tag=0xe3, nested=[ApduArDO, NfcArDO, PermArDO]):
122 # SEID v1.1 Table 6-7
123 pass
124
Harald Weltec91085e2022-02-10 18:05:45 +0100125
Harald Welte95ce6b12021-10-20 18:40:54 +0200126class RefArDO(BER_TLV_IE, tag=0xe2, nested=[RefDO, ArDO]):
127 # SEID v1.1 Table 6-6
128 pass
129
Harald Weltec91085e2022-02-10 18:05:45 +0100130
Harald Welte95ce6b12021-10-20 18:40:54 +0200131class ResponseAllRefArDO(BER_TLV_IE, tag=0xff40, nested=[RefArDO]):
132 # SEID v1.1 Table 4-2
133 pass
134
Harald Weltec91085e2022-02-10 18:05:45 +0100135
Harald Welte95ce6b12021-10-20 18:40:54 +0200136class ResponseArDO(BER_TLV_IE, tag=0xff50, nested=[ArDO]):
137 # SEID v1.1 Table 4-3
138 pass
139
Harald Weltec91085e2022-02-10 18:05:45 +0100140
Harald Welte95ce6b12021-10-20 18:40:54 +0200141class ResponseRefreshTagDO(BER_TLV_IE, tag=0xdf20):
142 # SEID v1.1 Table 4-4
143 _construct = Struct('refresh_tag'/HexAdapter(Bytes(8)))
144
Harald Weltec91085e2022-02-10 18:05:45 +0100145
Harald Welte95ce6b12021-10-20 18:40:54 +0200146class DeviceInterfaceVersionDO(BER_TLV_IE, tag=0xe6):
147 # SEID v1.1 Table 6-12
148 _construct = Struct('major'/Int8ub, 'minor'/Int8ub, 'patch'/Int8ub)
149
Harald Weltec91085e2022-02-10 18:05:45 +0100150
Harald Welte95ce6b12021-10-20 18:40:54 +0200151class DeviceConfigDO(BER_TLV_IE, tag=0xe4, nested=[DeviceInterfaceVersionDO]):
152 # SEID v1.1 Table 6-10
153 pass
154
Harald Weltec91085e2022-02-10 18:05:45 +0100155
Harald Welte95ce6b12021-10-20 18:40:54 +0200156class ResponseDeviceConfigDO(BER_TLV_IE, tag=0xff7f, nested=[DeviceConfigDO]):
157 # SEID v1.1 Table 5-14
158 pass
159
Harald Weltec91085e2022-02-10 18:05:45 +0100160
Harald Welte95ce6b12021-10-20 18:40:54 +0200161class AramConfigDO(BER_TLV_IE, tag=0xe5, nested=[DeviceInterfaceVersionDO]):
162 # SEID v1.1 Table 6-11
163 pass
164
Harald Weltec91085e2022-02-10 18:05:45 +0100165
Harald Welte95ce6b12021-10-20 18:40:54 +0200166class ResponseAramConfigDO(BER_TLV_IE, tag=0xdf21, nested=[AramConfigDO]):
167 # SEID v1.1 Table 4-5
168 pass
169
Harald Weltec91085e2022-02-10 18:05:45 +0100170
Harald Welte95ce6b12021-10-20 18:40:54 +0200171class CommandStoreRefArDO(BER_TLV_IE, tag=0xf0, nested=[RefArDO]):
172 # SEID v1.1 Table 5-2
173 pass
174
Harald Weltec91085e2022-02-10 18:05:45 +0100175
Harald Welte95ce6b12021-10-20 18:40:54 +0200176class CommandDelete(BER_TLV_IE, tag=0xf1, nested=[AidRefDO, AidRefEmptyDO, RefDO, RefArDO]):
177 # SEID v1.1 Table 5-4
178 pass
179
Harald Weltec91085e2022-02-10 18:05:45 +0100180
Harald Welte95ce6b12021-10-20 18:40:54 +0200181class CommandUpdateRefreshTagDO(BER_TLV_IE, tag=0xf2):
182 # SEID V1.1 Table 5-6
183 pass
184
Harald Weltec91085e2022-02-10 18:05:45 +0100185
Harald Welte95ce6b12021-10-20 18:40:54 +0200186class CommandRegisterClientAidsDO(BER_TLV_IE, tag=0xf7, nested=[AidRefDO, AidRefEmptyDO]):
187 # SEID v1.1 Table 5-7
188 pass
189
Harald Weltec91085e2022-02-10 18:05:45 +0100190
Harald Welte95ce6b12021-10-20 18:40:54 +0200191class CommandGet(BER_TLV_IE, tag=0xf3, nested=[AidRefDO, AidRefEmptyDO]):
192 # SEID v1.1 Table 5-8
193 pass
194
Harald Weltec91085e2022-02-10 18:05:45 +0100195
Harald Welte95ce6b12021-10-20 18:40:54 +0200196class CommandGetAll(BER_TLV_IE, tag=0xf4):
197 # SEID v1.1 Table 5-9
198 pass
199
Harald Weltec91085e2022-02-10 18:05:45 +0100200
Harald Welte95ce6b12021-10-20 18:40:54 +0200201class CommandGetClientAidsDO(BER_TLV_IE, tag=0xf6):
202 # SEID v1.1 Table 5-10
203 pass
204
Harald Weltec91085e2022-02-10 18:05:45 +0100205
Harald Welte95ce6b12021-10-20 18:40:54 +0200206class CommandGetNext(BER_TLV_IE, tag=0xf5):
207 # SEID v1.1 Table 5-11
208 pass
209
Harald Weltec91085e2022-02-10 18:05:45 +0100210
Harald Welte95ce6b12021-10-20 18:40:54 +0200211class CommandGetDeviceConfigDO(BER_TLV_IE, tag=0xf8):
212 # SEID v1.1 Table 5-12
213 pass
214
Harald Weltec91085e2022-02-10 18:05:45 +0100215
Harald Welte95ce6b12021-10-20 18:40:54 +0200216class ResponseAracAidDO(BER_TLV_IE, tag=0xff70, nested=[AidRefDO, AidRefEmptyDO]):
217 # SEID v1.1 Table 5-13
218 pass
219
Harald Weltec91085e2022-02-10 18:05:45 +0100220
Harald Welte95ce6b12021-10-20 18:40:54 +0200221class BlockDO(BER_TLV_IE, tag=0xe7):
222 # SEID v1.1 Table 6-13
223 _construct = Struct('offset'/Int16ub, 'length'/Int8ub)
224
225
226# SEID v1.1 Table 4-1
227class GetCommandDoCollection(TLV_IE_Collection, nested=[RefDO, DeviceConfigDO]):
228 pass
229
230# SEID v1.1 Table 4-2
Harald Weltec91085e2022-02-10 18:05:45 +0100231
232
Harald Welte95ce6b12021-10-20 18:40:54 +0200233class GetResponseDoCollection(TLV_IE_Collection, nested=[ResponseAllRefArDO, ResponseArDO,
234 ResponseRefreshTagDO, ResponseAramConfigDO]):
235 pass
236
237# SEID v1.1 Table 5-1
Harald Weltec91085e2022-02-10 18:05:45 +0100238
239
Harald Welte95ce6b12021-10-20 18:40:54 +0200240class StoreCommandDoCollection(TLV_IE_Collection,
241 nested=[BlockDO, CommandStoreRefArDO, CommandDelete,
242 CommandUpdateRefreshTagDO, CommandRegisterClientAidsDO,
243 CommandGet, CommandGetAll, CommandGetClientAidsDO,
244 CommandGetNext, CommandGetDeviceConfigDO]):
245 pass
246
247
248# SEID v1.1 Section 5.1.2
249class StoreResponseDoCollection(TLV_IE_Collection,
250 nested=[ResponseAllRefArDO, ResponseAracAidDO, ResponseDeviceConfigDO]):
251 pass
252
Harald Weltec91085e2022-02-10 18:05:45 +0100253
Harald Welte95ce6b12021-10-20 18:40:54 +0200254class ADF_ARAM(CardADF):
255 def __init__(self, aid='a00000015141434c00', name='ADF.ARA-M', fid=None, sfid=None,
256 desc='ARA-M Application'):
257 super().__init__(aid=aid, fid=fid, sfid=sfid, name=name, desc=desc)
258 self.shell_commands += [self.AddlShellCommands()]
259 files = []
260 self.add_files(files)
261
262 @staticmethod
Harald Weltec91085e2022-02-10 18:05:45 +0100263 def xceive_apdu_tlv(tp, hdr: Hexstr, cmd_do, resp_cls, exp_sw='9000'):
Harald Welte95ce6b12021-10-20 18:40:54 +0200264 """Transceive an APDU with the card, transparently encoding the command data from TLV
265 and decoding the response data tlv."""
266 if cmd_do:
267 cmd_do_enc = cmd_do.to_ie()
268 cmd_do_len = len(cmd_do_enc)
269 if cmd_do_len > 255:
270 return ValueError('DO > 255 bytes not supported yet')
271 else:
272 cmd_do_enc = b''
273 cmd_do_len = 0
274 c_apdu = hdr + ('%02x' % cmd_do_len) + b2h(cmd_do_enc)
275 (data, sw) = tp.send_apdu_checksw(c_apdu, exp_sw)
276 if data:
277 if resp_cls:
278 resp_do = resp_cls()
279 resp_do.from_tlv(h2b(data))
280 return resp_do
281 else:
282 return data
283 else:
284 return None
285
286 @staticmethod
287 def store_data(tp, do) -> bytes:
288 """Build the Command APDU for STORE DATA."""
289 return ADF_ARAM.xceive_apdu_tlv(tp, '80e29000', do, StoreResponseDoCollection)
290
291 @staticmethod
292 def get_all(tp):
293 return ADF_ARAM.xceive_apdu_tlv(tp, '80caff40', None, GetResponseDoCollection)
294
295 @staticmethod
296 def get_config(tp, v_major=0, v_minor=0, v_patch=1):
297 cmd_do = DeviceConfigDO()
Harald Weltec91085e2022-02-10 18:05:45 +0100298 cmd_do.from_dict([{'DeviceInterfaceVersionDO': {
299 'major': v_major, 'minor': v_minor, 'patch': v_patch}}])
Harald Welte95ce6b12021-10-20 18:40:54 +0200300 return ADF_ARAM.xceive_apdu_tlv(tp, '80cadf21', cmd_do, ResponseAramConfigDO)
301
302 @with_default_category('Application-Specific Commands')
303 class AddlShellCommands(CommandSet):
304 def __init(self):
305 super().__init__()
306
307 def do_aram_get_all(self, opts):
308 """GET DATA [All] on the ARA-M Applet"""
Harald Welte46255122023-10-21 23:40:42 +0200309 res_do = ADF_ARAM.get_all(self._cmd.lchan.scc._tp)
Harald Welte95ce6b12021-10-20 18:40:54 +0200310 if res_do:
311 self._cmd.poutput_json(res_do.to_dict())
312
313 def do_aram_get_config(self, opts):
Harald Welte12af7932022-02-15 16:39:08 +0100314 """Perform GET DATA [Config] on the ARA-M Applet: Tell it our version and retrieve its version."""
Harald Welte46255122023-10-21 23:40:42 +0200315 res_do = ADF_ARAM.get_config(self._cmd.lchan.scc._tp)
Harald Welte95ce6b12021-10-20 18:40:54 +0200316 if res_do:
317 self._cmd.poutput_json(res_do.to_dict())
318
319 store_ref_ar_do_parse = argparse.ArgumentParser()
320 # REF-DO
Harald Weltec91085e2022-02-10 18:05:45 +0100321 store_ref_ar_do_parse.add_argument(
322 '--device-app-id', required=True, help='Identifies the specific device application that the rule appplies to. Hash of Certificate of Application Provider, or UUID. (20/32 hex bytes)')
Harald Welte95ce6b12021-10-20 18:40:54 +0200323 aid_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()
Harald Weltec91085e2022-02-10 18:05:45 +0100324 aid_grp.add_argument(
325 '--aid', help='Identifies the specific SE application for which rules are to be stored. Can be a partial AID, containing for example only the RID. (5-16 hex bytes)')
326 aid_grp.add_argument('--aid-empty', action='store_true',
327 help='No specific SE application, applies to all applications')
328 store_ref_ar_do_parse.add_argument(
329 '--pkg-ref', help='Full Android Java package name (up to 127 chars ASCII)')
Harald Welte95ce6b12021-10-20 18:40:54 +0200330 # AR-DO
331 apdu_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()
Harald Weltec91085e2022-02-10 18:05:45 +0100332 apdu_grp.add_argument(
333 '--apdu-never', action='store_true', help='APDU access is not allowed')
334 apdu_grp.add_argument(
335 '--apdu-always', action='store_true', help='APDU access is allowed')
336 apdu_grp.add_argument(
337 '--apdu-filter', help='APDU filter: 4 byte CLA/INS/P1/P2 followed by 4 byte mask (8 hex bytes)')
Harald Welte95ce6b12021-10-20 18:40:54 +0200338 nfc_grp = store_ref_ar_do_parse.add_mutually_exclusive_group()
Harald Weltec91085e2022-02-10 18:05:45 +0100339 nfc_grp.add_argument('--nfc-always', action='store_true',
340 help='NFC event access is allowed')
341 nfc_grp.add_argument('--nfc-never', action='store_true',
342 help='NFC event access is not allowed')
343 store_ref_ar_do_parse.add_argument(
344 '--android-permissions', help='Android UICC Carrier Privilege Permissions (8 hex bytes)')
Harald Welte95ce6b12021-10-20 18:40:54 +0200345
346 @cmd2.with_argparser(store_ref_ar_do_parse)
347 def do_aram_store_ref_ar_do(self, opts):
Harald Welte12af7932022-02-15 16:39:08 +0100348 """Perform STORE DATA [Command-Store-REF-AR-DO] to store a (new) access rule."""
Harald Welte95ce6b12021-10-20 18:40:54 +0200349 # REF
350 ref_do_content = []
351 if opts.aid:
Harald Welte30de9fd2023-07-09 21:25:14 +0200352 ref_do_content += [{'aid_ref_do': opts.aid}]
Harald Welte95ce6b12021-10-20 18:40:54 +0200353 elif opts.aid_empty:
Harald Welte30de9fd2023-07-09 21:25:14 +0200354 ref_do_content += [{'aid_ref_empty_do': None}]
355 ref_do_content += [{'dev_app_id_ref_do': opts.device_app_id}]
Harald Welte95ce6b12021-10-20 18:40:54 +0200356 if opts.pkg_ref:
Alexander Couzens2dd59ed2023-07-29 02:29:08 +0200357 ref_do_content += [{'pkg_ref_do': {'package_name_string': opts.pkg_ref}}]
Harald Welte95ce6b12021-10-20 18:40:54 +0200358 # AR
359 ar_do_content = []
360 if opts.apdu_never:
Harald Welte30de9fd2023-07-09 21:25:14 +0200361 ar_do_content += [{'apdu_ar_od': {'generic_access_rule': 'never'}}]
Harald Welte95ce6b12021-10-20 18:40:54 +0200362 elif opts.apdu_always:
Harald Welte30de9fd2023-07-09 21:25:14 +0200363 ar_do_content += [{'apdu_ar_do': {'generic_access_rule': 'always'}}]
Harald Welte95ce6b12021-10-20 18:40:54 +0200364 elif opts.apdu_filter:
365 # TODO: multiple filters
Harald Welte30de9fd2023-07-09 21:25:14 +0200366 ar_do_content += [{'apdu_ar_do': {'apdu_filter': [opts.apdu_filter]}}]
Harald Welte95ce6b12021-10-20 18:40:54 +0200367 if opts.nfc_always:
Harald Welte30de9fd2023-07-09 21:25:14 +0200368 ar_do_content += [{'nfc_ar_do': {'nfc_event_access_rule': 'always'}}]
Harald Welte95ce6b12021-10-20 18:40:54 +0200369 elif opts.nfc_never:
Harald Welte30de9fd2023-07-09 21:25:14 +0200370 ar_do_content += [{'nfc_ar_do': {'nfc_event_access_rule': 'never'}}]
Harald Welte95ce6b12021-10-20 18:40:54 +0200371 if opts.android_permissions:
Harald Welte30de9fd2023-07-09 21:25:14 +0200372 ar_do_content += [{'perm_ar_do': {'permissions': opts.android_permissions}}]
373 d = [{'ref_ar_do': [{'ref_do': ref_do_content}, {'ar_do': ar_do_content}]}]
Harald Welte95ce6b12021-10-20 18:40:54 +0200374 csrado = CommandStoreRefArDO()
375 csrado.from_dict(d)
Harald Welte46255122023-10-21 23:40:42 +0200376 res_do = ADF_ARAM.store_data(self._cmd.lchan.scc._tp, csrado)
Harald Welte95ce6b12021-10-20 18:40:54 +0200377 if res_do:
378 self._cmd.poutput_json(res_do.to_dict())
379
380 def do_aram_delete_all(self, opts):
381 """Perform STORE DATA [Command-Delete[all]] to delete all access rules."""
382 deldo = CommandDelete()
Harald Welte46255122023-10-21 23:40:42 +0200383 res_do = ADF_ARAM.store_data(self._cmd.lchan.scc._tp, deldo)
Harald Welte95ce6b12021-10-20 18:40:54 +0200384 if res_do:
385 self._cmd.poutput_json(res_do.to_dict())
386
387
388# SEAC v1.1 Section 4.1.2.2 + 5.1.2.2
389sw_aram = {
390 'ARA-M': {
391 '6381': 'Rule successfully stored but an access rule already exists',
392 '6382': 'Rule successfully stored bu contained at least one unknown (discarded) BER-TLV',
393 '6581': 'Memory Problem',
394 '6700': 'Wrong Length in Lc',
395 '6981': 'DO is not supported by the ARA-M/ARA-C',
396 '6982': 'Security status not satisfied',
397 '6984': 'Rules have been updated and must be read again / logical channels in use',
398 '6985': 'Conditions not satisfied',
399 '6a80': 'Incorrect values in the command data',
400 '6a84': 'Rules have been updated and must be read again',
401 '6a86': 'Incorrect P1 P2',
402 '6a88': 'Referenced data not found',
403 '6a89': 'Conflicting access rule already exists in the Secure Element',
404 '6d00': 'Invalid instruction',
405 '6e00': 'Invalid class',
406 }
407}
408
Harald Weltec91085e2022-02-10 18:05:45 +0100409
Harald Welte95ce6b12021-10-20 18:40:54 +0200410class CardApplicationARAM(CardApplication):
411 def __init__(self):
Harald Weltec91085e2022-02-10 18:05:45 +0100412 super().__init__('ARA-M', adf=ADF_ARAM(), sw=sw_aram)