/src/opensc/src/libopensc/card-mcrd.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * card-mcrd.c: Support for MICARDO cards |
3 | | * |
4 | | * Copyright (C) 2004 Martin Paljak <martin@martinpaljak.net> |
5 | | * Copyright (C) 2004 Priit Randla <priit.randla@eyp.ee> |
6 | | * Copyright (C) 2003 Marie Fischer <marie@vtl.ee> |
7 | | * Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi> |
8 | | * Copyright (C) 2002 g10 Code GmbH |
9 | | * |
10 | | * This library is free software; you can redistribute it and/or |
11 | | * modify it under the terms of the GNU Lesser General Public |
12 | | * License as published by the Free Software Foundation; either |
13 | | * version 2.1 of the License, or (at your option) any later version. |
14 | | * |
15 | | * This library is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 | | * Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public |
21 | | * License along with this library; if not, write to the Free Software |
22 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 | | */ |
24 | | |
25 | | #ifdef HAVE_CONFIG_H |
26 | | #include "config.h" |
27 | | #endif |
28 | | |
29 | | #include <stdlib.h> |
30 | | #include <string.h> |
31 | | #include <ctype.h> |
32 | | |
33 | | #include "internal.h" |
34 | | #include "asn1.h" |
35 | | #include "cardctl.h" |
36 | | #include "gp.h" |
37 | | |
38 | | static const struct sc_atr_table mcrd_atrs[] = { |
39 | | {"3B:FF:94:00:FF:80:B1:FE:45:1F:03:00:68:D2:76:00:00:28:FF:05:1E:31:80:00:90:00:23", NULL, |
40 | | "Micardo 2.1/German BMI/D-Trust", SC_CARD_TYPE_MCRD_GENERIC, 0, NULL}, |
41 | | {"3b:6f:00:ff:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00", NULL, |
42 | | "D-Trust", SC_CARD_TYPE_MCRD_GENERIC, 0, NULL}, |
43 | | {"3b:ff:11:00:ff:80:b1:fe:45:1f:03:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00:a6", NULL, |
44 | | "D-Trust", SC_CARD_TYPE_MCRD_GENERIC, 0, NULL}, |
45 | | {NULL, NULL, NULL, 0, 0, NULL} |
46 | | }; |
47 | | |
48 | | |
49 | | static struct sc_card_operations mcrd_ops; |
50 | | static struct sc_card_driver mcrd_drv = { |
51 | | "MICARDO 2.1", |
52 | | "mcrd", |
53 | | &mcrd_ops, |
54 | | NULL, 0, NULL |
55 | | }; |
56 | | |
57 | | static const struct sc_card_operations *iso_ops = NULL; |
58 | | |
59 | | enum { |
60 | | MCRD_SEL_MF = 0x00, |
61 | | MCRD_SEL_DF = 0x01, |
62 | | MCRD_SEL_EF = 0x02, |
63 | | MCRD_SEL_PARENT = 0x03, |
64 | | MCRD_SEL_AID = 0x04 |
65 | | }; |
66 | | |
67 | 0 | #define MFID 0x3F00 |
68 | 0 | #define EF_KeyD 0x0013 /* File with extra key information. */ |
69 | 0 | #define EF_Rule 0x0030 /* Default ACL file. */ |
70 | | |
71 | 0 | #define MAX_CURPATH 10 |
72 | | |
73 | | struct rule_record_s { |
74 | | struct rule_record_s *next; |
75 | | unsigned int recno; |
76 | | size_t datalen; |
77 | | u8 data[1]; |
78 | | }; |
79 | | |
80 | | struct keyd_record_s { |
81 | | struct keyd_record_s *next; |
82 | | unsigned int recno; |
83 | | size_t datalen; |
84 | | u8 data[1]; |
85 | | }; |
86 | | |
87 | | struct df_info_s { |
88 | | struct df_info_s *next; |
89 | | unsigned short path[MAX_CURPATH]; |
90 | | size_t pathlen; |
91 | | struct rule_record_s *rule_file; /* keeps records of EF_Rule. */ |
92 | | struct keyd_record_s *keyd_file; /* keeps records of EF_KeyD. */ |
93 | | }; |
94 | | |
95 | | struct mcrd_priv_data { |
96 | | unsigned short curpath[MAX_CURPATH]; /* The currently selected path. */ |
97 | | int is_ef; /* True if the path points to an EF. */ |
98 | | size_t curpathlen; /* Length of this path or 0 if unknown. */ |
99 | | struct df_info_s *df_infos; |
100 | | sc_security_env_t sec_env; /* current security environment */ |
101 | | }; |
102 | | |
103 | 0 | #define DRVDATA(card) ((struct mcrd_priv_data *) ((card)->drv_data)) |
104 | | |
105 | | static int load_special_files(sc_card_t * card); |
106 | | static int select_part(sc_card_t * card, u8 kind, unsigned short int fid, sc_file_t ** file); |
107 | | |
108 | | /* Return the DF_info for the current path. If does not yet exist, |
109 | | create it. Returns NULL on error. */ |
110 | | static struct df_info_s *get_df_info(sc_card_t * card) |
111 | 0 | { |
112 | 0 | sc_context_t *ctx = card->ctx; |
113 | 0 | struct mcrd_priv_data *priv = DRVDATA(card); |
114 | 0 | struct df_info_s *dfi; |
115 | |
|
116 | 0 | if(!(!priv->is_ef)) |
117 | 0 | return NULL; |
118 | | |
119 | 0 | if (!priv->curpathlen) { |
120 | 0 | sc_log(ctx, "no current path to find the df_info\n"); |
121 | 0 | return NULL; |
122 | 0 | } |
123 | | |
124 | 0 | for (dfi = priv->df_infos; dfi; dfi = dfi->next) { |
125 | 0 | if (dfi->pathlen == priv->curpathlen |
126 | 0 | && !memcmp(dfi->path, priv->curpath, |
127 | 0 | dfi->pathlen * sizeof *dfi->path)) |
128 | 0 | return dfi; |
129 | 0 | } |
130 | | /* Not found, create it. */ |
131 | 0 | dfi = calloc(1, sizeof *dfi); |
132 | 0 | if (!dfi) { |
133 | 0 | sc_log(ctx, "out of memory while allocating df_info\n"); |
134 | 0 | return NULL; |
135 | 0 | } |
136 | 0 | dfi->pathlen = priv->curpathlen; |
137 | 0 | memcpy(dfi->path, priv->curpath, dfi->pathlen * sizeof *dfi->path); |
138 | 0 | dfi->next = priv->df_infos; |
139 | 0 | priv->df_infos = dfi; |
140 | 0 | return dfi; |
141 | 0 | } |
142 | | |
143 | | static void clear_special_files(struct df_info_s *dfi) |
144 | 0 | { |
145 | 0 | if (dfi) { |
146 | 0 | while (dfi->rule_file) { |
147 | 0 | struct rule_record_s *tmp = dfi->rule_file->next; |
148 | 0 | free(dfi->rule_file); |
149 | 0 | dfi->rule_file = tmp; |
150 | 0 | } |
151 | 0 | while (dfi->keyd_file) { |
152 | 0 | struct keyd_record_s *tmp = dfi->keyd_file->next; |
153 | 0 | free(dfi->keyd_file); |
154 | 0 | dfi->keyd_file = tmp; |
155 | 0 | } |
156 | 0 | } |
157 | 0 | } |
158 | | |
159 | | /* |
160 | | * Official notice: Refer to the Micardo 2.1 Public manual. |
161 | | * Sad side: not available without a NDA. |
162 | | */ |
163 | | |
164 | | static int mcrd_delete_ref_to_authkey(sc_card_t * card) |
165 | 0 | { |
166 | 0 | sc_apdu_t apdu; |
167 | 0 | int r; |
168 | 0 | u8 sbuf[2] = { 0x83, 0x00 }; |
169 | 0 | if(card == NULL) |
170 | 0 | return SC_ERROR_INTERNAL; |
171 | 0 | sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xA4, sbuf, 2, NULL, 0); |
172 | 0 | r = sc_transmit_apdu(card, &apdu); |
173 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
174 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
175 | 0 | } |
176 | | |
177 | | static int mcrd_delete_ref_to_signkey(sc_card_t * card) |
178 | 0 | { |
179 | 0 | sc_apdu_t apdu; |
180 | 0 | int r; |
181 | 0 | u8 sbuf[2] = { 0x83, 0x00 }; |
182 | 0 | if(card == NULL) |
183 | 0 | return SC_ERROR_INTERNAL; |
184 | 0 | sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB6, sbuf, 2, NULL, 0); |
185 | 0 | r = sc_transmit_apdu(card, &apdu); |
186 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
187 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); |
188 | 0 | } |
189 | | |
190 | | static int mcrd_match_card(sc_card_t * card) |
191 | 0 | { |
192 | 0 | int i = 0; |
193 | |
|
194 | 0 | i = _sc_match_atr(card, mcrd_atrs, &card->type); |
195 | 0 | if (i >= 0) { |
196 | 0 | card->name = mcrd_atrs[i].name; |
197 | 0 | return 1; |
198 | 0 | } |
199 | | |
200 | 0 | return 0; |
201 | 0 | } |
202 | | |
203 | | static int mcrd_init(sc_card_t * card) |
204 | 0 | { |
205 | 0 | unsigned long flags; |
206 | 0 | struct mcrd_priv_data *priv = calloc(1, sizeof *priv); |
207 | 0 | if (!priv) |
208 | 0 | return SC_ERROR_OUT_OF_MEMORY; |
209 | 0 | priv->curpath[0] = MFID; |
210 | 0 | priv->curpathlen = 1; |
211 | 0 | card->drv_data = priv; |
212 | 0 | card->cla = 0x00; |
213 | 0 | card->caps = SC_CARD_CAP_RNG; |
214 | |
|
215 | 0 | flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_RSA_HASH_NONE; |
216 | 0 | _sc_card_add_rsa_alg(card, 512, flags, 0); |
217 | 0 | _sc_card_add_rsa_alg(card, 768, flags, 0); |
218 | 0 | _sc_card_add_rsa_alg(card, 1024, flags, 0); |
219 | |
|
220 | 0 | if (SC_SUCCESS != sc_select_file (card, sc_get_mf_path(), NULL)) |
221 | 0 | sc_log(card->ctx, "Warning: select MF failed"); |
222 | |
|
223 | 0 | load_special_files(card); |
224 | |
|
225 | 0 | return SC_SUCCESS; |
226 | 0 | } |
227 | | |
228 | | static int mcrd_finish(sc_card_t * card) |
229 | 0 | { |
230 | 0 | struct mcrd_priv_data *priv; |
231 | |
|
232 | 0 | if (card == NULL) |
233 | 0 | return 0; |
234 | 0 | priv = DRVDATA(card); |
235 | 0 | while (priv->df_infos) { |
236 | 0 | struct df_info_s *tmp = priv->df_infos->next; |
237 | 0 | clear_special_files(priv->df_infos); |
238 | 0 | free(priv->df_infos); |
239 | 0 | priv->df_infos = tmp; |
240 | 0 | } |
241 | 0 | free(priv); |
242 | 0 | return 0; |
243 | 0 | } |
244 | | |
245 | | /* Load the rule and keyd file into our private data. |
246 | | Return 0 on success */ |
247 | | static int load_special_files(sc_card_t * card) |
248 | 0 | { |
249 | 0 | sc_context_t *ctx = card->ctx; |
250 | 0 | int r; |
251 | 0 | unsigned int recno; |
252 | 0 | struct df_info_s *dfi; |
253 | 0 | struct rule_record_s *rule; |
254 | 0 | struct keyd_record_s *keyd; |
255 | | |
256 | | /* First check whether we already cached it. */ |
257 | 0 | dfi = get_df_info(card); |
258 | 0 | if (dfi && dfi->rule_file) |
259 | 0 | return 0; /* yes. */ |
260 | 0 | clear_special_files(dfi); |
261 | 0 | if (!dfi) |
262 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_INTERNAL); |
263 | | |
264 | | /* Read rule file. Note that we bypass our cache here. */ |
265 | 0 | r = select_part(card, MCRD_SEL_EF, EF_Rule, NULL); |
266 | 0 | LOG_TEST_RET(ctx, r, "selecting EF_Rule failed"); |
267 | | |
268 | 0 | for (recno = 1;; recno++) { |
269 | 0 | u8 recbuf[256]; |
270 | 0 | r = sc_read_record(card, recno, 0, recbuf, sizeof(recbuf), |
271 | 0 | SC_RECORD_BY_REC_NR); |
272 | |
|
273 | 0 | if (r == SC_ERROR_RECORD_NOT_FOUND) |
274 | 0 | break; |
275 | 0 | if (r < 0) { |
276 | 0 | SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); |
277 | 0 | } else { |
278 | 0 | rule = malloc(sizeof *rule + (size_t)r); |
279 | 0 | if (!rule) |
280 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); |
281 | 0 | rule->recno = recno; |
282 | 0 | rule->datalen = (size_t)r; |
283 | 0 | memcpy(rule->data, recbuf, r); |
284 | 0 | rule->next = dfi->rule_file; |
285 | 0 | dfi->rule_file = rule; |
286 | 0 | } |
287 | 0 | } |
288 | | |
289 | 0 | sc_log(ctx, "new EF_Rule file loaded (%d records)\n", recno - 1); |
290 | | |
291 | | /* Read the KeyD file. Note that we bypass our cache here. */ |
292 | 0 | r = select_part(card, MCRD_SEL_EF, EF_KeyD, NULL); |
293 | 0 | if (r == SC_ERROR_FILE_NOT_FOUND) { |
294 | 0 | sc_log(ctx, "no EF_KeyD file available\n"); |
295 | 0 | return 0; /* That is okay. */ |
296 | 0 | } |
297 | 0 | LOG_TEST_RET(ctx, r, "selecting EF_KeyD failed"); |
298 | | |
299 | 0 | for (recno = 1;; recno++) { |
300 | 0 | u8 recbuf[256]; |
301 | 0 | r = sc_read_record(card, recno, 0, recbuf, sizeof(recbuf), |
302 | 0 | SC_RECORD_BY_REC_NR); |
303 | |
|
304 | 0 | if (r == SC_ERROR_RECORD_NOT_FOUND) |
305 | 0 | break; |
306 | 0 | if (r < 0) { |
307 | 0 | SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r); |
308 | 0 | } else { |
309 | 0 | keyd = malloc(sizeof *keyd + (size_t)r); |
310 | 0 | if (!keyd) |
311 | 0 | LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); |
312 | 0 | keyd->recno = recno; |
313 | 0 | keyd->datalen = (size_t) r; |
314 | 0 | memcpy(keyd->data, recbuf, r); |
315 | 0 | keyd->next = dfi->keyd_file; |
316 | 0 | dfi->keyd_file = keyd; |
317 | 0 | } |
318 | 0 | } |
319 | | |
320 | 0 | sc_log(ctx, "new EF_KeyD file loaded (%d records)\n", recno - 1); |
321 | | /* FIXME: Do we need to restore the current DF? I guess it is |
322 | | not required, but we could try to do so by selecting 3fff? */ |
323 | 0 | return 0; |
324 | 0 | } |
325 | | |
326 | | /* Process an ARR (7816-9/8.5.4) and setup the ACL. */ |
327 | | static void process_arr(sc_card_t * card, const u8 * buf, size_t buflen) |
328 | 0 | { |
329 | 0 | sc_context_t *ctx = card->ctx; |
330 | 0 | struct df_info_s *dfi; |
331 | 0 | struct rule_record_s *rule; |
332 | 0 | size_t left, taglen; |
333 | 0 | unsigned int cla, tag; |
334 | 0 | const u8 *p; |
335 | 0 | int skip; |
336 | 0 | char dbgbuf[2048]; |
337 | | |
338 | | /* Currently we support only the short for. */ |
339 | 0 | if (buflen != 1) { |
340 | 0 | sc_log(ctx, "can't handle long ARRs\n"); |
341 | 0 | return; |
342 | 0 | } |
343 | | |
344 | 0 | dfi = get_df_info(card); |
345 | 0 | for (rule = dfi ? dfi->rule_file : NULL; rule && rule->recno != *buf; |
346 | 0 | rule = rule->next) ; |
347 | 0 | if (!rule) { |
348 | 0 | sc_log(ctx, "referenced EF_rule record %d not found\n", *buf); |
349 | 0 | return; |
350 | 0 | } |
351 | | |
352 | 0 | sc_hex_dump(rule->data, rule->datalen, dbgbuf, sizeof dbgbuf); |
353 | 0 | sc_log(ctx, |
354 | 0 | "rule for record %d:\n%s", *buf, dbgbuf); |
355 | |
|
356 | 0 | p = rule->data; |
357 | 0 | left = rule->datalen; |
358 | 0 | skip = 1; /* Skip over initial unknown SC DOs. */ |
359 | 0 | for (;;) { |
360 | 0 | buf = p; |
361 | 0 | if (sc_asn1_read_tag(&p, left, &cla, &tag, &taglen) != SC_SUCCESS |
362 | 0 | || p == NULL) |
363 | 0 | break; |
364 | 0 | left -= (size_t)(p - buf); |
365 | 0 | tag |= cla; |
366 | |
|
367 | 0 | if (tag == 0x80 && taglen != 1) { |
368 | 0 | skip = 1; |
369 | 0 | } else if (tag == 0x80) { /* AM byte. */ |
370 | 0 | sc_log(ctx, " AM_DO: %02x\n", *p); |
371 | 0 | skip = 0; |
372 | 0 | } else if (tag >= 0x81 && tag <= 0x8f) { /* Cmd description */ |
373 | 0 | sc_hex_dump(p, taglen, dbgbuf, sizeof dbgbuf); |
374 | 0 | sc_log(ctx, " AM_DO: cmd[%s%s%s%s] %s", |
375 | 0 | (tag & 8) ? "C" : "", |
376 | 0 | (tag & 4) ? "I" : "", |
377 | 0 | (tag & 2) ? "1" : "", |
378 | 0 | (tag & 1) ? "2" : "", dbgbuf); |
379 | 0 | skip = 0; |
380 | 0 | } else if (tag == 0x9C) { /* Proprietary state machine descrip. */ |
381 | 0 | skip = 1; |
382 | 0 | } else if (!skip) { |
383 | 0 | switch (tag) { |
384 | 0 | case 0x90: /* Always */ |
385 | 0 | sc_log(ctx, " SC: always\n"); |
386 | 0 | break; |
387 | 0 | case 0x97: /* Never */ |
388 | 0 | sc_log(ctx, " SC: never\n"); |
389 | 0 | break; |
390 | 0 | case 0xA4: /* Authentication, value is a CRT. */ |
391 | 0 | sc_log_hex(ctx, " SC: auth", p, taglen); |
392 | 0 | break; |
393 | | |
394 | 0 | case 0xB4: |
395 | 0 | case 0xB6: |
396 | 0 | case 0xB8: /* Cmd or resp with SM, value is a CRT. */ |
397 | 0 | sc_log_hex(ctx, " SC: cmd/resp", p, taglen); |
398 | 0 | break; |
399 | | |
400 | 0 | case 0x9E: /* Security Condition byte. */ |
401 | 0 | sc_log_hex(ctx, " SC: condition", p, taglen); |
402 | 0 | break; |
403 | | |
404 | 0 | case 0xA0: /* OR template. */ |
405 | 0 | sc_log(ctx, " SC: OR\n"); |
406 | 0 | break; |
407 | 0 | case 0xAF: /* AND template. */ |
408 | 0 | sc_log(ctx, " SC: AND\n"); |
409 | 0 | break; |
410 | 0 | } |
411 | 0 | } |
412 | 0 | left -= taglen; |
413 | 0 | p += taglen; |
414 | 0 | } |
415 | |
|
416 | 0 | } |
417 | | |
418 | | static void process_fcp(sc_card_t * card, sc_file_t * file, |
419 | | const u8 * buf, size_t buflen) |
420 | 0 | { |
421 | 0 | sc_context_t *ctx = card->ctx; |
422 | 0 | size_t taglen, len = buflen; |
423 | 0 | const u8 *tag = NULL, *p = buf; |
424 | 0 | int bad_fde = 0; |
425 | |
|
426 | 0 | sc_log(ctx, "processing FCI bytes\n"); |
427 | | |
428 | | /* File identifier. */ |
429 | 0 | tag = sc_asn1_find_tag(ctx, p, len, 0x83, &taglen); |
430 | 0 | if (tag != NULL && taglen == 2) { |
431 | 0 | file->id = (tag[0] << 8) | tag[1]; |
432 | 0 | sc_log(ctx, |
433 | 0 | " file identifier: 0x%02X%02X\n", tag[0], tag[1]); |
434 | 0 | } |
435 | | /* Number of data bytes in the file including structural information. */ |
436 | 0 | tag = sc_asn1_find_tag(ctx, p, len, 0x81, &taglen); |
437 | 0 | if (!tag) { |
438 | | /* My card does not encode the filelength in 0x81 but |
439 | | in 0x85 which is the file descriptor extension in TCOS. |
440 | | Assume that this is the case when the regular file |
441 | | size tag is not encoded. */ |
442 | 0 | tag = sc_asn1_find_tag(ctx, p, len, 0x85, &taglen); |
443 | 0 | bad_fde = !!tag; |
444 | 0 | } |
445 | 0 | if (tag != NULL && taglen >= 2) { |
446 | 0 | int bytes = (tag[0] << 8) + tag[1]; |
447 | 0 | sc_log(ctx, |
448 | 0 | " bytes in file: %d\n", bytes); |
449 | 0 | file->size = (size_t)bytes; |
450 | 0 | } |
451 | 0 | if (tag == NULL) { |
452 | 0 | tag = sc_asn1_find_tag(ctx, p, len, 0x80, &taglen); |
453 | 0 | if (tag != NULL && taglen >= 2) { |
454 | 0 | int bytes = (tag[0] << 8) + tag[1]; |
455 | 0 | sc_log(ctx, |
456 | 0 | " bytes in file: %d\n", bytes); |
457 | 0 | file->size = (size_t)bytes; |
458 | 0 | } |
459 | 0 | } |
460 | | |
461 | | /* File descriptor byte(s). */ |
462 | 0 | tag = sc_asn1_find_tag(ctx, p, len, 0x82, &taglen); |
463 | 0 | if (tag != NULL) { |
464 | | /* Fixme, this might actual be up to 6 bytes. */ |
465 | 0 | if (taglen > 0) { |
466 | 0 | unsigned char byte = tag[0]; |
467 | 0 | const char *type; |
468 | |
|
469 | 0 | file->shareable = byte & 0x40 ? 1 : 0; |
470 | 0 | sc_log(ctx, |
471 | 0 | " shareable: %s\n", |
472 | 0 | (byte & 0x40) ? "yes" : "no"); |
473 | 0 | file->ef_structure = byte & 0x07; |
474 | 0 | switch ((byte >> 3) & 7) { |
475 | 0 | case 0: |
476 | 0 | type = "working EF"; |
477 | 0 | file->type = SC_FILE_TYPE_WORKING_EF; |
478 | 0 | break; |
479 | 0 | case 1: |
480 | 0 | type = "internal EF"; |
481 | 0 | file->type = SC_FILE_TYPE_INTERNAL_EF; |
482 | 0 | break; |
483 | 0 | case 7: |
484 | 0 | type = "DF"; |
485 | 0 | file->type = SC_FILE_TYPE_DF; |
486 | 0 | break; |
487 | 0 | default: |
488 | 0 | type = "unknown"; |
489 | 0 | break; |
490 | 0 | } |
491 | 0 | sc_log(ctx, |
492 | 0 | " type: %s\n", type); |
493 | 0 | sc_log(ctx, |
494 | 0 | " EF structure: %d\n", byte & 0x07); |
495 | 0 | } |
496 | 0 | } |
497 | | |
498 | | /* DF name. */ |
499 | 0 | tag = sc_asn1_find_tag(ctx, p, len, 0x84, &taglen); |
500 | 0 | if (tag != NULL && taglen > 0 && taglen <= 16) { |
501 | 0 | char name[17]; |
502 | 0 | size_t i; |
503 | |
|
504 | 0 | memcpy(file->name, tag, taglen); |
505 | 0 | file->namelen = taglen; |
506 | |
|
507 | 0 | for (i = 0; i < taglen; i++) { |
508 | 0 | if (isalnum(tag[i]) || ispunct(tag[i]) || isspace(tag[i])) |
509 | 0 | name[i] = (const char)tag[i]; |
510 | 0 | else |
511 | 0 | name[i] = '?'; |
512 | 0 | } |
513 | 0 | name[taglen] = 0; |
514 | 0 | sc_log(ctx, " file name: %s\n", name); |
515 | 0 | } |
516 | | |
517 | | /* Proprietary information. */ |
518 | 0 | tag = bad_fde ? NULL : sc_asn1_find_tag(ctx, p, len, 0x85, &taglen); |
519 | 0 | if (tag != NULL && taglen) { |
520 | 0 | sc_file_set_prop_attr(file, tag, taglen); |
521 | 0 | } else |
522 | 0 | file->prop_attr_len = 0; |
523 | | |
524 | | /* Proprietary information, constructed. */ |
525 | 0 | tag = sc_asn1_find_tag(ctx, p, len, 0xA5, &taglen); |
526 | 0 | if (tag != NULL && taglen) { |
527 | 0 | sc_file_set_prop_attr(file, tag, taglen); |
528 | 0 | } |
529 | | |
530 | | /* Security attributes, proprietary format. */ |
531 | 0 | tag = sc_asn1_find_tag(ctx, p, len, 0x86, &taglen); |
532 | 0 | if (tag != NULL && taglen) { |
533 | 0 | sc_file_set_sec_attr(file, tag, taglen); |
534 | 0 | } |
535 | | |
536 | | /* Security attributes, reference to expanded format. */ |
537 | 0 | tag = sc_asn1_find_tag(ctx, p, len, 0x8B, &taglen); |
538 | 0 | if (tag && taglen) { |
539 | 0 | process_arr(card, tag, taglen); |
540 | 0 | } else if ((tag = sc_asn1_find_tag(ctx, p, len, 0xA1, &taglen)) |
541 | 0 | && taglen) { |
542 | | /* Not found, but there is a Security Attribute |
543 | | Template for interface mode. */ |
544 | 0 | tag = sc_asn1_find_tag(ctx, tag, taglen, 0x8B, &taglen); |
545 | 0 | if (tag && taglen) |
546 | 0 | process_arr(card, tag, taglen); |
547 | 0 | } |
548 | |
|
549 | 0 | file->magic = SC_FILE_MAGIC; |
550 | 0 | } |
551 | | |
552 | | /* Send a select command and parse the response. */ |
553 | | static int |
554 | | do_select(sc_card_t * card, u8 kind, |
555 | | const u8 * buf, size_t buflen, sc_file_t ** file) |
556 | 0 | { |
557 | 0 | sc_apdu_t apdu; |
558 | 0 | u8 resbuf[SC_MAX_APDU_BUFFER_SIZE]; |
559 | 0 | int r; |
560 | |
|
561 | 0 | u8 p2 = 0x00; |
562 | 0 | if (kind == MCRD_SEL_EF) p2 = 0x04; |
563 | 0 | if (kind == MCRD_SEL_DF) p2 = 0x0C; |
564 | |
|
565 | 0 | sc_format_apdu_ex(&apdu, 0x00, 0xA4, kind, p2, buf, buflen, resbuf, 256); |
566 | 0 | r = sc_transmit_apdu(card, &apdu); |
567 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
568 | 0 | if (!file) { |
569 | 0 | if (apdu.sw1 == 0x61) |
570 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0); |
571 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
572 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
573 | 0 | } |
574 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
575 | 0 | if (r) |
576 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); |
577 | | |
578 | 0 | if (p2 == 0x0C) { |
579 | 0 | if (file) { |
580 | 0 | *file = sc_file_new(); |
581 | 0 | if (!*file) |
582 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); |
583 | 0 | (*file)->type = SC_FILE_TYPE_DF; |
584 | 0 | return SC_SUCCESS; |
585 | 0 | } |
586 | 0 | } |
587 | | |
588 | 0 | if (p2 == 0x04 && apdu.resplen > 2 && apdu.resp[0] == 0x62) { |
589 | 0 | *file = sc_file_new(); |
590 | 0 | if (!*file) |
591 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); |
592 | 0 | if (apdu.resp[1] > apdu.resplen - 2) |
593 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); |
594 | 0 | process_fcp(card, *file, apdu.resp + 2, apdu.resp[1]); |
595 | 0 | return SC_SUCCESS; |
596 | 0 | } |
597 | | |
598 | 0 | if (p2 != 0x0C && apdu.resplen > 2 && apdu.resp[0] == 0x6F) { |
599 | 0 | *file = sc_file_new(); |
600 | 0 | if (!*file) |
601 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); |
602 | 0 | if (apdu.resp[1] > apdu.resplen - 2) |
603 | 0 | LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_DATA); |
604 | 0 | process_fcp(card, *file, apdu.resp + 2, apdu.resp[1]); |
605 | 0 | return SC_SUCCESS; |
606 | 0 | } |
607 | 0 | return SC_SUCCESS; |
608 | 0 | } |
609 | | |
610 | | /* Wrapper around do_select to be used when multiple selects are |
611 | | required. */ |
612 | | static int |
613 | | select_part(sc_card_t * card, u8 kind, unsigned short int fid, |
614 | | sc_file_t ** file) |
615 | 0 | { |
616 | 0 | u8 fbuf[2]; |
617 | 0 | unsigned int len; |
618 | 0 | int r; |
619 | |
|
620 | 0 | sc_log(card->ctx, |
621 | 0 | "select_part (0x%04X, kind=%u)\n", fid, kind); |
622 | |
|
623 | 0 | if (fid == MFID) { |
624 | 0 | kind = MCRD_SEL_MF; /* force this kind. */ |
625 | 0 | len = 0; |
626 | 0 | } else { |
627 | 0 | fbuf[0] = fid >> 8; |
628 | 0 | fbuf[1] = fid & 0xff; |
629 | 0 | len = 2; |
630 | 0 | } |
631 | 0 | r = do_select(card, kind, fbuf, len, file); |
632 | |
|
633 | 0 | return r; |
634 | 0 | } |
635 | | |
636 | | /* Select a file by iterating over the FID in the PATHPTR array while |
637 | | updating the curpath kept in the private data cache. With DF_ONLY |
638 | | passed as true only DF are selected, otherwise the function tries |
639 | | to figure out whether the last path item is a DF or EF. */ |
640 | | static int |
641 | | select_down(sc_card_t * card, |
642 | | unsigned short *pathptr, size_t pathlen, |
643 | | int df_only, sc_file_t ** file) |
644 | 0 | { |
645 | 0 | struct mcrd_priv_data *priv = DRVDATA(card); |
646 | 0 | int r; |
647 | 0 | int found_ef = 0; |
648 | |
|
649 | 0 | if (!pathlen) |
650 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
651 | | |
652 | 0 | for (; pathlen; pathlen--, pathptr++) { |
653 | 0 | if (priv->curpathlen == MAX_CURPATH) |
654 | 0 | LOG_TEST_RET(card->ctx, SC_ERROR_INTERNAL, |
655 | 0 | "path too long for cache"); |
656 | 0 | r = -1; /* force DF select. */ |
657 | 0 | if (pathlen == 1 && !df_only) { |
658 | | /* first try to select an EF and retry an DF |
659 | | on error. */ |
660 | 0 | r = select_part(card, MCRD_SEL_EF, *pathptr, file); |
661 | 0 | if (!r) |
662 | 0 | found_ef = 1; |
663 | 0 | } |
664 | 0 | if (r) |
665 | 0 | r = select_part(card, MCRD_SEL_DF, *pathptr, |
666 | 0 | pathlen == 1 ? file : NULL); |
667 | 0 | LOG_TEST_RET(card->ctx, r, "unable to select DF"); |
668 | 0 | priv->curpath[priv->curpathlen] = *pathptr; |
669 | 0 | priv->curpathlen++; |
670 | 0 | } |
671 | 0 | priv->is_ef = found_ef; |
672 | 0 | if (!found_ef) |
673 | 0 | load_special_files(card); |
674 | |
|
675 | 0 | return 0; |
676 | 0 | } |
677 | | |
678 | | /* Handle the selection case when a PATH is requested. Our card does |
679 | | not support this addressing so we have to emulate it. To keep the |
680 | | security status we should not unnecessary change the directory; |
681 | | this is accomplished be keeping track of the currently selected |
682 | | file. Note that PATH is an array of PATHLEN file ids and not the |
683 | | usual sc_path structure. */ |
684 | | |
685 | | static int |
686 | | select_file_by_path(sc_card_t * card, unsigned short *pathptr, |
687 | | size_t pathlen, sc_file_t ** file) |
688 | 0 | { |
689 | 0 | struct mcrd_priv_data *priv = DRVDATA(card); |
690 | 0 | int r; |
691 | 0 | size_t i; |
692 | |
|
693 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
694 | |
|
695 | 0 | if (!(!priv->curpathlen || priv->curpath[0] == MFID)) |
696 | 0 | return SC_ERROR_INTERNAL; |
697 | | |
698 | 0 | if (pathlen && *pathptr == 0x3FFF) { |
699 | 0 | pathlen--; |
700 | 0 | pathptr++; |
701 | 0 | } |
702 | |
|
703 | 0 | if (!pathlen || pathlen >= MAX_CURPATH) |
704 | 0 | r = SC_ERROR_INVALID_ARGUMENTS; |
705 | 0 | else if (pathlen == 1 && pathptr[0] == MFID) { |
706 | | /* MF requested: clear the cache and select it. */ |
707 | 0 | priv->curpathlen = 0; |
708 | 0 | r = select_part(card, MCRD_SEL_MF, pathptr[0], file); |
709 | 0 | LOG_TEST_RET(card->ctx, r, "unable to select MF"); |
710 | 0 | priv->curpath[0] = pathptr[0]; |
711 | 0 | priv->curpathlen = 1; |
712 | 0 | priv->is_ef = 0; |
713 | 0 | } else if (pathlen > 1 && pathptr[0] == MFID) { |
714 | | /* Absolute addressing, check cache to avoid |
715 | | unnecessary selects. */ |
716 | 0 | for (i = 0; (i < pathlen && i < priv->curpathlen |
717 | 0 | && pathptr[i] == priv->curpath[i]); i++) ; |
718 | 0 | if (!priv->curpathlen) { |
719 | | /* Need to do all selects starting at the root. */ |
720 | 0 | priv->curpathlen = 0; |
721 | 0 | priv->is_ef = 0; |
722 | 0 | r = select_down(card, pathptr, pathlen, 0, file); |
723 | 0 | } else if (i == pathlen && i < priv->curpathlen) { |
724 | | /* Go upwards; we do it the easy way and start |
725 | | at the root. However we know that the target is a DF. */ |
726 | 0 | priv->curpathlen = 0; |
727 | 0 | priv->is_ef = 0; |
728 | 0 | r = select_down(card, pathptr, pathlen, 1, file); |
729 | 0 | } else if (i == pathlen && i == priv->curpathlen) { |
730 | | /* Already selected. */ |
731 | 0 | if (!file) |
732 | 0 | r = 0; /* The caller did not request the fci. */ |
733 | 0 | else { |
734 | | /* This EF or DF was already selected, but |
735 | | we need to get the FCI, so we have |
736 | | to select again. */ |
737 | 0 | if (!(priv->curpathlen > 1)) |
738 | 0 | return SC_ERROR_INTERNAL; |
739 | 0 | priv->curpathlen--; |
740 | 0 | priv->is_ef = 0; |
741 | 0 | r = select_down(card, pathptr + pathlen - 1, 1, |
742 | 0 | 0, file); |
743 | 0 | } |
744 | 0 | } else { |
745 | | /* We have to append something. For now we |
746 | | simply start at the root. (fixme) */ |
747 | 0 | priv->curpathlen = 0; |
748 | 0 | priv->is_ef = 0; |
749 | 0 | r = select_down(card, pathptr, pathlen, 0, file); |
750 | 0 | } |
751 | 0 | } else { |
752 | | /* Relative addressing. */ |
753 | 0 | if (!priv->curpathlen) { |
754 | | /* Relative addressing without a current path. So we |
755 | | select the MF first. */ |
756 | 0 | r = select_part(card, MCRD_SEL_MF, pathptr[0], file); |
757 | 0 | LOG_TEST_RET(card->ctx, r, "unable to select MF"); |
758 | 0 | priv->curpath[0] = pathptr[0]; |
759 | 0 | priv->curpathlen = 1; |
760 | 0 | priv->is_ef = 0; |
761 | 0 | } |
762 | 0 | if (priv->is_ef) { |
763 | 0 | if(!(priv->curpathlen > 1)) |
764 | 0 | return SC_ERROR_INTERNAL; |
765 | 0 | priv->curpathlen--; |
766 | 0 | priv->is_ef = 0; |
767 | 0 | } |
768 | | /* Free the previously allocated file so we do not leak memory here */ |
769 | 0 | if (file) { |
770 | 0 | sc_file_free(*file); |
771 | 0 | *file = NULL; |
772 | 0 | } |
773 | 0 | r = select_down(card, pathptr, pathlen, 0, file); |
774 | 0 | } |
775 | 0 | return r; |
776 | 0 | } |
777 | | |
778 | | static int |
779 | | select_file_by_fid(sc_card_t * card, unsigned short *pathptr, |
780 | | size_t pathlen, sc_file_t ** file) |
781 | 0 | { |
782 | 0 | struct mcrd_priv_data *priv = DRVDATA(card); |
783 | 0 | int r; |
784 | |
|
785 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
786 | |
|
787 | 0 | if (!(!priv->curpathlen || priv->curpath[0] == MFID)) |
788 | 0 | return SC_ERROR_INTERNAL; |
789 | | |
790 | 0 | if (pathlen > 1) |
791 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
792 | | |
793 | 0 | if (pathlen && *pathptr == 0x3FFF) |
794 | 0 | return 0; |
795 | | |
796 | 0 | if (!pathlen) { |
797 | | /* re-select the current one if needed. */ |
798 | 0 | if (!file) |
799 | 0 | r = 0; /* The caller did not request the fci. */ |
800 | 0 | else if (!priv->curpathlen) { |
801 | | /* There is no current file. */ |
802 | 0 | r = SC_ERROR_INTERNAL; |
803 | 0 | } else { |
804 | 0 | if (!(priv->curpathlen > 1)) |
805 | 0 | return SC_ERROR_INTERNAL; |
806 | 0 | priv->curpathlen--; |
807 | 0 | priv->is_ef = 0; |
808 | 0 | r = select_down(card, pathptr, 1, 0, file); |
809 | 0 | } |
810 | 0 | } else if (pathptr[0] == MFID) { |
811 | | /* MF requested: clear the cache and select it. */ |
812 | 0 | priv->curpathlen = 0; |
813 | 0 | r = select_part(card, MCRD_SEL_MF, MFID, file); |
814 | 0 | LOG_TEST_RET(card->ctx, r, "unable to select MF"); |
815 | 0 | priv->curpath[0] = MFID; |
816 | 0 | priv->curpathlen = 1; |
817 | 0 | priv->is_ef = 0; |
818 | 0 | } else { |
819 | | /* Relative addressing. */ |
820 | 0 | if (!priv->curpathlen) { |
821 | | /* Relative addressing without a current path. So we |
822 | | select the MF first. */ |
823 | 0 | r = select_part(card, MCRD_SEL_MF, pathptr[0], file); |
824 | 0 | LOG_TEST_RET(card->ctx, r, "unable to select MF"); |
825 | 0 | priv->curpath[0] = pathptr[0]; |
826 | 0 | priv->curpathlen = 1; |
827 | 0 | priv->is_ef = 0; |
828 | 0 | } |
829 | 0 | if (priv->is_ef) { |
830 | 0 | if (!(priv->curpathlen > 1)) |
831 | 0 | return SC_ERROR_INTERNAL; |
832 | 0 | priv->curpathlen--; |
833 | 0 | priv->is_ef = 0; |
834 | 0 | } |
835 | | /* Free the previously allocated file so we do not leak memory here */ |
836 | 0 | if (file) { |
837 | 0 | sc_file_free(*file); |
838 | 0 | *file = NULL; |
839 | 0 | } |
840 | 0 | r = select_down(card, pathptr, 1, 0, file); |
841 | 0 | } |
842 | | |
843 | 0 | return r; |
844 | 0 | } |
845 | | |
846 | | /* This drivers select command handler. */ |
847 | | static int |
848 | | mcrd_select_file(sc_card_t * card, const sc_path_t * path, sc_file_t ** file) |
849 | 0 | { |
850 | 0 | struct mcrd_priv_data *priv = DRVDATA(card); |
851 | 0 | int r = 0; |
852 | |
|
853 | 0 | SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); |
854 | |
|
855 | 0 | if (path->type == SC_PATH_TYPE_DF_NAME) { |
856 | 0 | if (path->len > 16) |
857 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
858 | 0 | r = do_select(card, MCRD_SEL_AID, path->value, path->len, file); |
859 | 0 | priv->curpathlen = 0; |
860 | 0 | } else { |
861 | 0 | unsigned short int pathtmp[SC_MAX_PATH_SIZE / 2]; |
862 | 0 | unsigned short int *pathptr; |
863 | 0 | int samepath = 1; |
864 | 0 | size_t pathlen, n; |
865 | |
|
866 | 0 | if ((path->len & 1) || path->len > sizeof(pathtmp)) |
867 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
868 | | |
869 | 0 | memset(pathtmp, 0, sizeof pathtmp); |
870 | 0 | pathptr = pathtmp; |
871 | 0 | for (n = 0; n < path->len; n += 2) |
872 | 0 | pathptr[n >> 1] = |
873 | 0 | (unsigned short)((path->value[n] << 8) | path->value[n + 1]); |
874 | 0 | pathlen = path->len >> 1; |
875 | |
|
876 | 0 | if (pathlen == priv->curpathlen && priv->is_ef != 2) { |
877 | 0 | for (n = 0; n < pathlen; n++) { |
878 | 0 | if (priv->curpath[n] != pathptr[n]) { |
879 | 0 | samepath = 0; |
880 | 0 | break; |
881 | 0 | } |
882 | 0 | } |
883 | 0 | } else if (priv->curpathlen < pathlen && priv->is_ef != 2) { |
884 | 0 | for (n = 0; n < priv->curpathlen; n++) { |
885 | 0 | if (priv->curpath[n] != pathptr[n]) { |
886 | 0 | samepath = 0; |
887 | 0 | break; |
888 | 0 | } |
889 | 0 | } |
890 | 0 | pathptr = pathptr + n; |
891 | 0 | pathlen = pathlen - n; |
892 | 0 | } |
893 | |
|
894 | 0 | if (samepath != 1 || priv->is_ef == 0 || priv->is_ef == 1) { |
895 | 0 | if (path->type == SC_PATH_TYPE_PATH) |
896 | 0 | r = select_file_by_path(card, pathptr, pathlen, file); |
897 | 0 | else { /* SC_PATH_TYPE_FILEID */ |
898 | 0 | r = select_file_by_fid(card, pathptr, pathlen, file); |
899 | 0 | } |
900 | 0 | } |
901 | 0 | } |
902 | | |
903 | 0 | return r; |
904 | 0 | } |
905 | | |
906 | | /* It seems that MICARDO does not fully comply with ISO, so I use |
907 | | values gathered from peeking actual signing operations using a |
908 | | different system. |
909 | | It has been generalized [?] and modified by information coming from |
910 | | openpgp card implementation and some other sources. -mp |
911 | | */ |
912 | | static int mcrd_set_security_env(sc_card_t * card, |
913 | | const sc_security_env_t * env, int se_num) |
914 | 0 | { |
915 | 0 | struct mcrd_priv_data *priv; |
916 | 0 | sc_apdu_t apdu; |
917 | 0 | u8 sbuf[5] = {0x83, 0x03, 0x80, 0, 0}; |
918 | 0 | int r = 0, locked = 0; |
919 | |
|
920 | 0 | if (card == NULL || env == NULL) |
921 | 0 | return SC_ERROR_INTERNAL; |
922 | 0 | LOG_FUNC_CALLED(card->ctx); |
923 | 0 | priv = DRVDATA(card); |
924 | | |
925 | | /* some sanity checks */ |
926 | 0 | if (env->flags & SC_SEC_ENV_ALG_PRESENT) { |
927 | 0 | if (env->algorithm != SC_ALGORITHM_RSA) |
928 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
929 | 0 | } |
930 | 0 | if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT) |
931 | 0 | || env->key_ref_len != 1) |
932 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
933 | | |
934 | 0 | switch (env->operation) { |
935 | 0 | case SC_SEC_OPERATION_DECIPHER: |
936 | 0 | sc_log(card->ctx, "Using keyref %d to decipher\n", env->key_ref[0]); |
937 | 0 | mcrd_delete_ref_to_authkey(card); |
938 | 0 | mcrd_delete_ref_to_signkey(card); |
939 | 0 | break; |
940 | 0 | case SC_SEC_OPERATION_SIGN: |
941 | 0 | sc_log(card->ctx, "Using keyref %d to sign\n", env->key_ref[0]); |
942 | 0 | break; |
943 | 0 | default: |
944 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
945 | 0 | } |
946 | 0 | priv->sec_env = *env; |
947 | |
|
948 | 0 | sbuf[3] = env->key_ref[0]; |
949 | 0 | switch (env->operation) { |
950 | 0 | case SC_SEC_OPERATION_DECIPHER: |
951 | 0 | sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB8, sbuf, 5, NULL, 0); |
952 | 0 | break; |
953 | 0 | case SC_SEC_OPERATION_SIGN: |
954 | 0 | sc_format_apdu_ex(&apdu, 0x00, 0x22, 0x41, 0xB6, sbuf, 5, NULL, 0); |
955 | 0 | break; |
956 | 0 | default: |
957 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
958 | 0 | } |
959 | | |
960 | 0 | if (se_num > 0) { |
961 | 0 | r = sc_lock(card); |
962 | 0 | LOG_TEST_RET(card->ctx, r, "sc_lock() failed"); |
963 | 0 | locked = 1; |
964 | 0 | } |
965 | 0 | if (apdu.datalen != 0) { |
966 | 0 | r = sc_transmit_apdu(card, &apdu); |
967 | 0 | if (r) { |
968 | 0 | sc_log(card->ctx, |
969 | 0 | "%s: APDU transmit failed", sc_strerror(r)); |
970 | 0 | goto err; |
971 | 0 | } |
972 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
973 | 0 | if (r) { |
974 | 0 | sc_log(card->ctx, |
975 | 0 | "%s: Card returned error", sc_strerror(r)); |
976 | 0 | goto err; |
977 | 0 | } |
978 | 0 | } |
979 | 0 | if (se_num <= 0) |
980 | 0 | return 0; |
981 | 0 | sc_unlock(card); |
982 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
983 | 0 | return sc_check_sw(card, apdu.sw1, apdu.sw2); |
984 | 0 | err: |
985 | 0 | if (locked) |
986 | 0 | sc_unlock(card); |
987 | 0 | return r; |
988 | 0 | } |
989 | | |
990 | | /* heavily modified by -mp */ |
991 | | static int mcrd_compute_signature(sc_card_t * card, |
992 | | const u8 * data, size_t datalen, |
993 | | u8 * out, size_t outlen) |
994 | 0 | { |
995 | 0 | struct mcrd_priv_data *priv = DRVDATA(card); |
996 | 0 | sc_security_env_t *env = NULL; |
997 | 0 | int r; |
998 | 0 | sc_apdu_t apdu; |
999 | |
|
1000 | 0 | if (data == NULL || out == NULL) |
1001 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
1002 | 0 | env = &priv->sec_env; |
1003 | |
|
1004 | 0 | LOG_FUNC_CALLED(card->ctx); |
1005 | 0 | if (env->operation != SC_SEC_OPERATION_SIGN) |
1006 | 0 | return SC_ERROR_INVALID_ARGUMENTS; |
1007 | 0 | if (datalen > 255) |
1008 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS); |
1009 | | |
1010 | 0 | sc_log(card->ctx, |
1011 | 0 | "Will compute signature (%d) for %"SC_FORMAT_LEN_SIZE_T"u (0x%02"SC_FORMAT_LEN_SIZE_T"x) bytes using key %d algorithm %lu flags %lu\n", |
1012 | 0 | env->operation, datalen, datalen, env->key_ref[0], |
1013 | 0 | env->algorithm, env->algorithm_flags); |
1014 | |
|
1015 | 0 | if (env->key_ref[0] == 1) /* authentication key */ |
1016 | 0 | sc_format_apdu_ex(&apdu, 0x00, 0x88, 0, 0, data, datalen, out, MIN(0x80U, outlen)); |
1017 | 0 | else |
1018 | 0 | sc_format_apdu_ex(&apdu, 0x00, 0x2A, 0x9E, 0x9A, data, datalen, out, MIN(0x80U, outlen)); |
1019 | 0 | r = sc_transmit_apdu(card, &apdu); |
1020 | 0 | LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); |
1021 | 0 | r = sc_check_sw(card, apdu.sw1, apdu.sw2); |
1022 | 0 | LOG_TEST_RET(card->ctx, r, "Card returned error"); |
1023 | | |
1024 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, (int)apdu.resplen); |
1025 | 0 | } |
1026 | | |
1027 | | /* added by -mp, to give pin information in the card driver (pkcs15emu->driver needed) */ |
1028 | | static int mcrd_pin_cmd(sc_card_t * card, struct sc_pin_cmd_data *data, |
1029 | | int *tries_left) |
1030 | 0 | { |
1031 | 0 | LOG_FUNC_CALLED(card->ctx); |
1032 | 0 | data->pin1.offset = 5; |
1033 | 0 | data->pin2.offset = 5; |
1034 | |
|
1035 | 0 | if (card->type == SC_CARD_TYPE_MCRD_GENERIC) { |
1036 | 0 | sc_log(card->ctx, "modify pin reference for D-Trust\n"); |
1037 | 0 | if (data->pin_reference == 0x02) |
1038 | 0 | data->pin_reference = data->pin_reference | 0x80; |
1039 | 0 | } |
1040 | 0 | SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, iso_ops->pin_cmd(card, data, tries_left)); |
1041 | 0 | } |
1042 | | |
1043 | | static int mcrd_logout(sc_card_t * card) |
1044 | 0 | { |
1045 | 0 | return SC_ERROR_NOT_SUPPORTED; |
1046 | 0 | } |
1047 | | |
1048 | | /* Driver binding */ |
1049 | | static struct sc_card_driver *sc_get_driver(void) |
1050 | 0 | { |
1051 | 0 | struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); |
1052 | 0 | if (iso_ops == NULL) |
1053 | 0 | iso_ops = iso_drv->ops; |
1054 | |
|
1055 | 0 | mcrd_ops = *iso_drv->ops; |
1056 | 0 | mcrd_ops.match_card = mcrd_match_card; |
1057 | 0 | mcrd_ops.init = mcrd_init; |
1058 | 0 | mcrd_ops.finish = mcrd_finish; |
1059 | 0 | mcrd_ops.select_file = mcrd_select_file; |
1060 | 0 | mcrd_ops.set_security_env = mcrd_set_security_env; |
1061 | 0 | mcrd_ops.compute_signature = mcrd_compute_signature; |
1062 | 0 | mcrd_ops.pin_cmd = mcrd_pin_cmd; |
1063 | 0 | mcrd_ops.logout = mcrd_logout; |
1064 | |
|
1065 | 0 | return &mcrd_drv; |
1066 | 0 | } |
1067 | | |
1068 | | struct sc_card_driver *sc_get_mcrd_driver(void) |
1069 | 0 | { |
1070 | 0 | return sc_get_driver(); |
1071 | 0 | } |