/src/opensc/src/libopensc/card-cardos.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * card-cardos.c: Support for CardOS (from Siemens or Atos) based cards and |
3 | | * tokens (for example Aladdin eToken PRO, Eutron CryptoIdentity IT-SEC) |
4 | | * |
5 | | * Copyright (c) 2005 Nils Larsch <nils@larsch.net> |
6 | | * Copyright (C) 2002 Andreas Jellinghaus <aj@dungeon.inka.de> |
7 | | * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi> |
8 | | * |
9 | | * This library is free software; you can redistribute it and/or |
10 | | * modify it under the terms of the GNU Lesser General Public |
11 | | * License as published by the Free Software Foundation; either |
12 | | * version 2.1 of the License, or (at your option) any later version. |
13 | | * |
14 | | * This library is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | | * Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public |
20 | | * License along with this library; if not, write to the Free Software |
21 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | | */ |
23 | | |
24 | | #ifdef HAVE_CONFIG_H |
25 | | #include "config.h" |
26 | | #endif |
27 | | |
28 | | #include <ctype.h> |
29 | | #include <string.h> |
30 | | #include <stdlib.h> |
31 | | |
32 | | #include "internal.h" |
33 | | #include "asn1.h" |
34 | | #include "cardctl.h" |
35 | | |
36 | | static const struct sc_card_operations *iso_ops = NULL; |
37 | | |
38 | | static struct sc_card_operations cardos_ops; |
39 | | static struct sc_card_driver cardos_drv = { |
40 | | "Siemens CardOS", |
41 | | "cardos", |
42 | | &cardos_ops, |
43 | | NULL, 0, NULL |
44 | | }; |
45 | | |
46 | | static const struct sc_atr_table cardos_atrs[] = { |
47 | | /* 4.0 */ |
48 | | { "3b:e2:00:ff:c1:10:31:fe:55:c8:02:9c", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL }, |
49 | | /* cardos m4.2 and above */ |
50 | | { "3b:f2:18:00:ff:c1:0a:31:fe:55:c8:06:8a", "ff:ff:0f:ff:00:ff:00:ff:ff:00:00:00:00", NULL, SC_CARD_TYPE_CARDOS_M4_2, 0, NULL }, |
51 | | /* CardOS 4.4 */ |
52 | | { "3b:d2:18:02:c1:0a:31:fe:58:c8:0d:51", NULL, NULL, SC_CARD_TYPE_CARDOS_M4_4, 0, NULL}, |
53 | | /* CardOS v5.0 */ |
54 | | { "3b:d2:18:00:81:31:fe:58:c9:01:14", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_0, 0, NULL}, |
55 | | /* CardOS v5.3 */ |
56 | | { "3b:d2:18:00:81:31:fe:58:c9:02:17", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_3, 0, NULL}, |
57 | | { "3b:d2:18:00:81:31:fe:58:c9:03:16", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_3, 0, NULL}, |
58 | | /* CardOS v5.4 */ |
59 | | { "3b:d2:18:00:81:31:fe:58:c9:04:11", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_3, 0, NULL}, |
60 | | { NULL, NULL, NULL, 0, 0, NULL } |
61 | | }; |
62 | | |
63 | | /* private data for cardos driver */ |
64 | | typedef struct cardos_data { |
65 | | /* constructed internally */ |
66 | | unsigned int algorithm_ids_in_tokeninfo[SC_MAX_SUPPORTED_ALGORITHMS]; |
67 | | unsigned int algorithm_ids_in_tokeninfo_count; |
68 | | unsigned long flags; /* flags used by init to create sc_algorithms */ |
69 | | unsigned long ec_flags; |
70 | | unsigned long ext_flags; |
71 | | int rsa_2048; |
72 | | const sc_security_env_t * sec_env; |
73 | | } cardos_data_t; |
74 | | |
75 | | static int cardos_match_card(sc_card_t *card) |
76 | 0 | { |
77 | 0 | unsigned char atr[SC_MAX_ATR_SIZE] = {0}; |
78 | 0 | int i; |
79 | |
|
80 | 0 | i = _sc_match_atr(card, cardos_atrs, &card->type); |
81 | 0 | if (i < 0) |
82 | 0 | return 0; |
83 | | |
84 | 0 | memcpy(atr, card->atr.value, card->atr.len); |
85 | | |
86 | | /* Do not change card type for CIE! */ |
87 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_CIE_V1) |
88 | 0 | return 1; |
89 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_M4_4) |
90 | 0 | return 1; |
91 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_V5_0) |
92 | 0 | return 1; |
93 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_V5_3) |
94 | 0 | return 1; |
95 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_M4_2) { |
96 | 0 | int rv; |
97 | 0 | sc_apdu_t apdu = {0}; |
98 | 0 | u8 rbuf[SC_MAX_APDU_BUFFER_SIZE] = {0}; |
99 | | /* first check some additional ATR bytes */ |
100 | 0 | if ((atr[4] != 0xff && atr[4] != 0x02) || |
101 | 0 | (atr[6] != 0x10 && atr[6] != 0x0a) || |
102 | 0 | (atr[9] != 0x55 && atr[9] != 0x58)) |
103 | 0 | return 0; |
104 | | /* get the os version using GET DATA and compare it with |
105 | | * version in the ATR */ |
106 | 0 | sc_log(card->ctx, "checking cardos version ..."); |
107 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x82); |
108 | 0 | apdu.resp = rbuf; |
109 | 0 | apdu.resplen = sizeof(rbuf); |
110 | 0 | apdu.le = 256; |
111 | 0 | apdu.lc = 0; |
112 | 0 | rv = sc_transmit_apdu(card, &apdu); |
113 | 0 | LOG_TEST_RET(card->ctx, rv, "APDU transmit failed"); |
114 | 0 | if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00 || apdu.resplen < 2) |
115 | 0 | return 0; |
116 | 0 | if (apdu.resp[0] != atr[10] || |
117 | 0 | apdu.resp[1] != atr[11]) |
118 | | /* version mismatch */ |
119 | 0 | return 0; |
120 | 0 | if (atr[11] <= 0x04) { |
121 | 0 | sc_log(card->ctx, "found cardos m4.01"); |
122 | 0 | card->type = SC_CARD_TYPE_CARDOS_M4_01; |
123 | 0 | } else if (atr[11] == 0x08) { |
124 | 0 | sc_log(card->ctx, "found cardos v4.3b"); |
125 | 0 | card->type = SC_CARD_TYPE_CARDOS_M4_3; |
126 | 0 | } else if (atr[11] == 0x09) { |
127 | 0 | sc_log(card->ctx, "found cardos v4.2b"); |
128 | 0 | card->type = SC_CARD_TYPE_CARDOS_M4_2B; |
129 | 0 | } else if (atr[11] >= 0x0B) { |
130 | 0 | sc_log(card->ctx, "found cardos v4.2c or higher"); |
131 | 0 | card->type = SC_CARD_TYPE_CARDOS_M4_2C; |
132 | 0 | } else { |
133 | 0 | sc_log(card->ctx, "found cardos m4.2"); |
134 | 0 | } |
135 | 0 | } |
136 | 0 | return 1; |
137 | 0 | } |
138 | | |
139 | | static int cardos_have_2048bit_package(sc_card_t *card) |
140 | 0 | { |
141 | 0 | sc_apdu_t apdu; |
142 | 0 | u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; |
143 | 0 | int r; |
144 | 0 | const u8 *p = rbuf, *q, *pp; |
145 | 0 | size_t len, tlen = 0, ilen = 0; |
146 | |
|
147 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x88); |
148 | 0 | apdu.resp = rbuf; |
149 | 0 | apdu.resplen = sizeof(rbuf); |
150 | 0 | apdu.lc = 0; |
151 | 0 | apdu.le = 256; |
152 | 0 | r = sc_transmit_apdu(card, &apdu); |
153 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
154 | | |
155 | 0 | if ((len = apdu.resplen) == 0) |
156 | | /* looks like no package has been installed */ |
157 | 0 | return 0; |
158 | | |
159 | 0 | while (len != 0) { |
160 | 0 | pp = sc_asn1_find_tag(card->ctx, p, len, 0xe1, &tlen); |
161 | 0 | if (pp == NULL) |
162 | 0 | return 0; |
163 | 0 | q = sc_asn1_find_tag(card->ctx, pp, tlen, 0x01, &ilen); |
164 | 0 | if (q == NULL || ilen != 4) |
165 | 0 | return 0; |
166 | 0 | if (q[0] == 0x1c) |
167 | 0 | return 1; |
168 | 0 | p += tlen; |
169 | 0 | len -= tlen + 2; |
170 | 0 | } |
171 | | |
172 | 0 | return 0; |
173 | 0 | } |
174 | | |
175 | | |
176 | | /* Called from cardos_init for old cards, from cardos_cardctl_parsed_token_info for new cards */ |
177 | | /* TODO see if works from old cards too */ |
178 | | static int cardos_add_algs(sc_card_t *card, unsigned long flags, unsigned long ec_flags, unsigned long ext_flags) |
179 | 0 | { |
180 | |
|
181 | 0 | cardos_data_t * priv = (cardos_data_t *)card->drv_data; |
182 | |
|
183 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
184 | |
|
185 | 0 | _sc_card_add_rsa_alg(card, 512, flags, 0); |
186 | 0 | _sc_card_add_rsa_alg(card, 768, flags, 0); |
187 | 0 | _sc_card_add_rsa_alg(card, 1024, flags, 0); |
188 | 0 | if (priv->rsa_2048 == 1) { |
189 | 0 | _sc_card_add_rsa_alg(card, 1280, flags, 0); |
190 | 0 | _sc_card_add_rsa_alg(card, 1536, flags, 0); |
191 | 0 | _sc_card_add_rsa_alg(card, 1792, flags, 0); |
192 | 0 | _sc_card_add_rsa_alg(card, 2048, flags, 0); |
193 | 0 | } |
194 | |
|
195 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { |
196 | | /* Starting with CardOS 5, the card supports PIN query commands */ |
197 | 0 | card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; |
198 | 0 | _sc_card_add_rsa_alg(card, 3072, flags, 0); |
199 | 0 | _sc_card_add_rsa_alg(card, 4096, flags, 0); |
200 | 0 | } |
201 | | |
202 | | /* TODO need to get sizes from supported_algos too */ |
203 | 0 | if (ec_flags != 0) { |
204 | 0 | _sc_card_add_ec_alg(card, 256, ec_flags, priv->ext_flags, NULL); |
205 | 0 | _sc_card_add_ec_alg(card, 384, ec_flags, priv->ext_flags, NULL); |
206 | 0 | } |
207 | |
|
208 | 0 | return 0; |
209 | 0 | } |
210 | | |
211 | | static int cardos_init(sc_card_t *card) |
212 | 0 | { |
213 | 0 | cardos_data_t * priv = NULL; |
214 | 0 | unsigned long flags = 0; |
215 | 0 | size_t data_field_length; |
216 | 0 | sc_apdu_t apdu; |
217 | 0 | u8 rbuf[2]; |
218 | 0 | int r; |
219 | |
|
220 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
221 | |
|
222 | 0 | priv = calloc(1, sizeof(cardos_data_t)); |
223 | 0 | if (!priv) |
224 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); |
225 | 0 | card->drv_data = priv; |
226 | |
|
227 | 0 | card->name = "Atos CardOS"; |
228 | 0 | card->cla = 0x00; |
229 | | |
230 | | /* let user override flags and type from opensc.conf */ |
231 | | /* user can override card->type too.*/ |
232 | 0 | if (card->flags) { |
233 | 0 | flags = card->flags; |
234 | 0 | } else { |
235 | | |
236 | | /* Set up algorithm info. */ |
237 | 0 | flags = 0; |
238 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_V5_0) { |
239 | 0 | flags |= SC_ALGORITHM_RSA_PAD_PKCS1; |
240 | 0 | } else if(card->type == SC_CARD_TYPE_CARDOS_V5_3) { |
241 | 0 | flags |= SC_ALGORITHM_RSA_RAW |
242 | 0 | | SC_ALGORITHM_RSA_HASH_NONE |
243 | 0 | | SC_ALGORITHM_ONBOARD_KEY_GEN; |
244 | 0 | } else { |
245 | 0 | flags |= SC_ALGORITHM_RSA_RAW |
246 | 0 | | SC_ALGORITHM_RSA_HASH_NONE |
247 | 0 | | SC_ALGORITHM_NEED_USAGE |
248 | 0 | | SC_ALGORITHM_ONBOARD_KEY_GEN; |
249 | 0 | } |
250 | 0 | } |
251 | |
|
252 | 0 | priv->flags = flags; |
253 | |
|
254 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_M4_2) { |
255 | 0 | r = cardos_have_2048bit_package(card); |
256 | 0 | if (r < 0) { |
257 | 0 | r = SC_ERROR_INVALID_CARD; |
258 | 0 | goto err; |
259 | 0 | } |
260 | 0 | if (r == 1) |
261 | 0 | priv->rsa_2048 = 1; |
262 | 0 | card->caps |= SC_CARD_CAP_APDU_EXT; |
263 | 0 | } else if (card->type == SC_CARD_TYPE_CARDOS_M4_3 |
264 | 0 | || card->type == SC_CARD_TYPE_CARDOS_M4_2B |
265 | 0 | || card->type == SC_CARD_TYPE_CARDOS_M4_2C |
266 | 0 | || card->type == SC_CARD_TYPE_CARDOS_M4_4 |
267 | 0 | || card->type == SC_CARD_TYPE_CARDOS_V5_0 |
268 | 0 | || card->type == SC_CARD_TYPE_CARDOS_V5_3) { |
269 | 0 | priv->rsa_2048 = 1; |
270 | 0 | card->caps |= SC_CARD_CAP_APDU_EXT; |
271 | | /* TODO check this. EC only if in supported_algo */ |
272 | 0 | priv->ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; |
273 | 0 | } |
274 | | |
275 | | /* probe DATA FIELD LENGTH with GET DATA */ |
276 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x8D); |
277 | 0 | apdu.le = sizeof rbuf; |
278 | 0 | apdu.resp = rbuf; |
279 | 0 | apdu.resplen = sizeof(rbuf); |
280 | |
|
281 | 0 | r = sc_transmit_apdu(card, &apdu); |
282 | 0 | if (r < 0) |
283 | 0 | LOG_TEST_GOTO_ERR(card->ctx, |
284 | 0 | SC_ERROR_INVALID_CARD, |
285 | 0 | "APDU transmit failed"); |
286 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
287 | 0 | if (r < 0) |
288 | 0 | LOG_TEST_GOTO_ERR(card->ctx, |
289 | 0 | SC_ERROR_INVALID_CARD, |
290 | 0 | "GET DATA command returned error"); |
291 | 0 | if (apdu.resplen != 2) { |
292 | 0 | r = SC_ERROR_INVALID_CARD; |
293 | 0 | goto err; |
294 | 0 | } |
295 | 0 | data_field_length = ((rbuf[0] << 8) | rbuf[1]); |
296 | | |
297 | | /* TODO is this really needed? strip the length of possible Lc and Le bytes */ |
298 | | |
299 | | /* Use Min card sizes and reader too. for V5_3 at least*/ |
300 | |
|
301 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { |
302 | 0 | sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "data_field_length:%"SC_FORMAT_LEN_SIZE_T"u " |
303 | 0 | "card->reader->max_send_size:%"SC_FORMAT_LEN_SIZE_T"u " |
304 | 0 | "card->reader->max_recv_size:%"SC_FORMAT_LEN_SIZE_T"u %s", |
305 | 0 | data_field_length, card->reader->max_send_size, card->reader->max_recv_size, |
306 | 0 | (card->caps & SC_CARD_CAP_APDU_EXT) ? "SC_CARD_CAP_APDU_EXT" : " "); |
307 | |
|
308 | 0 | if (card->caps & SC_CARD_CAP_APDU_EXT) { |
309 | 0 | card->max_send_size = data_field_length - 6; |
310 | | #ifdef _WIN32 |
311 | | /* Windows does not support PCSC PART_10 and may have forced reader to 255/256 |
312 | | * https://github.com/OpenSC/OpenSC/commit/eddea6f3c2d3dafc2c09eba6695c745a61b5186f |
313 | | * may have reset this. if so, will override and force extended |
314 | | * Most, if not all, cardos cards do extended, but not chaining |
315 | | */ |
316 | | if (card->reader->max_send_size == 255 && card->reader->max_recv_size == 256) { |
317 | | sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "resetting reader to use data_field_length"); |
318 | | card->reader->max_send_size = data_field_length - 6; |
319 | | card->reader->max_recv_size = data_field_length - 3; |
320 | | } |
321 | | #endif |
322 | 0 | } else |
323 | 0 | card->max_send_size = data_field_length - 3; |
324 | |
|
325 | 0 | card->max_send_size = sc_get_max_send_size(card); /* include reader sizes and protocol */ |
326 | 0 | card->max_recv_size = data_field_length - 2; |
327 | 0 | card->max_recv_size = sc_get_max_recv_size(card); |
328 | 0 | } else { |
329 | | /* old way, disregards reader capabilities */ |
330 | 0 | if (card->caps & SC_CARD_CAP_APDU_EXT) |
331 | 0 | card->max_send_size = data_field_length - 6; |
332 | 0 | else |
333 | 0 | card->max_send_size = data_field_length - 3; |
334 | | /* strip the length of SW bytes */ |
335 | 0 | card->max_recv_size = data_field_length - 2; |
336 | 0 | } |
337 | | |
338 | | /*for new cards, wait till after sc_pkcs15_bind_internal reads tokeninfo */ |
339 | 0 | if (card->type != SC_CARD_TYPE_CARDOS_V5_0 && card->type != SC_CARD_TYPE_CARDOS_V5_3) { |
340 | 0 | r = cardos_add_algs(card, flags, 0, 0); |
341 | 0 | } |
342 | |
|
343 | 0 | err: |
344 | 0 | if (r != SC_SUCCESS) { |
345 | 0 | free(priv); |
346 | 0 | card->drv_data = NULL; |
347 | 0 | } |
348 | |
|
349 | 0 | return r; |
350 | 0 | } |
351 | | |
352 | | static int cardos_pass_algo_flags(sc_card_t *card, struct sc_cardctl_cardos_pass_algo_flags * ptr) |
353 | 0 | { |
354 | 0 | cardos_data_t * priv = (cardos_data_t *)card->drv_data; |
355 | 0 | int r = 0; |
356 | |
|
357 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
358 | 0 | switch (ptr->pass) { |
359 | 0 | case 1: |
360 | 0 | ptr->card_flags = card->flags; |
361 | 0 | ptr->used_flags = priv->flags; |
362 | 0 | ptr->ec_flags = priv->ec_flags; |
363 | 0 | ptr->ext_flags = priv->ext_flags; |
364 | 0 | break; |
365 | 0 | case 2: |
366 | 0 | r = cardos_add_algs(card,ptr->new_flags, ptr->ec_flags, ptr->ext_flags); |
367 | 0 | break; |
368 | 0 | default: |
369 | 0 | sc_log(card->ctx, "ptr->pass: %ul invalid", ptr->pass); |
370 | 0 | r = SC_ERROR_INTERNAL; |
371 | 0 | } |
372 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
373 | 0 | } |
374 | | |
375 | | |
376 | | static int cardos_finish(sc_card_t *card) |
377 | 0 | { |
378 | 0 | int r = 0; |
379 | |
|
380 | 0 | if (card == NULL ) |
381 | 0 | return 0; |
382 | | |
383 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
384 | | |
385 | | /* free priv data */ |
386 | 0 | if (card->drv_data) { /* priv */ |
387 | 0 | free(card->drv_data); |
388 | 0 | card->drv_data = NULL; |
389 | 0 | } |
390 | |
|
391 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
392 | 0 | } |
393 | | |
394 | | |
395 | | |
396 | | static const struct sc_card_error cardos_errors[] = { |
397 | | /* some error inside the card */ |
398 | | /* i.e. nothing you can do */ |
399 | | { 0x6581, SC_ERROR_MEMORY_FAILURE, "EEPROM error; command aborted"}, |
400 | | { 0x6fff, SC_ERROR_CARD_CMD_FAILED, "internal assertion error"}, |
401 | | { 0x6700, SC_ERROR_WRONG_LENGTH, "LC invalid"}, |
402 | | { 0x6985, SC_ERROR_CARD_CMD_FAILED, "no random number available"}, |
403 | | { 0x6f81, SC_ERROR_CARD_CMD_FAILED, "file invalid, maybe checksum error"}, |
404 | | { 0x6f82, SC_ERROR_CARD_CMD_FAILED, "not enough memory in xram"}, |
405 | | { 0x6f84, SC_ERROR_CARD_CMD_FAILED, "general protection fault"}, |
406 | | |
407 | | /* the card doesn't know this combination of ins+cla+p1+p2 */ |
408 | | /* i.e. command will never work */ |
409 | | { 0x6881, SC_ERROR_NO_CARD_SUPPORT, "logical channel not supported"}, |
410 | | { 0x6a86, SC_ERROR_INCORRECT_PARAMETERS,"p1/p2 invalid"}, |
411 | | { 0x6d00, SC_ERROR_INS_NOT_SUPPORTED, "ins invalid"}, |
412 | | { 0x6e00, SC_ERROR_CLASS_NOT_SUPPORTED, "class invalid (hi nibble)"}, |
413 | | |
414 | | /* known command, but incorrectly used */ |
415 | | /* i.e. command could work, but you need to change something */ |
416 | | { 0x6981, SC_ERROR_CARD_CMD_FAILED, "command cannot be used for file structure"}, |
417 | | { 0x6a80, SC_ERROR_INCORRECT_PARAMETERS,"invalid parameters in data field"}, |
418 | | { 0x6a81, SC_ERROR_NOT_SUPPORTED, "function/mode not supported"}, |
419 | | { 0x6a85, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit the tlv structure"}, |
420 | | { 0x6986, SC_ERROR_INCORRECT_PARAMETERS,"no current ef selected"}, |
421 | | { 0x6a87, SC_ERROR_INCORRECT_PARAMETERS,"lc does not fit p1/p2"}, |
422 | | { 0x6c00, SC_ERROR_WRONG_LENGTH, "le does not fit the data to be sent"}, |
423 | | { 0x6f83, SC_ERROR_CARD_CMD_FAILED, "command must not be used in transaction"}, |
424 | | |
425 | | /* (something) not found */ |
426 | | { 0x6987, SC_ERROR_INCORRECT_PARAMETERS,"key object for sm not found"}, |
427 | | { 0x6f86, SC_ERROR_CARD_CMD_FAILED, "key object not found"}, |
428 | | { 0x6a82, SC_ERROR_FILE_NOT_FOUND, "file not found"}, |
429 | | { 0x6a83, SC_ERROR_RECORD_NOT_FOUND, "record not found"}, |
430 | | { 0x6a88, SC_ERROR_CARD_CMD_FAILED, "object not found"}, |
431 | | |
432 | | /* (something) invalid */ |
433 | | { 0x6884, SC_ERROR_CARD_CMD_FAILED, "chaining error"}, |
434 | | { 0x6984, SC_ERROR_CARD_CMD_FAILED, "bs object has invalid format"}, |
435 | | { 0x6988, SC_ERROR_INCORRECT_PARAMETERS,"key object used for sm has invalid format"}, |
436 | | |
437 | | /* (something) deactivated */ |
438 | | { 0x6283, SC_ERROR_CARD_CMD_FAILED, "file is deactivated" }, |
439 | | { 0x6983, SC_ERROR_AUTH_METHOD_BLOCKED, "bs object blocked"}, |
440 | | |
441 | | /* access denied */ |
442 | | { 0x6300, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"authentication failed"}, |
443 | | { 0x6982, SC_ERROR_SECURITY_STATUS_NOT_SATISFIED,"required access right not granted"}, |
444 | | |
445 | | /* other errors */ |
446 | | { 0x6a84, SC_ERROR_CARD_CMD_FAILED, "not enough memory"}, |
447 | | |
448 | | /* command ok, execution failed */ |
449 | | { 0x6f00, SC_ERROR_CARD_CMD_FAILED, "technical error (see eToken developers guide)"}, |
450 | | |
451 | | /* no error, maybe a note */ |
452 | | { 0x9000, SC_SUCCESS, NULL}, |
453 | | { 0x9001, SC_SUCCESS, "success, but eeprom weakness detected"}, |
454 | | { 0x9850, SC_SUCCESS, "over/underflow using in/decrease"} |
455 | | }; |
456 | | |
457 | | static int cardos_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2) |
458 | 0 | { |
459 | 0 | const int err_count = sizeof(cardos_errors)/sizeof(cardos_errors[0]); |
460 | 0 | int i; |
461 | |
|
462 | 0 | for (i = 0; i < err_count; i++) { |
463 | 0 | if (cardos_errors[i].SWs == ((sw1 << 8) | sw2)) { |
464 | 0 | if ( cardos_errors[i].errorstr ) |
465 | 0 | sc_log(card->ctx, "%s\n", |
466 | 0 | cardos_errors[i].errorstr); |
467 | 0 | return cardos_errors[i].errorno; |
468 | 0 | } |
469 | 0 | } |
470 | | |
471 | 0 | sc_log(card->ctx, "Unknown SWs; SW1=%02X, SW2=%02X\n", sw1, sw2); |
472 | 0 | return SC_ERROR_CARD_CMD_FAILED; |
473 | 0 | } |
474 | | |
475 | | static int cardos_list_files(sc_card_t *card, u8 *buf, size_t buflen) |
476 | 0 | { |
477 | 0 | sc_apdu_t apdu; |
478 | 0 | u8 rbuf[256], offset = 0; |
479 | 0 | const u8 *p, *q, *tag; |
480 | 0 | int r; |
481 | 0 | size_t fids = 0, len; |
482 | |
|
483 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
484 | | |
485 | | /* 0x16: DIRECTORY */ |
486 | | /* 0x02: list both DF and EF */ |
487 | |
|
488 | 0 | get_next_part: |
489 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x16, 0x02, offset); |
490 | 0 | apdu.cla = 0x80; |
491 | 0 | apdu.le = 256; |
492 | 0 | apdu.resplen = 256; |
493 | 0 | apdu.resp = rbuf; |
494 | |
|
495 | 0 | r = sc_transmit_apdu(card, &apdu); |
496 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
497 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
498 | 0 | LOG_TEST_RET(card->ctx, r, "DIRECTORY command returned error"); |
499 | | |
500 | 0 | if (apdu.resplen > 256) { |
501 | 0 | sc_log(card->ctx, "directory listing > 256 bytes, cutting"); |
502 | 0 | } |
503 | |
|
504 | 0 | p = rbuf; |
505 | 0 | len = apdu.resplen; |
506 | 0 | while (len != 0) { |
507 | 0 | size_t tlen = 0, ilen = 0; |
508 | | /* is there a file information block (0x6f) ? */ |
509 | 0 | tag = sc_asn1_find_tag(card->ctx, p, len, 0x6f, &tlen); |
510 | 0 | if (tag == NULL) { |
511 | 0 | sc_log(card->ctx, "directory tag missing"); |
512 | 0 | return SC_ERROR_INTERNAL; |
513 | 0 | } |
514 | 0 | len = len - tlen - (tag - p); |
515 | 0 | p = tag + tlen; |
516 | 0 | if (tlen == 0) |
517 | | /* empty directory */ |
518 | 0 | break; |
519 | 0 | q = sc_asn1_find_tag(card->ctx, tag, tlen, 0x86, &ilen); |
520 | 0 | if (q == NULL || ilen != 2) { |
521 | 0 | sc_log(card->ctx, "error parsing file id TLV object"); |
522 | 0 | return SC_ERROR_INTERNAL; |
523 | 0 | } |
524 | | /* put file id in buf */ |
525 | 0 | if (buflen >= 2) { |
526 | 0 | buf[fids++] = q[0]; |
527 | 0 | buf[fids++] = q[1]; |
528 | 0 | buflen -= 2; |
529 | 0 | } else |
530 | | /* not enough space left in buffer => break */ |
531 | 0 | break; |
532 | | /* extract next offset */ |
533 | 0 | q = sc_asn1_find_tag(card->ctx, tag, tlen, 0x8a, &ilen); |
534 | 0 | if (q != NULL && ilen == 1) { |
535 | 0 | offset = (u8)ilen; |
536 | 0 | goto get_next_part; |
537 | 0 | } |
538 | 0 | } |
539 | | |
540 | 0 | r = (int)fids; |
541 | |
|
542 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
543 | 0 | } |
544 | | |
545 | | static void add_acl_entry(sc_file_t *file, int op, u8 byte) |
546 | 0 | { |
547 | 0 | unsigned int method, key_ref = SC_AC_KEY_REF_NONE; |
548 | |
|
549 | 0 | switch (byte) { |
550 | 0 | case 0x00: |
551 | 0 | method = SC_AC_NONE; |
552 | 0 | break; |
553 | 0 | case 0xFF: |
554 | 0 | method = SC_AC_NEVER; |
555 | 0 | break; |
556 | 0 | default: |
557 | 0 | if (byte > 0x7F) { |
558 | 0 | method = SC_AC_UNKNOWN; |
559 | 0 | } else { |
560 | 0 | method = SC_AC_CHV; |
561 | 0 | key_ref = byte; |
562 | 0 | } |
563 | 0 | break; |
564 | 0 | } |
565 | 0 | sc_file_add_acl_entry(file, op, method, key_ref); |
566 | 0 | } |
567 | | |
568 | | static int acl_to_byte(const sc_acl_entry_t *e) |
569 | 0 | { |
570 | 0 | if (e != NULL) { |
571 | 0 | switch (e->method) { |
572 | 0 | case SC_AC_NONE: |
573 | 0 | return 0x00; |
574 | 0 | case SC_AC_NEVER: |
575 | 0 | return 0xFF; |
576 | 0 | case SC_AC_CHV: |
577 | 0 | case SC_AC_TERM: |
578 | 0 | case SC_AC_AUT: |
579 | 0 | if (e->key_ref == SC_AC_KEY_REF_NONE) |
580 | 0 | return -1; |
581 | 0 | if (e->key_ref > 0x7F) |
582 | 0 | return -1; |
583 | 0 | return e->key_ref; |
584 | 0 | } |
585 | 0 | } |
586 | 0 | return 0x00; |
587 | 0 | } |
588 | | |
589 | | static const int df_acl[9] = { |
590 | | -1, /* LCYCLE (life cycle change) */ |
591 | | SC_AC_OP_UPDATE, /* UPDATE Objects */ |
592 | | -1, /* APPEND Objects */ |
593 | | |
594 | | SC_AC_OP_INVALIDATE, /* DF */ |
595 | | SC_AC_OP_REHABILITATE, /* DF */ |
596 | | SC_AC_OP_DELETE, /* DF */ |
597 | | |
598 | | SC_AC_OP_UPDATE, /* ADMIN DF */ |
599 | | SC_AC_OP_CREATE, /* Files */ |
600 | | -1 /* Reserved */ |
601 | | }; |
602 | | static const int ef_acl[9] = { |
603 | | SC_AC_OP_READ, /* Data */ |
604 | | SC_AC_OP_UPDATE, /* Data (write file content) */ |
605 | | SC_AC_OP_WRITE, /* */ |
606 | | |
607 | | SC_AC_OP_INVALIDATE, /* EF */ |
608 | | SC_AC_OP_REHABILITATE, /* EF */ |
609 | | SC_AC_OP_DELETE, /* (delete) EF */ |
610 | | |
611 | | /* XXX: ADMIN should be an ACL type of its own, or mapped |
612 | | * to erase */ |
613 | | SC_AC_OP_UPDATE, /* ADMIN EF (modify meta information?) */ |
614 | | -1, /* INC (-> cyclic fixed files) */ |
615 | | -1 /* DEC */ |
616 | | }; |
617 | | |
618 | | static void parse_sec_attr(sc_file_t *file, const u8 *buf, size_t len) |
619 | 0 | { |
620 | 0 | size_t i; |
621 | 0 | const int *idx; |
622 | |
|
623 | 0 | idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; |
624 | | |
625 | | /* acl defaults to 0xFF if unspecified */ |
626 | 0 | for (i = 0; i < 9; i++) |
627 | 0 | if (idx[i] != -1) |
628 | 0 | add_acl_entry(file, idx[i], (u8)((i < len) ? buf[i] : 0xFF)); |
629 | 0 | } |
630 | | |
631 | | static int cardos_select_file(sc_card_t *card, |
632 | | const sc_path_t *in_path, |
633 | | sc_file_t **file) |
634 | 0 | { |
635 | 0 | int r; |
636 | |
|
637 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
638 | 0 | r = iso_ops->select_file(card, in_path, file); |
639 | 0 | if (r >= 0 && file) |
640 | 0 | parse_sec_attr((*file), (*file)->sec_attr, (*file)->sec_attr_len); |
641 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
642 | 0 | } |
643 | | |
644 | | static int cardos_acl_to_bytes(sc_card_t *card, const sc_file_t *file, |
645 | | u8 *buf, size_t *outlen) |
646 | 0 | { |
647 | 0 | int i, byte; |
648 | 0 | const int *idx; |
649 | |
|
650 | 0 | if (buf == NULL || *outlen < 9) |
651 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
652 | | |
653 | 0 | idx = (file->type == SC_FILE_TYPE_DF) ? df_acl : ef_acl; |
654 | 0 | for (i = 0; i < 9; i++) { |
655 | 0 | if (idx[i] < 0) |
656 | 0 | byte = 0x00; |
657 | 0 | else |
658 | 0 | byte = acl_to_byte(sc_file_get_acl_entry(file, idx[i])); |
659 | 0 | if (byte < 0) { |
660 | 0 | sc_log(card->ctx, "Invalid ACL\n"); |
661 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
662 | 0 | } |
663 | 0 | buf[i] = byte; |
664 | 0 | } |
665 | 0 | *outlen = 9; |
666 | |
|
667 | 0 | return SC_SUCCESS; |
668 | 0 | } |
669 | | |
670 | | static int cardos_set_file_attributes(sc_card_t *card, sc_file_t *file) |
671 | 0 | { |
672 | 0 | int r; |
673 | |
|
674 | 0 | if (file->type_attr_len == 0) { |
675 | 0 | u8 type[3]; |
676 | |
|
677 | 0 | memset(type, 0, sizeof(type)); |
678 | 0 | type[0] = 0x00; |
679 | 0 | switch (file->type) { |
680 | 0 | case SC_FILE_TYPE_WORKING_EF: |
681 | 0 | break; |
682 | 0 | case SC_FILE_TYPE_DF: |
683 | 0 | type[0] = 0x38; |
684 | 0 | break; |
685 | 0 | default: |
686 | 0 | return SC_ERROR_NOT_SUPPORTED; |
687 | 0 | } |
688 | 0 | if (file->type != SC_FILE_TYPE_DF) { |
689 | 0 | switch (file->ef_structure) { |
690 | 0 | case SC_FILE_EF_LINEAR_FIXED_TLV: |
691 | 0 | case SC_FILE_EF_LINEAR_VARIABLE: |
692 | 0 | case SC_FILE_EF_CYCLIC_TLV: |
693 | 0 | return SC_ERROR_NOT_SUPPORTED; |
694 | | /* No idea what this means, but it |
695 | | * seems to be required for key |
696 | | * generation. */ |
697 | 0 | case SC_FILE_EF_LINEAR_VARIABLE_TLV: |
698 | 0 | type[1] = 0xff; |
699 | | /* fall through */ |
700 | 0 | default: |
701 | 0 | type[0] |= file->ef_structure & 7; |
702 | 0 | break; |
703 | 0 | } |
704 | 0 | } |
705 | 0 | r = sc_file_set_type_attr(file, type, sizeof(type)); |
706 | 0 | if (r != SC_SUCCESS) |
707 | 0 | return r; |
708 | 0 | } |
709 | 0 | if (file->prop_attr_len == 0) { |
710 | 0 | u8 status[3]; |
711 | |
|
712 | 0 | status[0] = 0x01; |
713 | 0 | if (file->type == SC_FILE_TYPE_DF) { |
714 | 0 | status[1] = (file->size >> 8) & 0xFF; |
715 | 0 | status[2] = file->size & 0xFF; |
716 | 0 | } else { |
717 | 0 | status[1] = status[2] = 0x00; /* not used */ |
718 | 0 | } |
719 | 0 | r = sc_file_set_prop_attr(file, status, sizeof(status)); |
720 | 0 | if (r != SC_SUCCESS) |
721 | 0 | return r; |
722 | 0 | } |
723 | 0 | if (file->sec_attr_len == 0) { |
724 | 0 | u8 acl[9]; |
725 | 0 | size_t blen = sizeof(acl); |
726 | |
|
727 | 0 | r = cardos_acl_to_bytes(card, file, acl, &blen); |
728 | 0 | if (r != SC_SUCCESS) |
729 | 0 | return r; |
730 | 0 | r = sc_file_set_sec_attr(file, acl, blen); |
731 | 0 | if (r != SC_SUCCESS) |
732 | 0 | return r; |
733 | 0 | } |
734 | 0 | return SC_SUCCESS; |
735 | 0 | } |
736 | | |
737 | | /* newer versions of cardos seems to prefer the FCP */ |
738 | | static int cardos_construct_fcp(sc_card_t *card, const sc_file_t *file, |
739 | | u8 *out, size_t *outlen) |
740 | 0 | { |
741 | 0 | u8 buf[64], *p = out; |
742 | 0 | size_t inlen = *outlen, len; |
743 | 0 | int r; |
744 | |
|
745 | 0 | LOG_FUNC_CALLED(card->ctx); |
746 | |
|
747 | 0 | if (out == NULL || inlen < 64) |
748 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
749 | | /* add FCP tag */ |
750 | 0 | *p++ = 0x62; |
751 | | /* we will add the length later */ |
752 | 0 | p++; |
753 | |
|
754 | 0 | memset(buf, 0, sizeof(buf)); |
755 | | |
756 | | /* set the length */ |
757 | 0 | buf[0] = (file->size >> 8) & 0xff; |
758 | 0 | buf[1] = file->size & 0xff; |
759 | 0 | if (file->type == SC_FILE_TYPE_DF) |
760 | 0 | r = sc_asn1_put_tag(0x81, buf, 2, p, 4, &p); |
761 | 0 | else |
762 | 0 | r = sc_asn1_put_tag(0x80, buf, 2, p, 4, &p); |
763 | 0 | if (r != SC_SUCCESS) |
764 | 0 | return r; |
765 | | /* set file type */ |
766 | 0 | if (file->shareable != 0) |
767 | 0 | buf[0] = 0x40; |
768 | 0 | else |
769 | 0 | buf[0] = 0x00; |
770 | 0 | if (file->type == SC_FILE_TYPE_WORKING_EF) { |
771 | 0 | switch (file->ef_structure) { |
772 | 0 | case SC_FILE_EF_TRANSPARENT: |
773 | 0 | buf[0] |= 0x01; |
774 | 0 | break; |
775 | 0 | case SC_FILE_EF_LINEAR_VARIABLE_TLV: |
776 | 0 | buf[0] |= 0x05; |
777 | 0 | break; |
778 | 0 | case SC_FILE_EF_LINEAR_FIXED: |
779 | 0 | buf[0] |= 0x02; |
780 | 0 | buf[1] |= 0x21; |
781 | 0 | buf[2] |= 0x00; |
782 | 0 | buf[3] |= (u8) file->record_length; |
783 | 0 | buf[4] |= (u8) file->record_count; |
784 | 0 | break; |
785 | 0 | case SC_FILE_EF_CYCLIC: |
786 | 0 | buf[0] |= 0x06; |
787 | 0 | buf[1] |= 0x21; |
788 | 0 | buf[2] |= 0x00; |
789 | 0 | buf[3] |= (u8) file->record_length; |
790 | 0 | buf[4] |= (u8) file->record_count; |
791 | 0 | break; |
792 | 0 | default: |
793 | 0 | sc_log(card->ctx, "unknown EF type: %u", file->type); |
794 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
795 | 0 | } |
796 | 0 | if (file->ef_structure == SC_FILE_EF_CYCLIC || |
797 | 0 | file->ef_structure == SC_FILE_EF_LINEAR_FIXED) |
798 | 0 | r = sc_asn1_put_tag(0x82, buf, 5, p, 8, &p); |
799 | 0 | else |
800 | 0 | r = sc_asn1_put_tag(0x82, buf, 1, p, 8, &p); |
801 | 0 | } else if (file->type == SC_FILE_TYPE_DF) { |
802 | 0 | buf[0] |= 0x38; |
803 | 0 | r = sc_asn1_put_tag(0x82, buf, 1, p, 8, &p); |
804 | 0 | } else |
805 | 0 | return SC_ERROR_NOT_SUPPORTED; |
806 | 0 | if (r != SC_SUCCESS) |
807 | 0 | return r; |
808 | | /* set file id */ |
809 | 0 | buf[0] = (file->id >> 8) & 0xff; |
810 | 0 | buf[1] = file->id & 0xff; |
811 | 0 | r = sc_asn1_put_tag(0x83, buf, 2, p, 8, &p); |
812 | 0 | if (r != SC_SUCCESS) |
813 | 0 | return r; |
814 | | /* set aid (for DF only) */ |
815 | 0 | if (file->type == SC_FILE_TYPE_DF && file->namelen != 0) { |
816 | 0 | r = sc_asn1_put_tag(0x84, file->name, file->namelen, p, 20, &p); |
817 | 0 | if (r != SC_SUCCESS) |
818 | 0 | return r; |
819 | 0 | } |
820 | | /* set proprietary file attributes */ |
821 | 0 | buf[0] = 0x00; /* use default values */ |
822 | 0 | if (file->type == SC_FILE_TYPE_DF) |
823 | 0 | r = sc_asn1_put_tag(0x85, buf, 1, p, 8, &p); |
824 | 0 | else { |
825 | 0 | buf[1] = 0x00; |
826 | 0 | buf[2] = 0x00; |
827 | 0 | r = sc_asn1_put_tag(0x85, buf, 1, p, 8, &p); |
828 | 0 | } |
829 | 0 | if (r != SC_SUCCESS) |
830 | 0 | return r; |
831 | | /* set ACs */ |
832 | 0 | len = 9; |
833 | 0 | r = cardos_acl_to_bytes(card, file, buf, &len); |
834 | 0 | if (r != SC_SUCCESS) |
835 | 0 | return r; |
836 | 0 | r = sc_asn1_put_tag(0x86, buf, len, p, 18, &p); |
837 | 0 | if (r != SC_SUCCESS) |
838 | 0 | return r; |
839 | | /* finally set the length of the FCP */ |
840 | 0 | out[1] = p - out - 2; |
841 | |
|
842 | 0 | *outlen = p - out; |
843 | |
|
844 | 0 | return SC_SUCCESS; |
845 | 0 | } |
846 | | |
847 | | static int cardos_create_file(sc_card_t *card, sc_file_t *file) |
848 | 0 | { |
849 | 0 | int r; |
850 | |
|
851 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
852 | |
|
853 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_GENERIC || |
854 | 0 | card->type == SC_CARD_TYPE_CARDOS_M4_01) { |
855 | 0 | r = cardos_set_file_attributes(card, file); |
856 | 0 | if (r != SC_SUCCESS) |
857 | 0 | return r; |
858 | 0 | return iso_ops->create_file(card, file); |
859 | 0 | } else if (card->type == SC_CARD_TYPE_CARDOS_M4_2 || |
860 | 0 | card->type == SC_CARD_TYPE_CARDOS_M4_3 || |
861 | 0 | card->type == SC_CARD_TYPE_CARDOS_M4_2B || |
862 | 0 | card->type == SC_CARD_TYPE_CARDOS_M4_2C || |
863 | 0 | card->type == SC_CARD_TYPE_CARDOS_M4_4) { |
864 | 0 | u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; |
865 | 0 | size_t len = sizeof(sbuf); |
866 | 0 | sc_apdu_t apdu; |
867 | |
|
868 | 0 | r = cardos_construct_fcp(card, file, sbuf, &len); |
869 | 0 | if (r < 0) { |
870 | 0 | sc_log(card->ctx, "unable to create FCP"); |
871 | 0 | return r; |
872 | 0 | } |
873 | | |
874 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xE0, 0x00, 0x00); |
875 | 0 | apdu.lc = len; |
876 | 0 | apdu.datalen = len; |
877 | 0 | apdu.data = sbuf; |
878 | |
|
879 | 0 | r = sc_transmit_apdu(card, &apdu); |
880 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
881 | | |
882 | 0 | return sc_check_sw(card, apdu.sw1, apdu.sw2); |
883 | 0 | } else |
884 | 0 | return SC_ERROR_NOT_SUPPORTED; |
885 | 0 | } |
886 | | |
887 | | /* |
888 | | * Restore the indicated SE |
889 | | */ |
890 | | static int |
891 | | cardos_restore_security_env(sc_card_t *card, int se_num) |
892 | 0 | { |
893 | 0 | sc_apdu_t apdu; |
894 | 0 | int r; |
895 | |
|
896 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
897 | |
|
898 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x22, 0, se_num); |
899 | 0 | apdu.p1 = (card->type == SC_CARD_TYPE_CARDOS_CIE_V1 ? 0xF3 : 0x03); |
900 | |
|
901 | 0 | r = sc_transmit_apdu(card, &apdu); |
902 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
903 | | |
904 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
905 | 0 | LOG_TEST_RET(card->ctx, r, "Card returned error"); |
906 | | |
907 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
908 | 0 | } |
909 | | |
910 | | /* |
911 | | * Set the security context |
912 | | * Things get a little messy here. It seems you cannot do any |
913 | | * crypto without a security environment - but there isn't really |
914 | | * a way to specify the security environment in PKCS15. |
915 | | * What I'm doing here (for now) is to assume that for a key |
916 | | * object with ID 0xNN there is always a corresponding SE object |
917 | | * with the same ID. |
918 | | * XXX Need to find out how the Aladdin drivers do it. |
919 | | */ |
920 | | static int |
921 | | cardos_set_security_env(sc_card_t *card, |
922 | | const sc_security_env_t *env, |
923 | | int se_num) |
924 | 0 | { |
925 | 0 | cardos_data_t* priv = (cardos_data_t*)card->drv_data; |
926 | 0 | sc_apdu_t apdu; |
927 | 0 | u8 data[9]; |
928 | 0 | int key_id, r; |
929 | |
|
930 | 0 | assert(card != NULL && env != NULL); |
931 | | |
932 | 0 | if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) || env->key_ref_len != 1) { |
933 | 0 | sc_log(card->ctx, "No or invalid key reference\n"); |
934 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
935 | 0 | } |
936 | 0 | priv->sec_env = env; /* pass on to crypto routines */ |
937 | | |
938 | | /* key_ref includes card mechanism and key number |
939 | | * But newer cards appear to get this some other way, |
940 | | * We can use flags passed to know what OpenSC expects from the card |
941 | | * and have derived what these machanisums are. |
942 | | * Newer cards may change how this is done |
943 | | */ |
944 | |
|
945 | 0 | key_id = env->key_ref[0]; |
946 | |
|
947 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); |
948 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_CIE_V1) { |
949 | 0 | cardos_restore_security_env(card, 0x30); |
950 | 0 | apdu.p1 = 0xF1; |
951 | 0 | } else { |
952 | 0 | apdu.p1 = 0x41; |
953 | 0 | } |
954 | 0 | switch (env->operation) { |
955 | 0 | case SC_SEC_OPERATION_DECIPHER: |
956 | 0 | apdu.p2 = 0xB8; |
957 | 0 | break; |
958 | 0 | case SC_SEC_OPERATION_SIGN: |
959 | 0 | apdu.p2 = 0xB6; |
960 | 0 | break; |
961 | 0 | default: |
962 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
963 | 0 | } |
964 | | |
965 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { |
966 | | /* some cards appear to have key_id be both Cryptographic mechanism reference 4 bits |
967 | | * and key_ref 4 bits. But this limits card to 16 keys. |
968 | | * TODO may need to be looked at at a later time |
969 | | */ |
970 | | /* Private key reference */ |
971 | 0 | data[0] = 0x84; |
972 | 0 | data[1] = 0x01; |
973 | 0 | data[2] = key_id & 0x0F; |
974 | | /* Usage qualifier byte */ |
975 | 0 | data[3] = 0x95; |
976 | 0 | data[4] = 0x01; |
977 | 0 | data[5] = 0x40; |
978 | 0 | apdu.lc = apdu.datalen = 6; |
979 | 0 | if (key_id & 0xF0) { |
980 | | /* Cryptographic mechanism reference */ |
981 | 0 | data[6] = 0x80; |
982 | 0 | data[7] = 0x01; |
983 | 0 | data[8] = key_id & 0xF0; |
984 | 0 | apdu.lc = apdu.datalen = 9; |
985 | 0 | } else if ((env->operation == SC_SEC_OPERATION_SIGN |
986 | 0 | && priv->sec_env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_01) |
987 | 0 | || (env->operation == SC_SEC_OPERATION_DECIPHER |
988 | 0 | && priv->sec_env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1_TYPE_02)) { |
989 | | /* TODO this may only apply to c903 cards */ |
990 | | /* TODO or only for cards without any supported_algos or EIDComplient only */ |
991 | 0 | data[6] = 0x80; |
992 | 0 | data[7] = 0x01; |
993 | 0 | data[8] = 0x10; |
994 | 0 | apdu.lc = apdu.datalen = 9; |
995 | 0 | } else if (priv->sec_env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW) { |
996 | 0 | data[6] = 0x80; |
997 | 0 | data[7] = 0x01; |
998 | 0 | data[8] = 0x30; |
999 | 0 | apdu.lc = apdu.datalen = 9; |
1000 | 0 | } |
1001 | 0 | } else { |
1002 | 0 | data[0] = 0x83; |
1003 | 0 | data[1] = 0x01; |
1004 | 0 | data[2] = key_id; |
1005 | 0 | apdu.lc = apdu.datalen = 3; |
1006 | 0 | } |
1007 | 0 | apdu.data = data; |
1008 | |
|
1009 | 0 | r = sc_transmit_apdu(card, &apdu); |
1010 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
1011 | | |
1012 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
1013 | 0 | LOG_TEST_RET(card->ctx, r, "Card returned error"); |
1014 | | |
1015 | 0 | do { |
1016 | 0 | const struct sc_supported_algo_info* algorithm_info = env->supported_algos; |
1017 | 0 | int i=0; |
1018 | 0 | int algorithm_id_count = 0; |
1019 | |
|
1020 | 0 | for(i=0;i<SC_MAX_SUPPORTED_ALGORITHMS;++i) { |
1021 | 0 | struct sc_supported_algo_info alg = algorithm_info[i]; |
1022 | |
|
1023 | 0 | if(alg.operations & SC_PKCS15_ALGO_OP_COMPUTE_SIGNATURE) { |
1024 | 0 | unsigned int algorithm_id = alg.algo_ref; |
1025 | |
|
1026 | 0 | sc_log(card->ctx, "is signature"); |
1027 | 0 | sc_log(card->ctx, "Adding ID %d at index %d", algorithm_id, algorithm_id_count); |
1028 | 0 | priv->algorithm_ids_in_tokeninfo[algorithm_id_count++] = algorithm_id; |
1029 | 0 | } |
1030 | 0 | sc_log(card->ctx, "reference=%d, mechanism=%d, operations=%d, algo_ref=%d", |
1031 | 0 | alg.reference, alg.mechanism, alg.operations, alg.algo_ref); |
1032 | 0 | } |
1033 | 0 | priv -> algorithm_ids_in_tokeninfo_count = algorithm_id_count; |
1034 | 0 | } while (0); |
1035 | |
|
1036 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
1037 | 0 | } |
1038 | | |
1039 | | /* |
1040 | | * Compute digital signature |
1041 | | */ |
1042 | | |
1043 | | /* internal function to do the actual signature computation */ |
1044 | | static int |
1045 | | do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, |
1046 | | u8 *out, size_t outlen) |
1047 | 0 | { |
1048 | 0 | /* cardos_data_t* priv = (cardos_data_t*)card->drv_dataa */; |
1049 | 0 | int r; |
1050 | 0 | sc_apdu_t apdu; |
1051 | | |
1052 | | /* INS: 0x2A PERFORM SECURITY OPERATION |
1053 | | * P1: 0x9E Resp: Digital Signature |
1054 | | * P2: 0x9A Cmd: Input for Digital Signature */ |
1055 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x9E, 0x9A); |
1056 | 0 | apdu.resp = out; |
1057 | 0 | apdu.le = outlen; |
1058 | 0 | apdu.resplen = outlen; |
1059 | |
|
1060 | 0 | apdu.data = data; |
1061 | 0 | apdu.lc = datalen; |
1062 | 0 | apdu.datalen = datalen; |
1063 | 0 | iso7816_fixup_transceive_length(card, &apdu); |
1064 | 0 | r = sc_transmit_apdu(card, &apdu); |
1065 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
1066 | | |
1067 | 0 | if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) |
1068 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)apdu.resplen); |
1069 | 0 | else |
1070 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
1071 | 0 | } |
1072 | | |
1073 | | static int |
1074 | | cardos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, |
1075 | | u8 *out, size_t outlen) |
1076 | 0 | { |
1077 | 0 | cardos_data_t* priv; |
1078 | 0 | int r; |
1079 | 0 | sc_context_t *ctx; |
1080 | 0 | int do_rsa_pure_sig = 0; |
1081 | 0 | int do_rsa_sig = 0; |
1082 | 0 | size_t i; |
1083 | | |
1084 | |
|
1085 | 0 | assert(card != NULL && data != NULL && out != NULL); |
1086 | 0 | ctx = card->ctx; |
1087 | 0 | priv = (cardos_data_t*)card->drv_data; |
1088 | 0 | SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); |
1089 | | |
1090 | | /* sec_env has algorithm_flags set from sc_get_encoding_flags sec_flags |
1091 | | * If flags are set correctly we don't need to test anything |
1092 | | * TODO this assumes RSA is PSS, PKCS1 or RAW and we are passing |
1093 | | * the correct data. Should work for ECDSA too. |
1094 | | * use for V5 cards and TODO should for older cards too |
1095 | | */ |
1096 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { |
1097 | |
|
1098 | 0 | r = do_compute_signature(card, data, datalen, out, outlen); |
1099 | 0 | LOG_FUNC_RETURN(ctx, r); |
1100 | 0 | } |
1101 | | |
1102 | | /* There are two ways to create a signature, depending on the way, |
1103 | | * the key was created: RSA_SIG and RSA_PURE_SIG. |
1104 | | * We can use the following reasoning, to determine the correct operation: |
1105 | | * 1. We check for several caps flags (as set in card->caps), to prevent generating |
1106 | | * invalid signatures with duplicated hash prefixes with some cards |
1107 | | * 2. Use the information from AlgorithmInfo of the TokenInfo file. |
1108 | | * This information is parsed in set_security_env and stored in a static variable. |
1109 | | * The problem is, that that information is only available for the whole token and not |
1110 | | for a specific key, so if both operations are present, we can only do trial and error |
1111 | | * |
1112 | | * The Algorithm IDs for RSA_SIG are 0x86 and 0x88, those for RSA_PURE_SIG 0x8c and 0x8a |
1113 | | * (According to http://www.opensc-project.org/pipermail/opensc-devel/2010-September/014912.html |
1114 | | * and www.crysys.hu/infsec/M40_Manual_E_2001_10.pdf) |
1115 | | */ |
1116 | | |
1117 | | /* check the the algorithmIDs from the AlgorithmInfo */ |
1118 | 0 | for (i = 0; i < priv->algorithm_ids_in_tokeninfo_count; ++i) { |
1119 | 0 | unsigned int id = priv->algorithm_ids_in_tokeninfo[i]; |
1120 | 0 | if (id == 0x86 || id == 0x88) { |
1121 | 0 | do_rsa_sig = 1; |
1122 | 0 | } else if (id == 0x8C || id == 0x8A) { |
1123 | 0 | do_rsa_pure_sig = 1; |
1124 | 0 | } |
1125 | 0 | } |
1126 | | |
1127 | | /* check if any operation was selected */ |
1128 | 0 | if (do_rsa_sig == 0 && do_rsa_pure_sig == 0) { |
1129 | | /* no operation selected. we just have to try both, |
1130 | | * for the lack of any better reasoning */ |
1131 | 0 | sc_log(ctx, "I was unable to determine, whether this key can be used with RSA_SIG or RSA_PURE_SIG. I will just try both."); |
1132 | 0 | do_rsa_sig = 1; |
1133 | 0 | do_rsa_pure_sig = 1; |
1134 | 0 | } |
1135 | |
|
1136 | 0 | if(do_rsa_pure_sig == 1){ |
1137 | 0 | sc_log(ctx, "trying RSA_PURE_SIG (padded DigestInfo)"); |
1138 | 0 | r = do_compute_signature(card, data, datalen, out, outlen); |
1139 | 0 | if (r >= SC_SUCCESS) |
1140 | 0 | LOG_FUNC_RETURN(ctx, r); |
1141 | 0 | } |
1142 | | |
1143 | 0 | if(do_rsa_sig == 1){ |
1144 | 0 | u8 *buf = malloc(datalen); |
1145 | 0 | u8 *stripped_data = buf; |
1146 | 0 | size_t stripped_datalen = datalen; |
1147 | 0 | if (!buf) |
1148 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); |
1149 | 0 | memcpy(buf, data, datalen); |
1150 | 0 | data = buf; |
1151 | |
|
1152 | 0 | sc_log(ctx, "trying RSA_SIG (just the DigestInfo)"); |
1153 | | |
1154 | | /* remove padding: first try pkcs1 bt01 padding */ |
1155 | 0 | r = sc_pkcs1_strip_01_padding(ctx, data, datalen, stripped_data, &stripped_datalen); |
1156 | 0 | if (r != SC_SUCCESS) { |
1157 | | /* no pkcs1 bt01 padding => let's try zero padding |
1158 | | * This can only work if the data tbs doesn't have a |
1159 | | * leading 0 byte. */ |
1160 | 0 | while (*stripped_data == 0 && stripped_datalen != 0) { |
1161 | 0 | ++stripped_data; |
1162 | 0 | --stripped_datalen; |
1163 | 0 | } |
1164 | 0 | } |
1165 | 0 | sc_log(ctx, "trying to sign raw hash value with prefix"); |
1166 | 0 | r = do_compute_signature(card, stripped_data, stripped_datalen, out, outlen); |
1167 | 0 | if (r >= SC_SUCCESS) { |
1168 | 0 | free(buf); |
1169 | 0 | LOG_FUNC_RETURN(ctx, r); |
1170 | 0 | } |
1171 | 0 | sc_log(ctx, "trying to sign stripped raw hash value (card is responsible for prefix)"); |
1172 | 0 | r = sc_pkcs1_strip_digest_info_prefix(NULL, stripped_data, stripped_datalen, stripped_data, &stripped_datalen); |
1173 | 0 | if (r != SC_SUCCESS) { |
1174 | 0 | free(buf); |
1175 | 0 | LOG_FUNC_RETURN(ctx, r); |
1176 | 0 | } |
1177 | 0 | r = do_compute_signature(card, stripped_data, stripped_datalen, out, outlen); |
1178 | 0 | free(buf); |
1179 | 0 | LOG_FUNC_RETURN(ctx, r); |
1180 | 0 | } |
1181 | | |
1182 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); |
1183 | 0 | } |
1184 | | |
1185 | | static int |
1186 | | cardos_decipher(struct sc_card *card, |
1187 | | const u8 * crgram, size_t crgram_len, |
1188 | | u8 * out, size_t outlen) |
1189 | 0 | { |
1190 | 0 | cardos_data_t* priv = (cardos_data_t*)card->drv_data; |
1191 | 0 | int r; |
1192 | 0 | size_t card_max_send_size = card->max_send_size; |
1193 | 0 | size_t reader_max_send_size = card->reader->max_send_size; |
1194 | |
|
1195 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
1196 | | |
1197 | | /* 5.3 supports command chaining. Others may also |
1198 | | * card_max_send_size for 5.3 is already based on reader max_send_size */ |
1199 | |
|
1200 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { |
1201 | |
|
1202 | 0 | r = iso_ops->decipher(card, crgram, crgram_len, out, outlen); |
1203 | | /* |
1204 | | * 5.3 supports RAW as well as PKCS1 and PSS |
1205 | | * description may strip padding if card supports it |
1206 | | * with cards that support RAW, it always appears to |
1207 | | * drop first 00 that is start of padding. |
1208 | | */ |
1209 | |
|
1210 | 0 | if (r > 0 && priv->sec_env->algorithm_flags & SC_ALGORITHM_RSA_RAW) { |
1211 | 0 | size_t rsize = r; |
1212 | | /* RSA RAW crgram_len == modlen */ |
1213 | | /* removed padding is always > 1 byte */ |
1214 | | /* add back missing leading zero if card dropped it */ |
1215 | 0 | if (rsize == crgram_len - 1 && rsize < outlen) { |
1216 | 0 | memmove(out+1, out, rsize); |
1217 | 0 | out[0] =0x00; |
1218 | 0 | r++; |
1219 | 0 | } |
1220 | 0 | } |
1221 | |
|
1222 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
1223 | 0 | } |
1224 | | |
1225 | 0 | if (sc_get_max_send_size(card) < crgram_len + 1) { |
1226 | | /* CardOS doesn't support chaining for PSO:DEC, so we just _hope_ |
1227 | | * that both, the reader and the card are able to send enough data. |
1228 | | * (data is prefixed with 1 byte padding content indicator) */ |
1229 | 0 | card->max_send_size = crgram_len + 1; |
1230 | 0 | card->reader->max_send_size = crgram_len + 1; |
1231 | 0 | } |
1232 | |
|
1233 | 0 | r = iso_ops->decipher(card, crgram, crgram_len, out, outlen); |
1234 | | |
1235 | | /* reset whatever we've modified above */ |
1236 | 0 | card->max_send_size = card_max_send_size; |
1237 | 0 | card->reader->max_send_size = reader_max_send_size; |
1238 | |
|
1239 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
1240 | 0 | } |
1241 | | |
1242 | | static int |
1243 | | cardos_lifecycle_get(sc_card_t *card, int *mode) |
1244 | 0 | { |
1245 | 0 | sc_apdu_t apdu; |
1246 | 0 | u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; |
1247 | 0 | int r; |
1248 | |
|
1249 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
1250 | |
|
1251 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x83); |
1252 | 0 | apdu.cla = 0x00; |
1253 | 0 | apdu.le = 256; |
1254 | 0 | apdu.resplen = sizeof(rbuf); |
1255 | 0 | apdu.resp = rbuf; |
1256 | |
|
1257 | 0 | r = sc_transmit_apdu(card, &apdu); |
1258 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
1259 | | |
1260 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
1261 | 0 | LOG_TEST_RET(card->ctx, r, "Card returned error"); |
1262 | | |
1263 | 0 | if (apdu.resplen < 1) { |
1264 | 0 | LOG_TEST_RET(card->ctx, SC_ERROR_UNKNOWN_DATA_RECEIVED, "Lifecycle byte not in response"); |
1265 | 0 | } |
1266 | | |
1267 | 0 | r = SC_SUCCESS; |
1268 | 0 | switch (rbuf[0]) { |
1269 | 0 | case 0x10: |
1270 | 0 | *mode = SC_CARDCTRL_LIFECYCLE_USER; |
1271 | 0 | break; |
1272 | 0 | case 0x20: |
1273 | 0 | *mode = SC_CARDCTRL_LIFECYCLE_ADMIN; |
1274 | 0 | break; |
1275 | 0 | case 0x34: /* MANUFACTURING */ |
1276 | 0 | *mode = SC_CARDCTRL_LIFECYCLE_OTHER; |
1277 | 0 | break; |
1278 | 0 | default: |
1279 | 0 | sc_log(card->ctx, "Unknown lifecycle byte %d", rbuf[0]); |
1280 | 0 | r = SC_ERROR_INTERNAL; |
1281 | 0 | } |
1282 | | |
1283 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
1284 | 0 | } |
1285 | | |
1286 | | static int |
1287 | | cardos_lifecycle_set(sc_card_t *card, int *mode) |
1288 | 0 | { |
1289 | 0 | sc_apdu_t apdu; |
1290 | 0 | int r; |
1291 | |
|
1292 | 0 | int current; |
1293 | 0 | int target; |
1294 | |
|
1295 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
1296 | |
|
1297 | 0 | target = *mode; |
1298 | |
|
1299 | 0 | r = cardos_lifecycle_get(card, ¤t); |
1300 | |
|
1301 | 0 | if (r != SC_SUCCESS) |
1302 | 0 | return r; |
1303 | | |
1304 | 0 | if (current == target || current == SC_CARDCTRL_LIFECYCLE_OTHER) |
1305 | 0 | return SC_SUCCESS; |
1306 | | |
1307 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x10, 0, 0); |
1308 | 0 | apdu.cla = 0x80; |
1309 | 0 | apdu.le = 0; |
1310 | 0 | apdu.resplen = 0; |
1311 | 0 | apdu.resp = NULL; |
1312 | |
|
1313 | 0 | r = sc_transmit_apdu(card, &apdu); |
1314 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
1315 | | |
1316 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
1317 | 0 | LOG_TEST_RET(card->ctx, r, "Card returned error"); |
1318 | | |
1319 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
1320 | 0 | } |
1321 | | |
1322 | | static int |
1323 | | cardos_put_data_oci(sc_card_t *card, |
1324 | | struct sc_cardctl_cardos_obj_info *args) |
1325 | 0 | { |
1326 | 0 | sc_apdu_t apdu; |
1327 | 0 | int r; |
1328 | |
|
1329 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
1330 | |
|
1331 | 0 | memset(&apdu, 0, sizeof(apdu)); |
1332 | 0 | apdu.cse = SC_APDU_CASE_3_SHORT; |
1333 | 0 | apdu.cla = 0x00; |
1334 | 0 | apdu.ins = 0xda; |
1335 | 0 | apdu.p1 = 0x01; |
1336 | 0 | apdu.p2 = 0x6e; |
1337 | 0 | apdu.lc = args->len; |
1338 | 0 | apdu.data = args->data; |
1339 | 0 | apdu.datalen = args->len; |
1340 | |
|
1341 | 0 | r = sc_transmit_apdu(card, &apdu); |
1342 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
1343 | | |
1344 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
1345 | 0 | LOG_TEST_RET(card->ctx, r, "Card returned error"); |
1346 | | |
1347 | 0 | LOG_FUNC_RETURN(card->ctx, r); |
1348 | 0 | } |
1349 | | |
1350 | | static int |
1351 | | cardos_put_data_seci(sc_card_t *card, |
1352 | | struct sc_cardctl_cardos_obj_info *args) |
1353 | 0 | { |
1354 | 0 | sc_apdu_t apdu; |
1355 | 0 | int r; |
1356 | |
|
1357 | 0 | memset(&apdu, 0, sizeof(apdu)); |
1358 | 0 | apdu.cse = SC_APDU_CASE_3_SHORT; |
1359 | 0 | apdu.cla = 0x00; |
1360 | 0 | apdu.ins = 0xda; |
1361 | 0 | apdu.p1 = 0x01; |
1362 | 0 | apdu.p2 = 0x6d; |
1363 | 0 | apdu.lc = args->len; |
1364 | 0 | apdu.data = args->data; |
1365 | 0 | apdu.datalen = args->len; |
1366 | |
|
1367 | 0 | r = sc_transmit_apdu(card, &apdu); |
1368 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
1369 | | |
1370 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
1371 | 0 | LOG_TEST_RET(card->ctx, r, "Card returned error"); |
1372 | | |
1373 | 0 | return r; |
1374 | 0 | } |
1375 | | |
1376 | | static int |
1377 | | cardos_generate_key(sc_card_t *card, |
1378 | | struct sc_cardctl_cardos_genkey_info *args) |
1379 | 0 | { |
1380 | 0 | sc_apdu_t apdu; |
1381 | 0 | u8 data[8]; |
1382 | 0 | int r; |
1383 | |
|
1384 | 0 | data[0] = 0x20; /* store as PSO object */ |
1385 | 0 | data[1] = args->key_id; |
1386 | 0 | data[2] = args->fid >> 8; |
1387 | 0 | data[3] = args->fid & 0xff; |
1388 | 0 | data[4] = 0; /* additional Rabin Miller tests */ |
1389 | 0 | data[5] = 0x10; /* length difference between p, q (bits) */ |
1390 | 0 | data[6] = 0; /* default length of exponent, MSB */ |
1391 | 0 | data[7] = 0x20; /* default length of exponent, LSB */ |
1392 | |
|
1393 | 0 | memset(&apdu, 0, sizeof(apdu)); |
1394 | 0 | apdu.cse = SC_APDU_CASE_3_SHORT; |
1395 | 0 | apdu.cla = 0x00; |
1396 | 0 | apdu.ins = 0x46; |
1397 | 0 | apdu.p1 = 0x00; |
1398 | 0 | apdu.p2 = 0x00; |
1399 | 0 | apdu.data= data; |
1400 | 0 | apdu.datalen = apdu.lc = sizeof(data); |
1401 | |
|
1402 | 0 | r = sc_transmit_apdu(card, &apdu); |
1403 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
1404 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
1405 | 0 | LOG_TEST_RET(card->ctx, r, "GENERATE_KEY failed"); |
1406 | | |
1407 | 0 | return r; |
1408 | 0 | } |
1409 | | |
1410 | | static int cardos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) |
1411 | 0 | { |
1412 | 0 | int r; |
1413 | 0 | sc_apdu_t apdu; |
1414 | 0 | u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; |
1415 | |
|
1416 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xca, 0x01, 0x81); |
1417 | 0 | apdu.resp = rbuf; |
1418 | 0 | apdu.resplen = sizeof(rbuf); |
1419 | 0 | apdu.le = 256; |
1420 | 0 | r = sc_transmit_apdu(card, &apdu); |
1421 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
1422 | 0 | if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) |
1423 | 0 | return SC_ERROR_INTERNAL; |
1424 | 0 | if ((apdu.resplen == 8) && (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3)) { |
1425 | | /* cache serial number */ |
1426 | 0 | memcpy(card->serialnr.value, rbuf, 8); |
1427 | 0 | card->serialnr.len = 8; |
1428 | 0 | } else if (apdu.resplen == 32) { |
1429 | | /* cache serial number */ |
1430 | 0 | memcpy(card->serialnr.value, &rbuf[10], 6); |
1431 | 0 | card->serialnr.len = 6; |
1432 | 0 | } else { |
1433 | 0 | sc_log(card->ctx, "unexpected response to GET DATA serial" |
1434 | 0 | " number\n"); |
1435 | 0 | return SC_ERROR_INTERNAL; |
1436 | 0 | } |
1437 | | /* copy and return serial number */ |
1438 | 0 | memcpy(serial, &card->serialnr, sizeof(*serial)); |
1439 | 0 | return SC_SUCCESS; |
1440 | 0 | } |
1441 | | |
1442 | | static int |
1443 | | cardos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) |
1444 | 0 | { |
1445 | 0 | switch (cmd) { |
1446 | 0 | case SC_CARDCTL_CARDOS_PUT_DATA_FCI: |
1447 | 0 | break; |
1448 | 0 | case SC_CARDCTL_CARDOS_PUT_DATA_OCI: |
1449 | 0 | return cardos_put_data_oci(card, |
1450 | 0 | (struct sc_cardctl_cardos_obj_info *) ptr); |
1451 | 0 | break; |
1452 | 0 | case SC_CARDCTL_CARDOS_PUT_DATA_SECI: |
1453 | 0 | return cardos_put_data_seci(card, |
1454 | 0 | (struct sc_cardctl_cardos_obj_info *) ptr); |
1455 | 0 | break; |
1456 | 0 | case SC_CARDCTL_CARDOS_GENERATE_KEY: |
1457 | 0 | return cardos_generate_key(card, |
1458 | 0 | (struct sc_cardctl_cardos_genkey_info *) ptr); |
1459 | 0 | case SC_CARDCTL_CARDOS_PASS_ALGO_FLAGS: |
1460 | 0 | return cardos_pass_algo_flags(card, |
1461 | 0 | (struct sc_cardctl_cardos_pass_algo_flags *) ptr); |
1462 | 0 | case SC_CARDCTL_LIFECYCLE_GET: |
1463 | 0 | return cardos_lifecycle_get(card, (int *) ptr); |
1464 | 0 | case SC_CARDCTL_LIFECYCLE_SET: |
1465 | 0 | return cardos_lifecycle_set(card, (int *) ptr); |
1466 | 0 | case SC_CARDCTL_GET_SERIALNR: |
1467 | 0 | return cardos_get_serialnr(card, (sc_serial_number_t *)ptr); |
1468 | 0 | } |
1469 | 0 | return SC_ERROR_NOT_SUPPORTED; |
1470 | 0 | } |
1471 | | |
1472 | | /* |
1473 | | * The 0x80 thing tells the card it's okay to search parent |
1474 | | * directories as well for the referenced object. |
1475 | | * Unfortunately, it doesn't seem to work without this flag :-/ |
1476 | | */ |
1477 | | static int |
1478 | | cardos_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, |
1479 | | int *tries_left) |
1480 | 0 | { |
1481 | 0 | struct sc_context *ctx = card->ctx; |
1482 | 0 | int rv; |
1483 | |
|
1484 | 0 | LOG_FUNC_CALLED(card->ctx); |
1485 | |
|
1486 | 0 | data->flags |= SC_PIN_CMD_NEED_PADDING; |
1487 | 0 | data->pin_reference |= 0x80; |
1488 | |
|
1489 | 0 | sc_log(ctx, "PIN_CMD(cmd:%i, ref:%i)", data->cmd, data->pin_reference); |
1490 | 0 | sc_log(ctx, |
1491 | 0 | "PIN1(max:%"SC_FORMAT_LEN_SIZE_T"u, min:%"SC_FORMAT_LEN_SIZE_T"u)", |
1492 | 0 | data->pin1.max_length, data->pin1.min_length); |
1493 | 0 | sc_log(ctx, |
1494 | 0 | "PIN2(max:%"SC_FORMAT_LEN_SIZE_T"u, min:%"SC_FORMAT_LEN_SIZE_T"u)", |
1495 | 0 | data->pin2.max_length, data->pin2.min_length); |
1496 | | |
1497 | | /* FIXME: the following values depend on what pin length was |
1498 | | * used when creating the BS objects */ |
1499 | 0 | if (data->pin1.max_length == 0) |
1500 | 0 | data->pin1.max_length = 8; |
1501 | 0 | if (data->pin2.max_length == 0) |
1502 | 0 | data->pin2.max_length = 8; |
1503 | |
|
1504 | 0 | rv = iso_ops->pin_cmd(card, data, tries_left); |
1505 | 0 | LOG_FUNC_RETURN(ctx, rv); |
1506 | 0 | } |
1507 | | |
1508 | | |
1509 | | static int |
1510 | | cardos_logout(sc_card_t *card) |
1511 | 0 | { |
1512 | 0 | if (card->type == SC_CARD_TYPE_CARDOS_M4_01 |
1513 | 0 | || card->type == SC_CARD_TYPE_CARDOS_M4_2 |
1514 | 0 | || card->type == SC_CARD_TYPE_CARDOS_M4_2B |
1515 | 0 | || card->type == SC_CARD_TYPE_CARDOS_M4_2C |
1516 | 0 | || card->type == SC_CARD_TYPE_CARDOS_M4_3 |
1517 | 0 | || card->type == SC_CARD_TYPE_CARDOS_M4_4 |
1518 | 0 | || card->type == SC_CARD_TYPE_CARDOS_V5_0 |
1519 | 0 | || card->type == SC_CARD_TYPE_CARDOS_V5_3) { |
1520 | 0 | sc_apdu_t apdu; |
1521 | 0 | int r; |
1522 | 0 | sc_path_t path; |
1523 | |
|
1524 | 0 | sc_format_path("3F00", &path); |
1525 | 0 | r = sc_select_file(card, &path, NULL); |
1526 | 0 | if (r != SC_SUCCESS) |
1527 | 0 | return r; |
1528 | | |
1529 | 0 | sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0xEA, 0x00, 0x00); |
1530 | 0 | apdu.cla = 0x80; |
1531 | |
|
1532 | 0 | r = sc_transmit_apdu(card, &apdu); |
1533 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
1534 | | |
1535 | 0 | return sc_check_sw(card, apdu.sw1, apdu.sw2); |
1536 | 0 | } else |
1537 | 0 | return SC_ERROR_NOT_SUPPORTED; |
1538 | 0 | } |
1539 | | |
1540 | | /* eToken R2 supports WRITE_BINARY, PRO Tokens support UPDATE_BINARY */ |
1541 | | |
1542 | | static struct sc_card_driver * sc_get_driver(void) |
1543 | 3.19k | { |
1544 | 3.19k | if (iso_ops == NULL) |
1545 | 1 | iso_ops = sc_get_iso7816_driver()->ops; |
1546 | 3.19k | cardos_ops = *iso_ops; |
1547 | 3.19k | cardos_ops.match_card = cardos_match_card; |
1548 | 3.19k | cardos_ops.init = cardos_init; |
1549 | 3.19k | cardos_ops.finish = cardos_finish; |
1550 | 3.19k | cardos_ops.select_file = cardos_select_file; |
1551 | 3.19k | cardos_ops.create_file = cardos_create_file; |
1552 | 3.19k | cardos_ops.set_security_env = cardos_set_security_env; |
1553 | 3.19k | cardos_ops.restore_security_env = cardos_restore_security_env; |
1554 | 3.19k | cardos_ops.compute_signature = cardos_compute_signature; |
1555 | 3.19k | cardos_ops.decipher = cardos_decipher; |
1556 | | |
1557 | 3.19k | cardos_ops.list_files = cardos_list_files; |
1558 | 3.19k | cardos_ops.check_sw = cardos_check_sw; |
1559 | 3.19k | cardos_ops.card_ctl = cardos_card_ctl; |
1560 | 3.19k | cardos_ops.pin_cmd = cardos_pin_cmd; |
1561 | 3.19k | cardos_ops.logout = cardos_logout; |
1562 | | |
1563 | 3.19k | return &cardos_drv; |
1564 | 3.19k | } |
1565 | | |
1566 | | #if 1 |
1567 | | struct sc_card_driver * sc_get_cardos_driver(void) |
1568 | 3.19k | { |
1569 | 3.19k | return sc_get_driver(); |
1570 | 3.19k | } |
1571 | | #endif |