blob: 29c1e40ec68691b88e3e8b18ce7e8dba19561f37 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file class_tables.c
2 * simtrace - tables determining APDU case for card emulation. */
3/*
Harald Welte4acaa132016-03-14 15:35:50 +01004 * (C) 2016 by Harald Welte <laforge@gnumonks.org>
5 *
Harald Weltee08da972017-11-13 01:00:26 +09006 * SPDX-License-Identifier: GPL-2.0+
7 *
Harald Welte4acaa132016-03-14 15:35:50 +01008 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2, or
10 * any later version as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
Harald Welte4acaa132016-03-14 15:35:50 +010016 */
17
18#include <stdint.h>
19#include <osmocom/core/utils.h>
20#include <osmocom/sim/class_tables.h>
21
Harald Welte42602ec2023-11-28 21:59:32 +010022static const uint8_t iso7816_ins_tbl[256] = {
Harald Welte4acaa132016-03-14 15:35:50 +010023 [0xB0] = 2, /* READ BIN */
24 [0xD0] = 3, /* WRITE BIN */
25 [0xD6] = 3, /* UPDATE BIN */
26 [0x0E] = 3, /* ERASE BIN */
27 [0xB2] = 2, /* READ REC */
28 [0xD2] = 3, /* WRITE REC */
29 [0xE2] = 3, /* APPEND REC */
30 [0xDC] = 3, /* UPDATE REC */
31 [0xCA] = 2, /* GET DATA */
32 [0xDA] = 3, /* PUT DATA */
33 [0xA4] = 4, /* SELECT FILE */
34 [0x20] = 3, /* VERIFY */
35 [0x88] = 4, /* INT AUTH */
36 [0x82] = 3, /* EXT AUTH */
37 [0x84] = 2, /* GET CHALLENGE */
38 [0x70] = 2, /* MANAGE CHANNEL */
39};
40
41static const struct osim_cla_ins_case iso7816_4_ins_case[] = {
42 {
43 .cla = 0x00,
44 .cla_mask = 0xF0,
45 .ins_tbl = iso7816_ins_tbl,
46 }, {
47 .cla = 0x80, /* 0x80/0x90 */
48 .cla_mask = 0xE0,
49 .ins_tbl = iso7816_ins_tbl,
50 }, {
51 .cla = 0xB0,
52 .cla_mask = 0xF0,
53 .ins_tbl = iso7816_ins_tbl,
54 }, {
55 .cla = 0xC0,
56 .cla_mask = 0xF0,
57 .ins_tbl = iso7816_ins_tbl,
58 },
59};
60
61const struct osim_cla_ins_card_profile osim_iso7816_cic_profile = {
62 .name = "ISO 7816-4",
63 .description = "ISO 7816-4",
64 .cic_arr = iso7816_4_ins_case,
65 .cic_arr_size = ARRAY_SIZE(iso7816_4_ins_case),
66};
67
68static const uint8_t gsm1111_ins_tbl[256] = {
69 [0xA4] = 4, /* SELECT FILE */
70 [0xF2] = 2, /* STATUS */
71 [0xB0] = 2, /* READ BINARY */
72 [0xD6] = 3, /* UPDATE BINARY */
73 [0xB2] = 2, /* READ RECORD */
74 [0xDC] = 3, /* UPDATE RECORD */
75 [0xA2] = 4, /* SEEK */
76 [0x32] = 4, /* INCREASE */
77 [0x20] = 3, /* VERIFY CHV */
78 [0x24] = 3, /* CHANGE CHV */
79 [0x26] = 3, /* DISABLE CHV */
80 [0x28] = 3, /* ENABLE CHV */
81 [0x2C] = 3, /* UNBLOCK CHV */
82 [0x04] = 1, /* INVALIDATE */
83 [0x44] = 1, /* REHABILITATE */
84 [0x88] = 4, /* RUN GSM ALGO */
85 [0xFA] = 1, /* SLEEP */
86 [0xC0] = 2, /* GET RESPONSE */
87 [0x10] = 3, /* TERMINAL PROFILE */
88 [0xC2] = 4, /* ENVELOPE */
89 [0x12] = 2, /* FETCH */
90 [0x14] = 3, /* TERMINAL RESPONSE */
91};
92
93/* According to Table 9 / Section 9.2 of TS 11.11 */
94static const struct osim_cla_ins_case gsm1111_ins_case[] = {
95 {
96 .cla = 0xA0,
97 .cla_mask = 0xFF,
98 .ins_tbl = gsm1111_ins_tbl,
99 },
100};
101
102const struct osim_cla_ins_card_profile osim_gsm1111_cic_profile = {
103 .name = "GSM SIM",
104 .description = "GSM/3GPP TS 11.11",
105 .cic_arr = gsm1111_ins_case,
106 .cic_arr_size = ARRAY_SIZE(gsm1111_ins_case),
107};
108
109/* ETSI TS 102 221, Table 10.5, CLA = 0x0x, 0x4x or 0x6x */
110static const uint8_t uicc_ins_tbl_046[256] = {
Harald Welteca18c9d2019-09-25 17:46:50 +0200111 [0xA4] = 4, /* SELECT FILE */
Harald Welte4acaa132016-03-14 15:35:50 +0100112 [0xB0] = 2, /* READ BINARY */
113 [0xD6] = 3, /* UPDATE BINARY */
114 [0xB2] = 2, /* READ RECORD */
115 [0xDC] = 3, /* UPDATE RECORD */
Harald Welted6a49ce2022-07-20 18:32:19 +0200116 [0xA2] = 4, /* SEARCH RECORD */
Harald Welte4acaa132016-03-14 15:35:50 +0100117 [0x20] = 3, /* VERIFY PIN */
118 [0x24] = 3, /* CHANGE PIN */
119 [0x26] = 3, /* DISABLE PIN */
120 [0x28] = 3, /* ENABLE PIN */
121 [0x2C] = 3, /* UNBLOCK PIN */
122 [0x04] = 1, /* DEACTIVATE FILE */
123 [0x44] = 1, /* ACTIVATE FILE */
124 [0x88] = 4, /* AUTHENTICATE */
125 [0x89] = 4, /* AUTHENTICATE */
126 [0x84] = 2, /* GET CHALLENGE */
127 [0x70] = 2, /* MANAGE CHANNEL */
128 [0x73] = 0x80, /* MANAGE SECURE CHANNEL */
129 [0x75] = 0x80, /* TRANSACT DATA */
130 [0xC0] = 2, /* GET RESPONSE */
131};
132
133static int uicc046_cla_ins_helper(const struct osim_cla_ins_case *cic,
134 const uint8_t *hdr)
135{
136 uint8_t ins = hdr[1];
137 uint8_t p1 = hdr[2];
138 uint8_t p2 = hdr[3];
139 uint8_t p2_cmd;
140
141 switch (ins) {
142 case 0x73: /* MANAGE SECURE CHANNEL */
143 if (p1 == 0x00) /* Retrieve UICC Endpoints */
144 return 2;
145 switch (p1 & 0x07) {
146 case 1: /* Establish SA - Master SA */
147 case 2: /* Establish SA - Conn. SA */
148 case 3: /* Start secure channel SA */
149 p2_cmd = p2 >> 5;
150 if (p2 == 0x80 || p2_cmd == 0) {
151 /* command data */
152 return 3;
153 }
154 if (p2_cmd == 5 || p2_cmd == 1) {
155 /* response data */
156 return 2;
157 }
158 return 0;
159 break;
160 case 4: /* Terminate secure chan SA */
161 return 3;
162 break;
163 }
164 break;
165 case 0x75: /* TRANSACT DATA */
166 if (p1 & 0x04)
167 return 3;
168 else
169 return 2;
170 break;
171 }
172
173 return 0;
174}
175
Karsten Ohme6772dd72022-06-09 02:56:53 +0200176static int gp_cla_ins_helper(const struct osim_cla_ins_case *cic,
177 const uint8_t *hdr)
178{
179 uint8_t ins = hdr[1];
180 uint8_t p1 = hdr[2];
181
182 switch (ins) {
183 case 0xE2: /* STORE DATA */
184 switch (p1 & 0x01) {
185 case 1:
186 return 4;
187 default:
188 return 3;
189 }
190 }
191 return 0;
192}
193
Harald Welte4acaa132016-03-14 15:35:50 +0100194/* ETSI TS 102 221, Table 10.5, CLA = 0x8x, 0xCx or 0xEx */
195static const uint8_t uicc_ins_tbl_8ce[256] = {
196 [0xF2] = 2, /* STATUS */
197 [0x32] = 4, /* INCREASE */
198 [0xCB] = 4, /* RETRIEVE DATA */
199 [0xDB] = 3, /* SET DATA */
200 [0xAA] = 3, /* TERMINAL CAPABILITY */
Harald Welted6a49ce2022-07-20 18:32:19 +0200201 [0x78] = 4, /* GET IDENTITY */
Harald Welte4acaa132016-03-14 15:35:50 +0100202};
203
204/* ETSI TS 102 221, Table 10.5, CLA = 0x80 */
205static const uint8_t uicc_ins_tbl_80[256] = {
206 [0x10] = 3, /* TERMINAL PROFILE */
207 [0xC2] = 4, /* ENVELOPE */
208 [0x12] = 2, /* FETCH */
209 [0x14] = 3, /* TERMINAL RESPONSE */
Harald Welted6a49ce2022-07-20 18:32:19 +0200210 [0x76] = 4, /* SUSPEND UICC */
211 [0x7A] = 4, /* EXCHANGE CAPABILITIES */
Harald Welte4acaa132016-03-14 15:35:50 +0100212};
213
Karsten Ohme6772dd72022-06-09 02:56:53 +0200214/* Card Specification v2.3.1*/
215static const uint8_t gp_ins_tbl_8ce[256] = {
216 [0xE4] = 4, /* DELETE */
217 [0xE2] = 0x80, /* STORE DATA */
218 [0xCA] = 4, /* GET DATA */
219 [0xCB] = 4, /* GET DATA */
220 [0xF2] = 4, /* GET STATUS */
221 [0xE6] = 4, /* INSTALL */
222 [0xE8] = 4, /* LOAD */
223 [0xD8] = 4, /* PUT KEY */
224 [0xF0] = 3, /* SET STATUS */
Karsten Ohme433218a2023-09-14 11:58:59 +0200225 [0xC0] = 2, /* GET RESPONSE */
Karsten Ohme6772dd72022-06-09 02:56:53 +0200226};
227
Harald Welte4acaa132016-03-14 15:35:50 +0100228static const struct osim_cla_ins_case uicc_ins_case[] = {
229 {
230 .cla = 0x80,
231 .cla_mask = 0xFF,
232 .ins_tbl = uicc_ins_tbl_80,
233 }, {
234 .cla = 0x00,
235 .cla_mask = 0xF0,
236 .helper = uicc046_cla_ins_helper,
237 .ins_tbl = uicc_ins_tbl_046,
238 }, {
239 .cla = 0x40,
240 .cla_mask = 0xF0,
241 .helper = uicc046_cla_ins_helper,
242 .ins_tbl = uicc_ins_tbl_046,
243 }, {
244 .cla = 0x60,
245 .cla_mask = 0xF0,
246 .helper = uicc046_cla_ins_helper,
247 .ins_tbl = uicc_ins_tbl_046,
248 }, {
249 .cla = 0x80,
250 .cla_mask = 0xF0,
251 .ins_tbl = uicc_ins_tbl_8ce,
252 }, {
253 .cla = 0xC0,
254 .cla_mask = 0xF0,
255 .ins_tbl = uicc_ins_tbl_8ce,
256 }, {
257 .cla = 0xE0,
258 .cla_mask = 0xF0,
259 .ins_tbl = uicc_ins_tbl_8ce,
Karsten Ohme6772dd72022-06-09 02:56:53 +0200260 }, {
261 .cla = 0x80,
262 .cla_mask = 0xF0,
263 .helper = gp_cla_ins_helper,
264 .ins_tbl = gp_ins_tbl_8ce,
265 }, {
266 .cla = 0xC0,
267 .cla_mask = 0xF0,
268 .helper = gp_cla_ins_helper,
269 .ins_tbl = gp_ins_tbl_8ce,
270 }, {
271 .cla = 0xE0,
272 .cla_mask = 0xF0,
273 .helper = gp_cla_ins_helper,
274 .ins_tbl = gp_ins_tbl_8ce,
Harald Welte4acaa132016-03-14 15:35:50 +0100275 },
276};
277
278const struct osim_cla_ins_card_profile osim_uicc_cic_profile = {
279 .name = "UICC",
280 .description = "TS 102 221 / 3GPP TS 31.102",
281 .cic_arr = uicc_ins_case,
282 .cic_arr_size = ARRAY_SIZE(uicc_ins_case),
283};
284
285
286static const struct osim_cla_ins_case uicc_sim_ins_case[] = {
287 {
288 .cla = 0xA0,
289 .cla_mask = 0xFF,
290 .ins_tbl = gsm1111_ins_tbl,
291 }, {
292 .cla = 0x80,
293 .cla_mask = 0xFF,
294 .ins_tbl = uicc_ins_tbl_80,
295 }, {
296 .cla = 0x00,
297 .cla_mask = 0xF0,
298 .helper = uicc046_cla_ins_helper,
299 .ins_tbl = uicc_ins_tbl_046,
300 }, {
301 .cla = 0x40,
302 .cla_mask = 0xF0,
303 .helper = uicc046_cla_ins_helper,
304 .ins_tbl = uicc_ins_tbl_046,
305 }, {
306 .cla = 0x60,
307 .cla_mask = 0xF0,
308 .helper = uicc046_cla_ins_helper,
309 .ins_tbl = uicc_ins_tbl_046,
310 }, {
311 .cla = 0x80,
312 .cla_mask = 0xF0,
313 .ins_tbl = uicc_ins_tbl_8ce,
314 }, {
315 .cla = 0xC0,
316 .cla_mask = 0xF0,
317 .ins_tbl = uicc_ins_tbl_8ce,
318 }, {
319 .cla = 0xE0,
320 .cla_mask = 0xF0,
321 .ins_tbl = uicc_ins_tbl_8ce,
Karsten Ohme6772dd72022-06-09 02:56:53 +0200322 }, {
323 .cla = 0x80,
324 .cla_mask = 0xF0,
325 .helper = gp_cla_ins_helper,
326 .ins_tbl = gp_ins_tbl_8ce,
327 }, {
328 .cla = 0xC0,
329 .cla_mask = 0xF0,
330 .helper = gp_cla_ins_helper,
331 .ins_tbl = gp_ins_tbl_8ce,
332 }, {
333 .cla = 0xE0,
334 .cla_mask = 0xF0,
335 .helper = gp_cla_ins_helper,
336 .ins_tbl = gp_ins_tbl_8ce,
Harald Welte4acaa132016-03-14 15:35:50 +0100337 },
Karsten Ohme6772dd72022-06-09 02:56:53 +0200338
Harald Welte4acaa132016-03-14 15:35:50 +0100339};
340
341const struct osim_cla_ins_card_profile osim_uicc_sim_cic_profile = {
342 .name = "UICC+SIM",
343 .description = "TS 102 221 / 3GPP TS 31.102 + GSM TS 11.11",
344 .cic_arr = uicc_sim_ins_case,
345 .cic_arr_size = ARRAY_SIZE(uicc_sim_ins_case),
346};
347
348/* 3GPP TS 31.102 */
349const uint8_t usim_ins_case[256] = {
350 [0x88] = 4, /* AUTHENTICATE */
351};
352
Harald Welted6e16de2023-11-28 21:55:27 +0100353/* https://learn.microsoft.com/en-us/windows-hardware/drivers/smartcard/discovery-process */
354static const uint8_t microsoft_discovery_ins_tbl[256] = {
355 [0xA4] = 4, /* SELECT FILE */
356 [0xCA] = 2, /* GET DATA */
357 [0xC0] = 2, /* GET RESPONSE */
358};
359
Harald Welte4acaa132016-03-14 15:35:50 +0100360int osim_determine_apdu_case(const struct osim_cla_ins_card_profile *prof,
361 const uint8_t *hdr)
362{
363 uint8_t cla = hdr[0];
364 uint8_t ins = hdr[1];
365 int i;
366 int rc;
367
368 for (i = 0; i < prof->cic_arr_size; i++) {
369 const struct osim_cla_ins_case *cic = &prof->cic_arr[i];
370 if ((cla & cic->cla_mask) != cic->cla)
371 continue;
372 rc = cic->ins_tbl[ins];
373 switch (rc) {
374 case 0x80:
375 return cic->helper(cic, hdr);
376 case 0x00:
Karsten Ohme6772dd72022-06-09 02:56:53 +0200377 /* continue with further cic, rather than abort
Harald Welte4acaa132016-03-14 15:35:50 +0100378 * now */
379 continue;
380 default:
381 return rc;
382 }
383 }
Harald Welted6e16de2023-11-28 21:55:27 +0100384 /* special handling for Microsoft who insists to use INS=0xCA in CLA=0x00 which is not
385 * really part of GSM SIM, ETSI UICC or 3GPP USIM specifications, but only ISO7816. Rather than adding
386 * it to each and every card profile, let's add the instructions listed at
387 * https://learn.microsoft.com/en-us/windows-hardware/drivers/smartcard/discovery-process explicitly
388 * here. They will only be used in case no more specific match was found in the actual profile above. */
389 if (cla == 0x00) {
390 rc = microsoft_discovery_ins_tbl[ins];
391 if (rc)
392 return rc;
393 }
394
Harald Welte4acaa132016-03-14 15:35:50 +0100395 return 0;
396}